Lambda Durable Functions 解説:Step Functions との使い分けと実践的ユースケース

記事タイトルとURLをコピーする

re:Invent 2025 会場からコップンカー!!
アプリケーションサービス部の千葉です。

2025年12月、AWS は Lambda Durable Functions を一般公開しました。
この新機能により、Lambda 関数内で複数ステップのワークフローを構築し、最長1年間の待機も可能になります。

「Step Functions があるのに、なぜ新しいワークフロー機能が必要なのか?」

この疑問に答えながら、Durable Functions の特徴、適切なユースケース、そして Step Functions との使い分けについて解説します。


Durable Functions とは

基本コンセプト: Durable Functions は、通常の Lambda 関数にチェックポイント機能を追加したものです。

# NOTE:
# Durable Functions は Durable Execution SDK で Lambda のハンドラを wrap し、
# ハンドラ内で DurableContext(step / wait / parallel 等のAPI)を利用します。
# SDK の import パスやラッパー関数名は更新される可能性があるため、
# 最新の名称は公式ドキュメント/SDKに合わせてください。

async def handler(event, lambda_context, durable_context):
    # Step 1: 注文を受け付け
    order = await durable_context.step("create_order", create_order, event)

    # Step 2: 決済処理
    payment = await durable_context.step("process_payment", charge_card, order)

    # Step 3: 承認待ち(最長48時間)
    approval = await durable_context.wait_for_callback(
        "manager_approval",
        timeout_hours=48,
    )

    # Step 4: 配送手配
    shipment = await durable_context.step("arrange_shipment", ship_order, order)

    return {"status": "completed", "shipment_id": shipment.id}

各 step や wait の完了時に自動的にチェックポイントが作成され、障害発生時はそこから再開できます。

# 主な特徴

特徴 説明
自動チェックポイント 各ステップ完了時に進捗を永続化
コスト効率の良い待機 Wait により実行をサスペンドでき、待機中は実行コンピュートの消費を抑えられる
最長1年の待機 人間の判断を含む長期プロセスに対応
慣れ親しんだ開発体験 通常の Lambda コードと同じ書き方

Step Functions との比較: 開発体験の違い

# Durable Functions: コードベース

## 条件分岐も通常のPythonで記述
if order.total > 100000:
    approval = await context.wait_for_callback("executive_approval")
else:
    approval = await context.step("auto_approve", auto_approve, order)

# Step Functions: 定義ファイルベース

States:
  CheckAmount:
    Type: Choice
    Choices:
      - Variable: "$.order.total"
        NumericGreaterThan: 100000
        Next: ExecutiveApproval
    Default: AutoApprove
  ExecutiveApproval:
    Type: Task
    Resource: arn:aws:states:::lambda:invoke.waitForTaskToken

機能比較

観点 Durable Functions Step Functions
定義方法 コード内で直接記述 ASL (JSON/YAML)
学習コスト 低(Lambda 経験者向け) 中〜高(ASL 習得必要)
可視化 コンソールで実行履歴確認 ビジュアルワークフローエディタ
デバッグ 通常の Lambda と同様 Workflow Studio で視覚的に
並列実行 context.parallel() / context.map() Parallel / Map ステート
エラーハンドリング try-except で記述 Catch/Retry を定義
実行状態 RUNNING / SUCCEEDED / FAILED など より詳細な状態遷移
最大実行時間 1年 Standard: 1年 / Express: 5分
料金体系 実行時間課金(Wait により実行コンピュートを抑えられる)、回数課金 状態遷移回数課金

ユースケース: Durable Functions が適しているケース

1. シンプルな承認フロー

申請 → 承認待ち → 処理実行 → 完了

直線的なフローで、コードで書いた方が早いケース。

2. 既存 Lambda の拡張

すでに Lambda で実装されているロジックに、待機やリトライを追加したい場合。
Step Functions への移行コストを避けられます。

3. 人間の判断を含む AI ワークフロー

# AI が下書き → 人間がレビュー → AI が修正 → 公開
draft = await context.step("ai_draft", generate_with_llm, prompt)
feedback = await context.wait_for_callback("human_review", timeout_days=7)
final = await context.step("ai_revise", revise_with_llm, draft, feedback)
await context.step("publish", publish_content, final)

