AWS Serverless Vol4|Lambda Durable Functions×Step Functions使い分け

目次

1. この記事について

fig01: Durable Functions ワークフロー全体構成図 (event handler内のsteps/waits・チェックポイントストア・自動復旧)
fig01: Durable Functions ワークフロー全体構成図 (event handler内のsteps/waits・チェックポイントストア・自動復旧)
この記事で扱う Lambda Durable Functions の全体像

  • steps/waits プリミティブによるチェックポイント付き長時間・多段ワークフローを、Step Functions を介さず Lambda 単体で実装する方法
  • waits による課金なしの実行中断を human-in-the-loop の承認待ちや外部システムのポーリングに応用する設計
  • Step Functions との選択基準 — 実行時間・コスト・状態管理・可視化・オーケストレーション粒度の5軸でどちらを選ぶか
Serverless 本番運用シリーズの到達点と本 Vol の位置づけ

  • Vol1 (Lambda × API Gateway × Step Functions 基礎) → Vol2 (EventBridge/SQS/SNS/Kinesis によるイベント駆動) → Vol3 (SnapStart/Extensions/Powertools による Lambda 高度化) に続く第4弾
  • 本 Vol4 は Vol1 で確立した Step Functions 基礎と補完関係にある「Durable Functions 使い分けガイド」であり、Step Functions の実装詳細は Vol1 へクロスリンク委譲する
  • 既存 Serverless シリーズが Vol1〜Vol3 を通じて一切扱っていなかった新プリミティブを、本番運用の観点から深掘りする
なぜ今 Durable Functions なのか (時事性)

  • 2025-12-18 (re:Invent 2025 直後) に GA、当初は米国東部 (オハイオ) 限定だったが同月中に東京 (ap-northeast-1) を含む14リージョンへ拡大 (2026-04には16リージョンが追加され約31リージョン規模に)
  • Lambda 単体の実行時間・多段オーケストレーションという長年の制約を、steps/waits という新プリミティブで運用解決できるようになった
  • waits による中断中は on-demand 関数であれば計算課金が発生せず、長時間ワークフローのコスト構造を大きく変える

1-1. 本記事のゴール

本記事を読み終える頃には、Lambda Durable Functions の steps/waits プリミティブがどのように動作し、なぜ Step Functions を介さずにチェックポイント付きの長時間・多段ワークフローを実現できるのかを理解できる状態になります。具体的には、steps によるビジネスロジックの分割とビルトインのリトライ・進捗追跡の設計、waits による課金なしの実行中断とその再開条件の設計、そして障害発生時にチェックポイントから自動復旧する挙動を、Terraform によるインフラ定義とあわせて手を動かしながら習得します。

さらに、Durable Functions と Step Functions は機能的に重複する部分があるため、実行時間・コスト・状態管理・可視化・オーケストレーション粒度という5つの軸で両者を比較し、自分のワークロードにどちらが適しているかを根拠を持って判断できる設計力を養うことをゴールとします。読了後は、既存の Step Functions ワークロードを Durable Functions へ移行すべきかどうかも、コストと運用負荷の両面から自力で判断できるようになります。

本記事で習得するスキルは、大きく次の4つに整理できます。

  • steps 設計: ビジネスロジックを意味のある単位に分割し、ビルトインのリトライポリシーと進捗追跡を組み合わせる設計パターン
  • waits 設計: 承認待ちや外部イベント待ちを、課金なしの実行中断として実装するタイミング設計と、再開トリガーの選定基準
  • チェックポイント復旧: 実行途中で障害が発生した際に、直前のチェックポイントから自動的に処理を再開する仕組みの理解と動作検証
  • Step Functions との使い分け: 実行時間・コスト・状態管理・可視化・オーケストレーション粒度の5軸で、どちらを採用すべきかを判断する基準

これら4点を、Terraform でのインフラ定義・実装コード・障害注入による復旧検証という3ステップで一通り体験し、本番投入の判断材料を持ち帰れる内容を目指します。

Durable Functions・Step Functions・従来の単発 Lambda の位置づけを概観すると、以下の通りです (詳細な選択基準は §6 で解説します)。

観点単発 LambdaDurable FunctionsStep Functions
実行時間最大15分最大1年 (中断込み)ステートマシン全体で最大1年
状態管理呼び出し元アプリで管理チェックポイントで自動管理ステートマシン定義で管理
待機中の課金実行し続ける限り課金on-demand なら課金なしStandard は状態遷移課金
可視化CloudWatch Logs 中心実行履歴 API (発展途上)Step Functions コンソールで詳細可視化
導入コスト低い (既存の延長)低い (既存コードへの追加)中〜高 (ASL 定義が必要)

なお、本記事が扱う範囲は以下の通りに限定します。

  • Step Functions のステートマシン定義 (ASL) や実装詳細は Vol1 で解説済みのため、本記事では再掲せず本文中でクロスリンク委譲します
  • Durable Functions の対応言語のうち、本記事では Node.js と Python を中心に実装例を提示します (Java での実装パターンは差分が大きいため対象外とします)
  • Durable Functions と Bedrock AgentCore など AI エージェント基盤との統合は範囲外とし、あくまで Lambda 単体でのワークフロー実装に焦点を当てます

なお本記事は、Vol1 の Step Functions 基礎を前提にはしていますが、Vol1 未読でも Durable Functions 単体の実装・使い分け判断は理解できるよう構成しています。Step Functions の詳細な設計思想まで深掘りしたい場合は、あわせて Vol1 を参照してください。

1-2. 読者像

本記事の主な読者は、すでに Lambda と Step Functions を本番環境で運用しており、Lambda 単体の実行時間制約 (最大15分) や、複数ステップにまたがる処理をオーケストレーションする際のコスト・複雑性に課題を感じているサーバーレス開発者・クラウドアーキテクトです。Step Functions のステートマシン定義や実行履歴の可視化には価値を感じつつも、シンプルな多段処理のためだけに別サービスを組み込む設計コストに疑問を持ち始めている層を想定しています。

また、承認待ちや外部システムのポーリングを伴うワークフローを Lambda + SQS のポーリングや EventBridge Scheduler の再実行で無理やり実装しており、待機中の課金や実装の複雑さに課題を感じているチームにも有用です。Terraform で Lambda 関数を IaC 管理した経験があり、Vol1〜Vol3 で扱った Serverless 本番運用の基礎知識がある読者を前提とします。

具体的には、以下のような課題意識を持つ読者に特に役立ちます。

  • Lambda の15分実行時間制約に達し、複数の Lambda 関数を Step Functions で連結する設計に複雑さを感じている
  • Step Functions のステートマシン定義 (ASL) の学習コストや、ステート遷移ごとの課金体系に負担を感じている
  • 承認待ちワークフローを SQS の長時間ポーリングや EventBridge Scheduler の再実行で無理やり実装しており、待機中の課金や実装の複雑さに課題がある
  • 新しい AWS サービスをすぐに採用するのではなく、既存の Step Functions 実装からの移行コストとメリットを定量的に比較してから判断したい

本記事の想定読者像を整理すると、以下の通りです。

項目想定読者像
実務経験Lambda 本番運用1年以上、Step Functions での実装経験あり
IaC 経験Terraform でのリソース管理経験 (module 分割・state 管理を含む)
現在の課題15分実行時間制約、多段オーケストレーションのコスト・複雑性
読了後のゴールDurable Functions と Step Functions の使い分け基準を持てる状態

一方で、Lambda や Terraform を初めて触る読者、あるいは単発の短時間タスクしか扱わない読者には、本記事の内容はオーバースペックです。まずは Vol1〜Vol3 で Lambda 本番運用の基礎を固めてから本記事に戻ることを推奨します。

想定読者の多くは、現時点で以下のようなアーキテクチャを本番運用している段階にあると考えられます。

  • 単発 Lambda を Step Functions の Task ステートから呼び出す、比較的シンプルなオーケストレーション構成
  • 長時間待機が必要な処理を、EventBridge Scheduler による定期実行や SQS の可視性タイムアウト延長で代替している構成
  • Lambda の実行時間制約 (最大15分) に達したため、処理を複数の Lambda 関数に分割し、Step Functions で連結している構成

こうした構成をすでに運用している読者であれば、本記事の実装例をそのまま自身の環境に適用しやすいはずです。

1-3. なぜ今これを書くか

Lambda Durable Functions は 2025-12-18、re:Invent 2025 直後に GA しました。当初は米国東部 (オハイオ) リージョン限定でしたが、同月中に東京 (ap-northeast-1) を含む14リージョンへ拡大し、日本のプロダクション環境でも即座に採用検討できる状態です。これにより、Lambda 単体でチェックポイント付きの長時間・多段ワークフローを実装するという、これまで Step Functions か自前実装でしか選択肢がなかった領域に、新たな第三の選択肢が加わりました。

Durable Functions のリージョン展開は以下の通りです。

時期展開内容
2025-12-18GA。米国東部 (オハイオ) リージョン限定で提供開始
2025-12 (同月中)東京 (ap-northeast-1) を含む14リージョンへ拡大
2026-04さらに16リージョンが追加され、約31リージョン規模に拡大

Serverless 本番運用シリーズは Vol1 で Step Functions の基礎、Vol2 でイベント駆動アーキテクチャ、Vol3 で Lambda 自体の高度化 (SnapStart/Extensions/Powertools) を扱ってきましたが、Durable Functions は Vol1〜Vol3 のいずれでも一切カバーしていない完全な空白領域でした。既存の Step Functions 実装との使い分けに迷う読者が今後増えることを見越し、GA から日が浅い今のタイミングで本番運用の観点から先取りして解説する意義があると判断しました。

AWS の公式発表では、Durable Functions は「複数ステップのアプリケーションと AI 支援ワークフロー (AI-assisted workflows) の構築を簡素化する」機能と位置づけられています。注文処理やユーザーオンボーディングのような従来型の多段処理に加え、外部 LLM 呼び出しや人間による承認を挟む AI エージェント的なワークフローが増えている背景があり、これらは単一の短時間タスクを前提とした従来の Lambda では実装しづらい領域でした。Durable Functions は実行を最大1年間、計算課金なしで一時停止できるため、こうした長時間・非同期的なワークフローとの親和性が高いことも、今このタイミングで整理する価値があると判断した理由の一つです。

また、Durable Functions は既存の Lambda 関数プログラミングモデルの延長として導入できるため、Step Functions のように別リソースを新設する必要がなく、既存の Terraform 構成への組み込みコストが小さいという実務上の利点もあります。この「導入コストの低さ」こそが、GA 直後の今このタイミングで本番運用ノウハウを整理しておく価値だと考えています。

具体的には、既存の Lambda ハンドラー関数に対して steps/waits という2つの新しい命令を組み込むだけで導入でき、Step Functions のように新規ステートマシンリソースを作成したり、既存のビジネスロジックをステートマシン定義言語 (ASL) で再構築したりする必要がありません。この「既存コードへの追加」という導入形態の軽さが、Step Functions への移行を躊躇していたチームにとっての現実的な選択肢になり得ると考え、本記事のテーマとして取り上げました。

AWS が公式に挙げているユースケースと、本記事での対応箇所を整理すると以下の通りです。

  • 注文処理: 複数の外部 API 呼び出しを跨ぐ、リトライ・進捗追跡が必要な多段処理 (§4 で steps の実装例として扱います)
  • ユーザーオンボーディング: 複数ステップの登録フローで、途中の承認待ちが発生するケース (§5 で waits の実装例として扱います)
  • AI 支援ワークフロー: 外部 LLM 呼び出しや人間によるレビューを挟む、非同期的で長時間に及ぶ処理フロー (§5 の human-in-the-loop パターンとして扱います)

これらのユースケースに共通するのは、いずれも「単一の短時間タスク」という従来の Lambda の前提から外れている点です。本記事では、この前提の変化を踏まえた本番運用パターンを、実装コードとあわせて解説します。

前作: Serverless本番運用 Vol3 (Lambda高度化) を読む


2. 前提・環境・準備

fig02: steps 実行フロー図 (business logic × built-in retry × progress tracking)
fig02: steps 実行フロー図 (business logic × built-in retry × progress tracking)

2-1. 前提環境

Durable Functionsを本番運用へ導入する前に、以下の前提条件を整理します。

AWSアカウント・リージョン

Lambda Durable Functionsは2025-12(re:Invent 2025)にGAし、東京リージョン(ap-northeast-1)を含む14リージョンで提供が開始されました。2026-04にはさらに16リージョンが追加され、東京を含む主要リージョンで広く利用できる状態に拡大しています(出典: AWS What’s New — Lambda durable functions 14 additional regions / 16 additional regions)。本記事の検証環境はap-northeast-1を前提とします。事前にDurable Functionsの利用可否をリージョン単位で確認してください。

対応ランタイム

Durable Functionsを有効化できるマネージドランタイムは、以下の3言語系統に限定されます。

  • Node.js: nodejs22.x / nodejs24.x
  • Python: python3.13 / python3.14
  • Java: java17 / java21 / java25

上記以外のバージョン(nodejs20.xpython3.12など)ではDurable Functionsを有効化できません。既存Lambda関数を流用する場合は、ランタイムのアップグレードが前提になります。マネージドランタイムの対象外バージョンやカスタムランタイムを使いたい場合は、コンテナイメージ(OCIベース)にDurable Execution SDKを組み込む方式で対応できます。

Durable Execution SDK

  • Node.js: npm install @aws/durable-execution-sdk-js
  • Python: pip install aws-durable-execution-sdk-python
  • Java: pom.xmlsoftware.amazon.lambda.durable:aws-durable-execution-sdk-javaを依存追加

Node.js/PythonのマネージドランタイムはテストIDE用にSDKを同梱済みですが、本番ではランタイム側の自動更新によるバージョンずれを避けるため、デプロイパッケージへの明示的な同梱が推奨されています。Javaはコンパイル言語のためSDK同梱が必須です。SDKはメジャーバージョンを固定し、実行中のワークフローに影響する破壊的変更を避けてください。

IAM権限・CLI

実行ロールにはlambda:CheckpointDurableExecutionlambda:GetDurableExecutionStateのチェックポイント操作権限が必要です(詳細は2-4)。CLIから作成する場合はAWS CLIが--durable-configパラメータに対応したバージョンである必要があります。

Terraform / プロバイダーバージョン

  • Terraform CLI: 本記事では1.9.x系を使用します(required_version >= 1.0を満たせば動作しますが、実務では1.9.x系を推奨します)
  • hashicorp/awsプロバイダー: Durable Functionsのterraformサポートには6.25.0以降が必須です。durable_configブロックは5系プロバイダーには実装されていないため、既存プロジェクトで5系(~> 5.0)を固定運用している場合はプロバイダーのメジャーアップグレードが前提になります。

バージョン確認

作業前に、手元のCLI/プロバイダーバージョンが要件を満たしているか確認します。

# Terraform CLIバージョン確認 (1.9.x系であること)
terraform version

# 使用中のhashicorp/awsプロバイダーバージョン確認 (6.25.0以降であること)
terraform providers

# AWS CLIが --durable-config パラメータに対応しているか確認
aws lambda create-function help | grep -A2 durable-config

terraform providersで表示されたaws providerのバージョンが6.25.0未満の場合は、required_providersブロックのバージョン制約を更新したうえでterraform init -upgradeを実行してください。