LLM を使ったコンテンツ生成で、人間のレビューを待機するパターン。

4. 分散トランザクション(Saga パターン)

try:
    payment = await context.step("charge", charge_card, order)
    inventory = await context.step("reserve", reserve_inventory, order)
    shipment = await context.step("ship", create_shipment, order)
except Exception as e:
    # 補償トランザクション
    await context.step("rollback", compensate, order)
    raise

複数サービスにまたがる処理を、コードで直感的に記述。

ユースケース: Step Functions が適しているケース

1. 複雑な並列・分岐処理

10以上の分岐、複数の並列実行、動的な並列度制御が必要な場合は、ビジュアルエディタで設計した方が見通しが良い。

2. 運用監視が重要なミッションクリティカルな処理

Step Functions の成熟した監視・アラート機能が活きるケース。

3. 非エンジニアもワークフローを理解する必要がある

ビジネスサイドのステークホルダーにフローを説明する際、ビジュアル図があると効果的。

4. AWS サービスとのネイティブ統合

# Step Functions は200以上のAWSサービスと直接統合

Resource: arn:aws:states:::dynamodb:putItem

Lambda を経由せず直接 AWS サービスを呼び出せるため、シンプルな統合では Step Functions の方が効率的。


定期バッチ処理との相性

Durable Functions を検討する際、定期バッチ処理との相性には注意が必要です。

問題の起きそうなシナリオ

Day 1: バッチ実行開始 → 外部API待ち(Wait状態)
Day 2: 新しいバッチが起動 → Day 1 の Wait がまだ残っている
       → データ不整合の可能性

なぜ問題になるか

  • Wait を含む実行は、運用上 RUNNING として見える場合がある
  • バッチの二重起動を避けるには、RUNNING の有無確認に加え、ExecutionName の命名規則や開始時刻などで「対象実行」を絞る設計が必要
  • 「前回と同一系統の実行が残っていない」ことを確認せずに次のバッチが走ると、データ不整合の可能性がある

対策

import boto3

def has_running_executions(function_name: str) -> bool:
    """RUNNING 状態の実行が存在するかを確認(ページネーション対応)"""
    client = boto3.client("lambda")
    token = None

    while True:
        kwargs = {"FunctionName": function_name, "Status": "RUNNING"}
        if token:
            kwargs["NextToken"] = token

        response = client.list_durable_executions_by_function(**kwargs)

        executions = response.get("Executions", [])
        if executions:
            return True  # 1件でも見つかったら即終了

        token = response.get("NextToken")
        if not token:
            return False

def batch_entry(event, context):
    if has_running_executions("my-durable-function"):
        raise Exception("前回の実行が完了していません")
    # 本処理を開始

本質的な考え方

Durable Functions はイベントドリブンな処理を想定しています:

  • ユーザーアクションで開始
  • 人間の判断や外部イベントを待機
  • 完了まで一貫したフロー

定期バッチのような「前回と今回が独立している」処理では、実行の重複管理を自前で実装する必要があります。


まとめ:選択のフローチャート

ワークフローを実装したい
    │
    ├─ 既存の Lambda コードを拡張したい?
    │   └─ Yes → Durable Functions
    │
    ├─ ビジュアルでの設計・監視が重要?
    │   └─ Yes → Step Functions
    │
    ├─ AWS サービスと直接統合したい?
    │   └─ Yes → Step Functions
    │
    ├─ 人間の待機を含む直線的フロー?
    │   └─ Yes → Durable Functions
    │
    ├─ 複雑な並列・分岐がある?
    │   └─ Yes → Step Functions
    │
    └─ 迷ったら → Step Functions(実績と成熟度)

おわりに

Lambda Durable Functions は、シンプルなワークフローを「Lambda らしく」書きたい開発者にとって魅力的な選択肢です。

一方で、GA 直後ということもあり、運用ノウハウはこれから蓄積されていく段階です。本番導入の際は、監視・アラートの設計と、実行状態の管理方法を 十分に検討することをおすすめします。


参考リンク

千葉 哲也 (執筆記事の一覧)

アプリケーションサービス部

コーヒーゼリーが好きです。