2-2. 使用技術スタック

Durable Functions本番運用を構成する要素を5レイヤで整理します。

レイヤ要素役割東京提供備考
IaCTerraform 1.9.x / hashicorp/aws >= 6.25.0Lambda関数・IAMロール・DurableConfigの宣言的管理5系プロバイダーはdurable_config未対応
ランタイムnodejs22.x/24.x・python3.13/3.14・java17/21/25(コンテナ可)steps/waitsハンドラの実行基盤マネージド外バージョンはコンテナイメージで対応
プリミティブstepsリトライ+進捗追跡付きでビジネスロジックを実行各step完了時にチェックポイントを生成
プリミティブwaits課金なしで実行を中断・再開human-in-the-loop / 外部ポーリング待ちに使用
状態管理チェックポイントストア進捗の暗号化永続化・障害時の自動復旧実行ごとに分離・保存期間はRetentionPeriodInDaysで指定

いずれのレイヤも東京リージョンで利用可能です。特にIaCレイヤは、プロバイダーバージョンの誤りがそのままdurable_config未対応エラーに直結するため、2-3で明示的に検証します。

2-3. Terraform 初期化 — 3点セット

Durable Functionsを有効化したLambda関数をTerraformで管理する最小構成を示します。ポイントは、①プロバイダーバージョンを>= 6.25.0に固定すること、②実行ロールにAWSLambdaBasicDurableExecutionRolePolicyをアタッチすること、③バージョンまたはエイリアスを発行して修飾ARNを用意すること、の3点です。Durable Functionsは無修飾ARN($LATEST)からの呼び出しに対応していません。

terraform {
  required_version = ">= 1.9.0, < 2.0.0"
  required_providers {
 aws = {
source  = "hashicorp/aws"
version = ">= 6.25.0, < 7.0.0"
 }
  }

  backend "s3" {
 bucket= "your-tfstate-bucket"
 key= "serverless-vol4/durable-functions/terraform.tfstate"
 region= "ap-northeast-1"
 dynamodb_table = "terraform-lock"
 encrypt  = true
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

variable "function_name" {
  description = "Durable Functionsを有効化するLambda関数名"
  type  = string
  default  = "order-workflow-durable"
}

variable "durable_execution_timeout" {
  description = "Durable Execution全体のタイムアウト(秒)。stepsとwaitsをまたいだ実行全体の上限"
  type  = number
  default  = 3600
}

variable "durable_retention_days" {
  description = "実行完了後にチェックポイント履歴を保持する日数"
  type  = number
  default  = 7
}

resource "aws_iam_role" "durable_function_role" {
  name = "${var.function_name}-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
 }]
  })
}

resource "aws_iam_role_policy_attachment" "durable_execution" {
  role = aws_iam_role.durable_function_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy"
}

resource "aws_lambda_function" "durable_workflow" {
  function_name = var.function_name
  filename= "function.zip"
  role = aws_iam_role.durable_function_role.arn
  handler = "index.handler"
  runtime = "nodejs22.x"
  timeout = 30
  memory_size= 512
  publish = true

  durable_config {
 execution_timeout = var.durable_execution_timeout
 retention_period  = var.durable_retention_days
  }
}

resource "aws_lambda_alias" "prod" {
  name = "prod"
  function_name = aws_lambda_function.durable_workflow.function_name
  function_version = aws_lambda_function.durable_workflow.version
}

output "durable_function_alias_arn" {
  description = "Durable Functions呼び出しに使う修飾ARN(バージョン/エイリアス付き)"
  value = aws_lambda_alias.prod.arn
}

terraform initterraform planterraform applyの順に適用します。durable_configブロックはLambda関数の新規作成時のみ設定可能で、既存関数への後付け有効化はできません。既存関数をDurable Functions化する場合は新規リソースとして作り直し、トラフィックをエイリアス切り替えで移行する設計にします。

2-4. IAM 構成

Durable Functionsの実行ロールには、チェックポイント作成と実行状態取得の2操作を許可する必要があります。最小権限で構成する場合は次のカスタムポリシーを使用します。

{
  "Version": "2012-10-17",
  "Statement": [
 {
"Effect": "Allow",
"Action": [
  "lambda:CheckpointDurableExecution",
  "lambda:GetDurableExecutionState"
],
"Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:order-workflow-durable:*"
 }
  ]
}

Resourceはワイルドカードではなく対象関数のARNに絞り込みます。マルチファンクション構成では、対象ARNを配列で列挙してください。上記のTerraform例のように、AWS管理ポリシーAWSLambdaBasicDurableExecutionRolePolicyをアタッチする方法でも同等の権限(チェックポイント操作権限+CloudWatch Logsへの基本実行権限)を付与できます。個別ポリシーとの使い分けは、複数関数を横断する権限境界を明示したいか(カスタムポリシー)、CloudWatch Logs権限も含めて簡潔に管理したいか(管理ポリシー)で判断します。

実行ロールの信頼ポリシーには、Lambdaサービスプリンシパル(lambda.amazonaws.com)を許可する記述が必須です(Terraform例のassume_role_policyを参照)。チェックポイントデータをカスタムシリアライザ/デシリアライザで独自暗号化する場合は、実行ロールに使用するKMSキーへのkms:Encryptkms:Decrypt権限を追加します。デフォルトではDurable Functionsの実行状態はAWS管理キーで保存時暗号化され、転送中もTLSで保護されます。

チェックポイント操作はCloudTrailのデータイベントとしてCheckpointDurableExecution(step完了時)・GetDurableExecutionState(リプレイ時の状態取得)の2種類が記録されます。監査要件がある場合は、Lambdaデータイベントを対象にしたCloudTrail証跡を有効化してください。また、Durable Functionsをクロスアカウントで呼び出す場合、呼び出し元アカウントにはlambda:InvokeFunction権限のみが必要で、チェックポイント操作は常に関数が属するアカウントの実行ロールで行われます。呼び出し元アカウントからチェックポイントデータや実行状態を直接参照できません。

2-5. ゴール状態の定義

本記事を完走した時点で、以下の状態に到達していることを目標とします。

  • stepsで分割した複数ロジックが、リトライと進捗追跡付きで多段ワークフローとして実行できる
  • waitsによって課金なしで実行を中断し、外部イベントやタイマーで再開できる(human-in-the-loop / 外部ポーリング双方)
  • 実行途中の障害発生時に、直前のチェックポイントから自動復旧できる(最初のstepからの再実行にならない)
  • Terraformでhashicorp/aws >= 6.25.0を用い、durable_configを含むLambda関数・IAMロール・エイリアスをコード管理下に置いている
  • 実行ロールの権限をlambda:CheckpointDurableExecution / lambda:GetDurableExecutionStateに絞り込み、最小権限の原則を満たしている
  • CloudTrailでチェックポイント操作の監査ログを追跡できる状態になっている
  • Step Functionsとの選択基準を持ち、どちらを採用すべきかをコスト・状態管理・可視化の観点で説明できる

この前提・環境が整った状態から、次節では実際にsteps/waitsプリミティブの動作原理とチェックポイントの仕組みをコードレベルで見ていきます。


3. Durable Functions の基礎 — steps / waits プリミティブとチェックポイント

3-1. steps と waits — Lambda プログラミングモデルに追加された2つのプリミティブ

Durable Functions は、既存の Lambda 関数をラップする形で提供されます。関数を Durable Execution SDK でラップすると、通常の Lambda コンテキストの代わりに DurableContext を受け取れるようになり、そこから stepswaits という2つの durable operation にアクセスできます。

  • steps: context.step() でビジネスロジックをラップし、実行前後にチェックポイントを作成します。built-in retry と進捗追跡 (progress tracking) が組み込まれており、失敗した場合は自動的に再実行されます。
  • waits: context.wait() で実行を一時停止します。停止中は Lambda の実行環境がリサイクルされ、コンピュートリソースを消費しません。時間指定の待機だけでなく、外部からのコールバックを待つ waitForCallback() も用意されています。

いずれのプリミティブも「関数の実行を止めずに、進捗だけを永続化する」という発想が共通しています。従来の Lambda では 15 分のタイムアウト内にすべての処理を完結させる必要がありましたが、steps/waits を使うと1つの durable execution が複数回の Lambda 呼び出しにまたがって実行され、最長1年間 (31,536,000 秒) 進捗を維持できます。

プリミティブ役割チェックポイント挙動実行中の課金主用途
stepsリトライ + 進捗追跡付きでビジネスロジックを実行実行前後に自動で作成あり (通常のLambda課金)決済処理・API呼び出し・DB更新等の副作用を伴う処理
waits課金なしで実行を中断・再開中断ポイントと再開理由を記録なし (on-demand実行時)human-in-the-loop承認待ち・外部システムのポーリング・時間指定の遅延

3-2. チェックポイントによる進捗保存と自動復旧 — checkpoint / replay の仕組み

Durable Functions の中核は checkpoint-and-replay モデル です。関数の実行中、Lambda は steps や waits などの durable operation を実行するたびに、その結果を「チェックポイントログ」として記録していきます。中断や失敗が発生すると、Lambda はこのログを保存した状態で実行を停止します。

再開時 (waits の待機時間経過後、またはリトライ時) は、関数のコードを 最初から 再実行します。ただし、すでにチェックポイントが記録されている steps/waits は再実行されず、保存済みの結果がそのまま差し込まれます。これを replay (リプレイ) と呼びます。この仕組みにより、関数コード自体は「毎回最初から素直に実行される通常の関数」として書けるにもかかわらず、実際には完了済みの処理が重複実行されることはありません。

replay の性質上、注意すべき点が1つあります。関数コードは決定的 (deterministic) でなければなりません。 context.step() の外側で乱数生成や現在時刻取得のような副作用のある処理を行うと、replay のたびに異なる値が生成され、実行の一貫性が崩れます。時刻や乱数が必要な処理は必ず context.step() の内側に記述し、チェックポイント化する必要があります。

用語Lambda 関数タイムアウトdurable execution の実行タイムアウト (ExecutionTimeout)
上限15分 (単一invocationあたり)最大1年 (31,536,000秒・durable execution全体)
意味1回のLambda呼び出しが完了しなければならない時間開始から完了/失敗/停止までの累積経過時間
waits の扱いwaitsの待機時間はカウントしないwaitsの待機時間もカウントする

durable execution は SUCCEEDED / FAILED / STOPPED / TIMED_OUT のいずれかの終端状態に達すると完了し、その後は RetentionPeriodInDays (デフォルト14日・最大90日) の間だけ実行履歴とチェックポイントデータが保持されます。

3-3. 待機中の課金モデル — waits は「止まっている間は払わない」

context.wait() による中断中は、Lambda の実行環境がリサイクルされ、コンピュート課金が発生しません。これは Durable Functions の最大のコストメリットです。承認待ちが数時間〜数日に及ぶ human-in-the-loop ワークフローや、外部システムの完了を待つポーリング処理では、待機時間そのものに課金される従来方式 (自前のポーリング Lambda を EventBridge Scheduler で定期起動し続ける、あるいは Step Functions Standard のステート遷移課金を積み重ねる) と比べて、コスト構造が大きく変わります。

waits には大きく3種類の使い方があります。

  • 時間指定の待機 (context.wait({ seconds: N })): 一定時間後に自動的に再開します。
  • コールバック待ち (context.waitForCallback()): 外部から明示的な callback ID 付きリクエストが届くまで待機します。human-in-the-loop の承認フローに向いています。
  • 条件待ち (context.waitForCondition()): 外部条件が満たされるまでポーリングしながら待機します。

いずれも待機中は課金対象外である一方、durable execution の ExecutionTimeout には引き続きカウントされる点は忘れてはなりません。

3-4. 従来 Lambda / Step Functions との位置関係

Durable Functions は Step Functions を置き換えるものではなく、「ワークフローをどこで・どう定義するか」が異なる補完的な選択肢 です。

判定軸Durable FunctionsStep Functions
実行場所Lambda 内 (関数コードの一部として実行)独立したオーケストレーションサービス
ワークフロー定義標準プログラミング言語 (Python/Node.js/Java)ASL (グラフベースDSL) またはビジュアルデザイナー
開発体験既存のLambda開発フローに統合・単体テストしやすいWorkflow Studio でのビジュアル設計・非開発者も参加可能
外部サービス連携自前でSDK/APIコールを実装220以上のAWSサービスへのネイティブ統合
可視化・監視Lambdaコンソールの Durable executions タブStep Functions実行履歴・Workflow Studio
向いているケースビジネスロジックと密結合したアプリ内ワークフローサービス横断のオーケストレーション・非開発者も含めた運用

Vol1 で確立した Step Functions の基礎実装 (Standard/Express の選定・Distributed Map・Callback Pattern) は、複数の AWS サービスを疎結合にオーケストレーションする場面で今後も有効です。一方、本 Vol4 が扱う Durable Functions は「Lambda 単体でアプリケーションロジックとワークフロー制御を同じコードベースに書きたい」場面に向いており、両者は競合ではなく使い分けの関係にあります。どちらを選ぶべきかの詳細な判断基準は §6 で扱います。


4. 実装: 多段ワークフロー — steps によるロジック分割・リトライ・進捗追跡 (★山場1)

4-1. DurableContext 実装の基本形

Python では @durable_execution デコレータでハンドラーをラップし、各処理単位を @durable_step デコレータの付いた関数として切り出します。ハンドラーは標準の event に加えて DurableContext を受け取り、context.step() にステップ関数を渡して呼び出します。

from aws_durable_execution_sdk_python import (
 DurableContext,
 durable_execution,
 durable_step,
)
from aws_durable_execution_sdk_python.config import Duration

@durable_step
def reserve_inventory(step_context, order_id, items):
 step_context.logger.info(f"在庫確保: order={order_id}")
 return inventory_service.reserve(items)  # 副作用を伴う呼び出しはstep内に限定する

@durable_execution
def lambda_handler(event, context: DurableContext):
 order_id = event["orderId"]
 items = event["items"]

 inventory = context.step(reserve_inventory(order_id, items))
 return {"orderId": order_id, "inventory": inventory}

Python SDK の durable operation はすべて同期メソッドであり、await は使いません (Node.js/TypeScript SDK は Promise ベースで await context.step(...) と記述します)。いずれの言語でも「context.step() の中に副作用のある処理を閉じ込め、外側は決定的なコードだけにする」という原則は共通です。

4-2. Terraform: durable_config で Durable Functions を有効化

Durable Functions を Terraform で構築する場合、aws_lambda_functiondurable_config ブロックを追加します。この機能を扱うには hashicorp/aws provider 6.25.0 以降 が必須です。§2 のベースラインである ~> 5.0 とは別に、Durable Functions を有効化する Lambda 関数リソースだけは provider バージョンを引き上げてください。また、durable execution は qualified ARN (バージョンまたはエイリアス) でのみ呼び出せるため、aws_lambda_alias の作成も必須です。

terraform {
  required_version = ">= 1.9.0"
  required_providers {
 aws = {
source  = "hashicorp/aws"
version = ">= 6.25.0"  # durable_config利用時の最低バージョン
 }
  }
}

# チェックポイント操作 (lambda:CheckpointDurableExecution / lambda:GetDurableExecutionState) を許可
resource "aws_iam_role_policy_attachment" "durable_execution" {
  role = aws_iam_role.order_workflow.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy"
}

resource "aws_lambda_function" "order_workflow" {
  function_name = "order-durable-workflow"
  role = aws_iam_role.order_workflow.arn
  handler = "lambda_function.lambda_handler"
  runtime = "python3.14"
  filename= "order_workflow.zip"
  timeout = 300  # 単一invocationの上限 (15分以内)
  memory_size= 512

  durable_config {
 execution_timeout = 86400  # durable execution全体の上限 (秒・ここでは24時間)
 retention_period= 14  # 実行履歴・チェックポイントの保持日数
  }
}

# durable executionはqualified ARN (version/alias) 経由でのみ呼び出せる
resource "aws_lambda_alias" "prod" {
  name = "prod"
  function_name = aws_lambda_function.order_workflow.function_name
  function_version = aws_lambda_function.order_workflow.version
}

durable_config は関数作成時にのみ設定可能で、既存関数へ後付けで有効化できません。設計段階で Durable Functions を使うと決めたら、最初から durable_config 付きで関数を作成する必要があります。

4-3. 多段ワークフロー実装 — steps でのビジネスロジック分割

注文処理を例に、検証・与信確認・在庫確保・確定の4ステップに分割した実装を見ていきます。各ステップは独立した context.step() 呼び出しとして切り出し、それぞれが個別にチェックポイントされます。

from aws_durable_execution_sdk_python import DurableContext, durable_execution

@durable_execution
def lambda_handler(event, context: DurableContext):
 order_id = event["orderId"]
 customer_id = event["customerId"]
 items = event["items"]

 # step1: 注文検証 — 完了するとチェックポイントされ、以降のリトライでは再実行されない
 validation = context.step(
  lambda _: order_service.validate(customer_id, items),
  name="validate-order",
 )
 if not validation["itemsValid"]:
  return {"orderId": order_id, "status": "rejected", "reason": "invalid_items"}

 # step2: 与信確認 (built-in retryが自動適用される)
 authorization = context.step(
  lambda _: payment_service.authorize(validation["customer"], items),
  name="authorize-payment",
 )

 # step3: 在庫確保 — 外部サービス呼び出しは一時的な障害が起きやすい代表例
 inventory = context.step(
  lambda _: inventory_service.reserve(items),
  name="reserve-inventory",
 )

 # step4: 注文確定
 confirmation = context.step(
  lambda _: order_service.confirm(order_id, authorization, inventory),
  name="confirm-order",
 )

 return {"orderId": order_id, "status": "completed", "confirmation": confirmation}

context.step() に渡す name は、チェックポイントログ上でどのステップかを識別するための名前です。Lambda コンソールの Durable executions タブでは、この名前ごとに実行タイムライン・チェックポイント履歴・各ステップの結果を確認できます。

ステップの間にある if not validation["itemsValid"]: のような条件分岐は context.step() の外側にあるためチェックポイント化されませんが、決定的な処理 (同じ入力に対して常に同じ結果を返す) であれば replay 時に再実行されても問題ありません。非決定的な処理だけを step の内側に閉じ込める、という原則がここでも徹底されています。

4-4. 失敗復旧の実証 — チェックポイントからの resume

steps の真価は、途中のステップで障害が発生したときに発揮されます。以下は reserve-inventory (step3) で一時的な障害が発生し、built-in retry によって checkpoint から resume するシーケンスです。

sequenceDiagram
 participant Client as 呼び出し元
 participant Lambda as Lambda Service
 participant Fn as order_workflow
 participant CP as チェックポイントログ

 Note over Client,CP: 初回実行 — step1・step2は成功、step3で一時障害
 Client->>Lambda: Invoke (qualified ARN)
 Lambda->>Fn: 実行開始 (durable execution開始)
 Fn->>CP: checkpoint保存 (validate-order: 検証OK)
 Fn->>CP: checkpoint保存 (authorize-payment: 与信OK)
 Fn->>Fn: reserve-inventory 実行 (在庫サービスAPI呼び出し)
 Fn--xLambda: reserve-inventory がタイムアウトで異常終了
 Lambda->>Lambda: built-in retryをスケジュール

 Note over Client,CP: リトライ実行 — replayでstep1・2をスキップ
 Lambda->>Fn: 再実行 (同一durable execution・最初からreplay)
 Fn->>CP: replay: validate-orderの保存結果を再利用 (再実行なし)
 Fn->>CP: replay: authorize-paymentの保存結果を再利用 (再実行なし)
 Fn->>Fn: reserve-inventoryを再実行 (built-in retry)
 Fn->>CP: checkpoint保存 (reserve-inventory: 在庫確保OK)
 Fn->>CP: checkpoint保存 (confirm-order: 確定OK)
 Fn->>Lambda: 全step完了・durable execution成功 (SUCCEEDED)
 Lambda->>Client: 結果返却

このシーケンスのポイントは、リトライ時に validate-orderauthorize-payment再実行されていない ことです。replay はチェックポイントログを読み、完了済みステップの保存結果をそのまま差し込んで先に進みます。これにより、与信確認 (課金が発生しうる処理) が二重に実行される事故を構造的に防いでいます。

一方で、reserve-inventory のように未完了のまま中断したステップは resume 時に再実行されます。そのため、steps の内側に書く処理は 冪等 (idempotent) に設計する ことが前提になります。在庫確保 API が「同じ注文IDに対して2回呼ばれても在庫を二重に減らさない」ことを保証していなければ、リトライのたびに在庫が余分に減っていく不具合につながります。この冪等性設計の勘所は §7 の詰まりポイントで具体例とともに扱います。


5. 実装: 長時間待機 — waits で human-in-the-loop / 外部ポーリング (★山場2)

fig03: waits 実行フロー図 (課金なし中断→外部イベント/タイマーで再開・human-in-the-loop/polling)
fig03: waits 実行フロー図 (課金なし中断→外部イベント/タイマーで再開・human-in-the-loop/polling)

Durable Functions の waits は、実行を一時停止させて Lambda の計算課金を止めるプリミティブです。停止中は関数コンテナが解放されるため、on-demand 関数では再開までの待機時間に対してコンピューティング時間課金が発生しません。§4 の steps がビジネスロジックを堅牢に「実行する」ためのプリミティブだったのに対し、waits は「実行しない時間」を安全かつ低コストに扱うためのプリミティブです。

5-1. waits の3系統 — 時間指定・コールバック・条件ポーリング

Durable Execution SDK の待機系オペレーションは目的に応じて3系統に分かれます。承認待ちのように「いつ再開するか外部が決める」場合と、固定時間だけ待つ場合とでは、選ぶべき API が異なります。

オペレーション再開トリガー主なユースケース課金
context.wait()指定した時間(秒〜日単位、最小1秒・最大1年)の経過デプロイ待ち・レート制限のための固定遅延・再試行前のクールダウン停止中は課金なし
context.createCallback() / waitForCallback()外部システムからの SendDurableExecutionCallbackSuccess / Failure API呼び出しhuman-in-the-loop 承認・決済プロバイダーの非同期通知・Webhook 待ち停止中は課金なし(callback timeoutは関数タイムアウトを超えて設定可)
context.waitForCondition()check 関数が完了条件を満たすまでのポーリング(exponential backoffで再試行)外部バッチジョブの完了待ち・非同期APIのステータス確認check 関数実行分のみ課金・待機区間は無課金

いずれも実行結果はチェックポイントとして永続化されるため、待機中に Lambda 実行環境が再起動されても、待機の残り時間や callback ID は失われません。ただし waits は一度開始すると取り消せないため、承認却下やタイムアウトによる分岐はアプリケーションコード側(callback の失敗応答・タイムアウト例外の catch)で設計する必要があります。

5-2. human-in-the-loop 承認待ちの実装 (waitForCallback)

代表的な waits ユースケースが「人間の承認を待ってから処理を再開する」ワークフローです。waitForCallback は callback の発行・外部システムへの通知・結果待ちを1操作にまとめます。

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.config import CallbackConfig, Duration


def notify_approver(callback_id: str, ctx) -> None:
 # callback_id を承認システム (Slack/社内ワークフローツール等) へ送付する。
 # 承認者がリンクを押すと、承認システム側の Lambda が
 # send-durable-execution-callback-success を呼び出す。
 send_approval_request(callback_id=callback_id, request_id=ctx.event["request_id"])


@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
 order = context.step("validate_order", lambda: validate_order(event))

 config = CallbackConfig(
  timeout=Duration.from_hours(24),
  heartbeat_timeout=Duration.from_minutes(30),
 )
 approval = context.wait_for_callback(
  submitter=notify_approver,
  name="wait-for-manager-approval",
  config=config,
 )

 if approval["status"] != "approved":
  return context.step("cancel_order", lambda: cancel_order(order))

 return context.step("fulfill_order", lambda: fulfill_order(order))

承認を受け取る側(承認システムの Webhook 処理 Lambda)は、callback_id を使って結果を送信するだけです。

# 承認ボタン押下時に承認システム側 Lambda が実行
aws lambda send-durable-execution-callback-success \
  --callback-id "$CALLBACK_ID" \
  --cli-binary-format raw-in-base64-out \
  --result '{"status":"approved","approver":"manager-01"}'

# 却下時
aws lambda send-durable-execution-callback-failure \
  --callback-id "$CALLBACK_ID" \
  --error ErrorType=ApprovalRejected,ErrorMessage="承認者により却下"

承認システム側 Lambda には callback API を呼び出すための IAM 権限が必要です。

data "aws_iam_policy_document" "callback_invoke" {
  statement {
 effect = "Allow"
 actions = [
"lambda:SendDurableExecutionCallbackSuccess",
"lambda:SendDurableExecutionCallbackFailure",
 ]
 resources = [aws_lambda_function.order_workflow.arn]
  }
}

resource "aws_iam_role_policy" "approval_notifier_callback" {
  name= "approval-notifier-callback-invoke"
  role= aws_iam_role.approval_notifier.id
  policy = data.aws_iam_policy_document.callback_invoke.json
}

timeout (24時間) は承認担当者が不在の場合の上限であり、Lambda 関数自体のタイムアウト(最大15分/回)を超えて設定できます。これは待機中に Lambda 実行環境そのものが停止しているためです。heartbeat_timeout は、長時間の承認フローで外部システムが定期的に生存確認を送る場合の異常検知用で、実際の承認待ち時間より短く設定します。

5-3. 外部依存のポーリング実装 (waitForCondition)

Webhook を持たない外部システム(バッチジョブの完了確認など)を待つ場合は、waitForCondition でポーリングを実装します。ポーリング自体は check 関数の実行として課金されますが、待機区間そのものは課金されません。

from aws_durable_execution_sdk_python import DurableContext, durable_execution
from aws_durable_execution_sdk_python.wait import WaitStrategy
from aws_durable_execution_sdk_python.config import Duration


def check_batch_job_status(ctx) -> bool:
 status = get_external_batch_status(ctx.state["job_id"])
 return status == "COMPLETED"


@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
 job = context.step("submit_batch_job", lambda: submit_batch_job(event))

 strategy = WaitStrategy.exponential_backoff(
  initial_delay=Duration.from_seconds(10),
  max_delay=Duration.from_minutes(5),
  max_attempts=50,
  jitter="FULL",
 )
 context.wait_for_condition(
  check=check_batch_job_status,
  wait_strategy=strategy,
  name="poll-batch-job",
 )

 return context.step("process_result", lambda: process_batch_result(job))

check 関数はステータス確認に限定し、重い処理や副作用を持たせないことが公式ベストプラクティスです。exponential backoff + jitter を用いることで、外部システム障害時に再試行が集中してリトライストームになるのを防ぎます。

5-4. コスト最適化効果 — 待機時間は課金されない

waits の最大の利点は、on-demand 関数において「待機中は Duration 課金が発生しない」ことです。たとえば承認待ちが平均12時間かかるワークフローを、Lambda + waits と、常時ポーリングする EventBridge Scheduler + Lambda(§7で比較する Step Functions Standard も同様の課金モデル)で比較すると、waits 実装では実処理時間(数百ミリ秒〜数秒 ×実行回数)のみが Duration 課金対象になり、待機している12時間分の Duration 課金は発生しません。長時間・低頻度の待機を伴うワークフローほど、この差はコストに直結します。

一方で、待機中も Durable Execution のチェックポイント・実行履歴の保持自体には RetentionPeriodInDays に応じたストレージ課金が発生する点は留意してください。waits は「計算課金を止める」ものであり、状態管理コストをゼロにするものではありません。


6. Step Functions との使い分け — 選択基準・コスト比較・移行判断

fig04: Durable Functions vs Step Functions 選択基準 比較図 (実行時間/コスト/状態管理/可視化/オーケストレーション粒度)
fig04: Durable Functions vs Step Functions 選択基準 比較図 (実行時間/コスト/状態管理/可視化/オーケストレーション粒度)

Lambda Durable FunctionsとAWS Step Functionsは、どちらも自動的な状態管理と失敗時の復旧を備えたワークフローオーケストレーション機能です。AWS公式ドキュメントは両者について「異なる開発者の好みとアーキテクチャパターンに対応するもの」と位置づけており、優劣で選ぶのではなく適材適所で使い分けるべきサービスとされています。Durable FunctionsはLambda内でのアプリケーション開発に最適化されており、Step FunctionsはAWSサービスを横断するワークフローオーケストレーションに特化しています。

本節では、実行時間・コスト・状態管理・可視化・オーケストレーション粒度の5軸で両者を整理し、いつどちらを選ぶべきかを判断できる状態を目指します。なお、Step Functionsそのものの基礎実装(ステートマシンの定義やAmazon States Languageの書き方)は、本シリーズVol1で解説済みのため、本節では改めて扱いません。

6-1. 5軸比較表

比較軸Lambda Durable FunctionsAWS Step Functions選択の目安
実行時間個々の呼び出しはLambda関数タイムアウト(最大15分)に制約されますが、durable execution全体は最大1年間継続できますStandardワークフローは最大1年、Expressワークフローは最大5分です待機を挟む長時間ワークフローで処理ロジックがLambdaで完結するなら前者、Express相当の高頻度・短時間処理ならStep Functionsが適しています
コストDurable operations $8.00/百万件 + データ書き込み $0.25/GB + データ保持 $0.15/GB・月が、標準Lambdaの実行時間・リクエスト課金に加算されますStandardは約$25/百万ステート遷移が課金されます待機時間が長く遷移回数(ステップ数)が少ないワークロードほどDurable Functionsがコスト面で有利になります
状態管理コード内のcheckpoint/replay機構で管理し、checkpoint 1件あたり256KBの上限があります。超過分はAmazon S3やDynamoDBへの参照退避が必要ですステート間で受け渡すJSON形式の状態をサービス側が管理し、実行履歴を自動的に保持します大きなペイロードを扱う場合は、どちらを選んでも外部ストレージの併用が前提になります
可視化Lambdaコンソールでログと実行詳細を確認できますが、専用のビジュアルデザイナーはありません実行コンソールでどのステートが失敗したか、各遷移の入出力を視覚的に確認できます障害対応時の可視性やチーム横断での説明責任を重視するなら、Step Functionsに軍配が上がります
オーケストレーション粒度JavaScript/TypeScript・Python・Javaなど標準プログラミング言語による、1関数内のコードレベル制御ですAmazon States Language(グラフベースDSL)による220以上のAWSサービスとのネイティブ統合が可能です複数のAWSサービス(Amazon ECS・DynamoDB Streams・Amazon SNSなど)を跨ぐ調整が必要なら、Step Functionsを選ぶべきです

6-2. 判断フレームワーク

AWS公式の意思決定フレームワークに沿って整理すると、次の問いに答えることで選択の方向性が見えてきます。

  • 主眼はどこにあるか: Lambda内でのアプリケーション開発が主眼ならDurable Functions、AWSサービス横断のワークフローオーケストレーションが主眼ならStep Functionsを選びます
  • 好みのプログラミングモデルは何か: 標準プログラミング言語で書きたいならDurable Functions、グラフベースDSLやビジュアルデザイナーで設計したいならStep Functionsを選びます
  • 関与するAWSサービスの数はどれくらいか: ほぼLambdaのみで完結するならDurable Functions、複数サービスに及ぶならStep Functionsを選びます
  • インフラの運用主体はどちらが望ましいか: Lambda環境内での柔軟性を求めるならDurable Functions、パッチ適用やランタイム更新が不要な完全マネージド運用を求めるならStep Functionsを選びます

両者は排他的な関係ではなく、併用も一般的なパターンです。Lambda内のアプリケーションロジックはDurable Functionsで、複数AWSサービスにまたがる高レベルなワークフローはStep Functionsで調整するというハイブリッド構成が、公式にも推奨されています。

6-3. 選択判断ツリー

単発Lambda・Durable Functions・Step Functionsのいずれを選ぶべきかを、以下の判断ツリーに整理しました。

flowchart TD
 A[ワークフローを検討する] --> B{待機や複数ステップの調整が必要か}
 B -->|いいえ・単発の処理で完結する| C[単発Lambda]
 B -->|はい| D{ロジックはLambda単体で完結するか}
 D -->|はい・標準言語でコード制御したい| E{複数AWSサービスを跨ぐ調整や<br/>ビジュアルな可視化が必要か}
 E -->|いいえ| F[Lambda Durable Functions]
 E -->|はい・非技術者への説明も必要| G[AWS Step Functions]
 D -->|いいえ・Amazon ECS/DynamoDB Streams/<br/>Amazon SNS等の複数サービス連携が前提| G

このツリーが示すとおり、判断の起点は「待機やチェックポイントを伴う複数ステップの処理かどうか」であり、次に「Lambda単体で完結するか」、最後に「可視化やサービス横断統合が必須かどうか」という順で絞り込みます。単発の同期処理であれば、Durable Functionsを導入する必要はなく、通常のLambda関数で十分対応できます。

6-4. Vol1(Step Functions基礎)との補完関係

本Vol4のDurable FunctionsとVol1で解説したStep Functionsは、対立する選択肢ではなく補完関係にあります。Step Functionsのステートマシン定義・Amazon States Languageの書き方・実行ロールの設計といった基礎実装は、Vol1で解説済みの内容をそのまま活用してください。

Step Functions基礎実装への参照

  • ステートマシンの定義・Amazon States Language・実行ロール設計はVol1で解説済みです。本節では「いつDurable Functionsを選び、いつStep Functionsを選ぶか」の判断基準に絞って解説しました

Serverless本番運用 Vol1 — Lambda × API Gateway × Step Functions基礎を読む


7. 詰まりポイント7選 図解

fig05: 詰まりポイント判断ツリー
fig05: 詰まりポイント判断ツリー

Durable Functionsは2025-12にGAしたばかりの新機能であり、checkpoint/replayモデルという通常のLambda開発とは異なる設計思想を前提とします。本番導入時に見落としやすい典型的な詰まりポイントを7つ整理し、それぞれの回避策を示します。

7-1. stepsの冪等性設計

Durable Functionsのオーケストレーションコードは、実行のたびにreplay(最初から再実行しつつ完了済みcheckpointをスキップ)される前提で書く必要があります。乱数生成・現在時刻の取得・外部API呼び出しといった非決定的な処理をstepsの外側(オーケストレーション層)に直接書いてしまうと、replay時に結果が変わってしまい、checkpointとの整合性が崩れます。

// NG: オーケストレーション層で乱数・時刻を直接使用してしまう
const delay = Math.random() * 1000;

// OK: 非決定的な処理はstepの中に閉じ込める
const delay = await ctx.step('compute-delay', async () => Math.random() * 1000);

対応策として、副作用を伴う処理はすべてstepsの内側に閉じ込め、execution nameを使って同一実行が重複起動されないようにします。durable executionは失敗時に自動再試行されないため、非同期呼び出し時はデッドレターキュー(DLQ)を必ず設定し、at-least-onceの再試行を前提にsteps内の処理をべき等に設計することが欠かせません。

7-2. waitsのタイムアウト仕様

waitsは課金なしで実行を中断できますが、無制限に待てるわけではありません。durable execution全体のタイムアウトは最大1年ですが、Amazon SQS・Kinesis・DynamoDB StreamsなどのEvent Source Mapping経由で起動したDurable Functionsは、個々の呼び出しタイムアウト(最大15分)とは別に、durable execution全体の実行時間が15分を超えると失敗する制約があります。

例えば、SQSメッセージを処理して2分のstepを3回実行したのち20分待って最後のstepを完了する設計は、合計22分となり15分制限を超えて失敗します。この制約はEvent Source Mapping経由で呼び出す場合にのみ適用され、非同期呼び出しでは適用されません。

対応策として、Event Source Mapping経由で長時間ワークフローを起動する場合は、仲介用の通常Lambda関数を挟み、そこからexecution nameを指定した非同期(Event型)呼び出しでDurable Functionsを起動する「intermediary function」パターンを使います。

7-3. チェックポイント肥大

checkpointのペイロードは1件あたり256KBの上限があります。大きなレスポンス(画像処理結果や大量レコードの集計結果など)をそのままstepsの戻り値にしてしまうと、この上限に抵触して実行が失敗します。

対応策として、大きなデータはAmazon S3やDynamoDBに書き込み、checkpointにはオブジェクトキーやアイテムIDといった参照のみを持たせます。SDKのカスタムシリアライザ機能を使えば、この退避処理を透過的に実装できます。

7-4. 対応ランタイム制約

Durable Functionsのマネージドランタイムは、Node.js(nodejs22.x/nodejs24.x)・Python(python3.13/python3.14)・Java(java17/java21/java25)に限定されています。これより古いランタイムバージョンや、Go・.NET・Rubyなど他言語を使う場合は、マネージドランタイムでは選択できません。

対応策として、マネージドランタイムの対象外言語・バージョンを使う場合は、コンテナイメージ(最大10GB非圧縮)にDurable Execution SDKを組み込む方式を使います。ただしコンテナイメージはコールドスタートの時間が伸びやすい点に注意してください。レイテンシ要件が厳しい場合は、Provisioned Concurrencyの併用を検討してください。

7-5. 既存Lambdaからの移行

既存のLambda関数にDurable Execution SDKを追加するだけでは、replayモデルに対応したコードにはなりません。オーケストレーション層に紛れ込んだ副作用や非決定的処理を洗い出し、stepsに移し替える設計変更が必須です。また、本番運用中の関数は$LATESTではなくバージョン番号やエイリアスで呼び出し、実行中(in-flight)のexecutionが新しいコードバージョンでも正しく扱える状態を維持する必要があります。

対応策として、移行時はまず影響範囲の小さい新規ワークフローでDurable Functionsを試験導入し、既存の重要な本番Lambda関数への適用は、ステップ設計とバージョニング運用の両方を検証してから段階的に進めます。

7-6. Step Functionsと混同

Durable FunctionsはStep Functionsの完全な代替ではありません。Step Functionsが持つビジュアルなステート可視化・220以上のAWSサービスとのネイティブ統合・完全マネージドでのゼロメンテナンス運用は、Durable Functionsにはありません。特に、Amazon ECSタスクやDynamoDB Streamsトリガー、Amazon SNS通知などを含む複数サービス横断のワークフローをDurable Functionsだけで組もうとすると、Step Functionsが提供する可視性を失ったまま複雑な調整コードを自前で書くことになります。

対応策として、「Lambda単体で完結するかどうか」を移行判断の最初の分岐点にします(§6の判断ツリーを参照してください)。複数サービスを跨ぐ既存のStep Functionsワークフローは無理にDurable Functionsへ置き換えず、ハイブリッド構成を検討してください。

7-7. コスト誤算

「waitsは課金なし」という説明だけを見て、Durable Functions全体がほぼ無料だと誤解するのは危険です。実際にはDurable operations(steps・waits・execution開始などのcheckpoint操作)自体に$8.00/百万件の課金があり、加えてデータ書き込み$0.25/GB、データ保持$0.15/GB・月(デフォルト14日・最大90日で設定可能)が、標準Lambdaの実行時間・リクエスト課金に上乗せされます。

対応策として、コスト試算時は「待機中は課金されない」という一点だけでなく、durable operationsの発生回数・書き込みデータ量・保持期間設定(デフォルトの14日のままでよいか)を含めて計算します。保持期間を必要以上に長く設定すると、実行完了後もストレージ課金が積み上がる点に注意してください。


8. アンチパターン → 正解パターン変換演習 + シリーズ繋ぎ

ここまで解説した steps/waits の設計原則と、§7 で整理した詰まりポイントを踏まえ、Durable Functions の本番実装で実際に見かけるアンチパターンを5問の変換演習として整理します。それぞれ「なぜ問題か」を確認したうえで、正解パターンのコードと照らし合わせてください。

アンチパターン→正解パターン変換 5問

Q1: steps — 非決定的処理をオーケストレーション層に直接書いてしまう

# アンチパターン: オーケストレーション層で乱数・現在時刻を直接使用
@durable_execution
def lambda_handler(event, context: DurableContext):
 delay_seconds = random.randint(1, 10)  # replay のたびに値が変わる
 time.sleep(delay_seconds)
 return context.step(fulfill_order(event["orderId"]))

# 正解パターン: 非決定的な処理を@durable_stepの中に閉じ込める
@durable_step
def compute_delay(step_context):
 return random.randint(1, 10)

@durable_execution
def lambda_handler(event, context: DurableContext):
 delay_seconds = context.step(compute_delay())  # checkpointされ、replay時も同じ値
 return context.step(fulfill_order(event["orderId"]))

replay 時にオーケストレーション層の値が変わると、checkpoint との整合性が崩れ、意図しない分岐や重複実行につながります。

Q2: Terraform — 既存 Lambda 関数に durable_config を後付けしようとする

# アンチパターン: 既存関数のupdateでdurable_configを後付け(適用時にエラー)
resource "aws_lambda_function" "order_workflow" {
  function_name = "order-workflow" # 既存関数と同名で更新扱い
  runtime = "python3.14"
  handler = "lambda_function.lambda_handler"
  filename= "order_workflow.zip"

  durable_config {
 execution_timeout = 86400
  }
}

# 正解パターン: durable_config付きの新規関数として作成し、呼び出し元を切り替える
resource "aws_lambda_function" "order_workflow_durable" {
  function_name = "order-workflow-durable"  # 新規関数名
  runtime = "python3.14"
  handler = "lambda_function.lambda_handler"
  filename= "order_workflow.zip"

  durable_config {
 execution_timeout = 86400
 retention_period  = 14
  }
}

durable_config は関数作成時にのみ指定でき、既存関数への後付けはできません。移行時は新規関数として作成し、エイリアスやルーティング側で段階的に切り替える設計にします。

Q3: waits — wait_for_callback にタイムアウトを設定しない

# アンチパターン: timeout未設定 → 承認者が離席・退職すると永久に待機し続ける
approval = context.wait_for_callback(
 submitter=notify_approver,
 name="wait-for-manager-approval",
)

# 正解パターン: timeoutを設定し、期限切れ時の代替フローを用意する
config = CallbackConfig(timeout=Duration.from_hours(24))
approval = context.wait_for_callback(
 submitter=notify_approver,
 name="wait-for-manager-approval",
 config=config,
)
if approval is None:  # timeout到達時
 return context.step(escalate_to_backup_approver(event["orderId"]))

timeout を省略すると durable execution はコールバックを待ち続け、承認者が対応不能になった場合の代替フロー(エスカレーション)を組み込めなくなります。

Q4: durable_config — retention_period をデフォルトのまま監査要件に使う

# アンチパターン: デフォルト(14日)のまま、30日後の監査で実行履歴を参照しようとして消失
durable_config {
  execution_timeout = 86400
  # retention_period未指定 → デフォルト14日
}

# 正解パターン: 監査要件に合わせてretention_periodを明示的に延長する(最大90日)
durable_config {
  execution_timeout = 86400
  retention_period  = 90  # 監査要件に合わせて最大値まで延長
}

実行履歴・チェックポイントの保持期間はデフォルト14日で、監査や障害調査で長期保持が必要な場合は明示的に延長しなければ、参照したい時点で既にデータが失われています。

Q5: Event Source Mapping — 合計実行時間が15分を超える waits 設計

# アンチパターン: SQSトリガー起動で2分のstepを3回 + 20分waitsを直列に実行(合計22分)
@durable_execution
def lambda_handler(event, context: DurableContext):  # SQS Event Source Mapping経由
 for _ in range(3):
  context.step(process_batch(event)) # 2分 × 3 = 6分
 context.wait_for_condition(check_external_status, timeout=Duration.from_minutes(20))
 # 合計22分 > Event Source Mapping経由の15分制約を超えて失敗

# 正解パターン: 仲介Lambdaを挟み、非同期(Event型)呼び出しでDurable Functionsを起動する
def intermediary_handler(event, context):  # 通常のLambda(SQSトリガー)
 lambda_client.invoke(
  FunctionName="order-workflow-durable",
  InvocationType="Event", # 非同期呼び出し(15分制約の対象外)
  Payload=json.dumps({"executionName": event["messageId"], "body": event}),
 )

Amazon SQS・Kinesis・DynamoDB Streams などの Event Source Mapping 経由で起動した Durable Functions は、durable execution 全体の実行時間が15分を超えると失敗します。長時間ワークフローは仲介用の通常 Lambda を挟み、非同期呼び出しで起動する「intermediary function」パターンで回避します。

Serverless 本番運用シリーズの繋ぎ

本 Vol4 では、2025-12 GA の Lambda Durable Functions を、steps によるチェックポイント付きロジック分割、waits による課金なし中断、そして Step Functions との使い分け基準という3本柱で解説しました。Vol1 で確立した Step Functions 基礎、Vol2 のイベント駆動アーキテクチャ、Vol3 の Lambda 高度化と組み合わせることで、サーバーレスワークロードのオーケストレーション選択肢は「単発 Lambda」「Durable Functions」「Step Functions」「イベント駆動 (EventBridge/SQS/SNS/Kinesis)」の4象限が出揃ったことになります。

次にどのパターンを選ぶかで迷ったときは、まず §6 の判断ツリーに立ち返り、実行時間・コスト・状態管理・可視化・オーケストレーション粒度の5軸で自分のワークロードを評価してください。既存の Step Functions ワークロードを Durable Functions へ移行すべきかどうかも、本 Vol4 で得た判断基準をもとに、まずは影響範囲の小さいワークロードから段階的に検証することを推奨します。

シリーズ Vol1 (Lambda × API Gateway × Step Functions 基礎) を読む


AWS / サーバーレスの最新記事8件