re:Invent 2025 新機能:AWS Lambda Durable Functions 詳解。仕組み、コスト、Step Functions との違い

AWS

はじめに:Lambda の歴史が動いた re:Invent 2025

2014 年の登場以来、AWS Lambda は「ステートレス」であることをその美学としてきました。しかし、現実のビジネスロジックは往々にして「状態(ステート)」を必要とします。

re:Invent 2025 で発表された AWS Lambda Durable Functions は、この 10 年の常識を覆すアップデートです。Lambda に stepwait といった操作を導入することで、処理途中の チェックポイント(状態) を保存しながら実行を継続できるようになりました。

本記事では、この新機能の背景から、アーキテクチャ、Step Functions との使い分けまでを解説します。


開発の背景:モノリスの書き心地をマイクロサービスへ

「Build like Monoliths, Deploy like Microservices」

開発者にとって、モノリス(単一のコードベース)は理解しやすく、ローカルでのデバッグも容易です。一方で、運用フェーズではスケーラビリティや独立デプロイが可能なマイクロサービスが理想です。

AWS はこれまで、Lambda、Step Functions (2016)、EventBridge (2019) を通じてこのギャップを埋めてきました。しかし、開発者は依然として以下の不満を抱えていました。

  • ASL (Amazon States Language) の学習コスト: ワークフローを記述するために独自の JSON 構造を書く必要がある。
  • ロジックの断片化: 本来 1 つのビジネスドメインであるはずの処理が、複数の Lambda 関数に分割され、管理が複雑になる。
  • 長時間の待機: Lambda 単体では最大 15 分という制限があり、数日間の待機が必要な場合は Step Functions が必須だった。

AWS Lambda Durable Functions は、「慣れ親しんだプログラミング言語のまま、長時間のワークフローを Lambda 1 つで完結させる」 ことを目指して誕生しました。


Durable Execution の仕組み:チェックポイントとリプレイ

Durable Functions の核となる概念は Durable Execution(永続実行) です。これは、SDK が提供するプリミティブを使用して、進捗をチェックポイントし、障害や待機から回復する仕組みです。

チェックポイント (Checkpointing)

関数が context.step() を実行するたびに、Lambda はその結果を自動的に保存します。これが「チェックポイント」です。

リプレイ (Replay) のメカニズム

これが最も重要なポイントです。関数が待機(wait)から復帰したり、エラーからリトライしたりする際、Lambda は関数を最初から(ハンドラの先頭から)再実行します。

  1. 再開: Lambda が再度インボークされる。
  2. ログの確認: SDK がこれまでの「チェックポイントログ」を確認。
  3. 値の置換: すでに完了している step に到達すると、実際の処理は行わず、保存されている結果を即座に返します。
  4. 継続: 未完了のステップから実際の処理を再開します。
[!CAUTION]

非決定論的(Non-deterministic)な操作の禁止

リプレイの特性上、コードは「同じ入力に対して常に同じ結果」を返す必要があります。random() や datetime.now() を step の外で直接使うと、リプレイ時に値が変わり、不整合(Non-deterministic error)を引き起こします。これらは必ず step 内で実行するか、SDK の提供するメソッドを使用してください。


実装ガイド:SDK と DurableContext

現在、JavaScript/TypeScript および Python 用の SDK が提供されています。

Python での実装例

Python SDK は同期的な記述スタイルを採用しており、await を使用しません。

Python

from aws_durable_execution_sdk_python import (
    DurableContext,
    durable_execution,
    durable_step,
)

@durable_step
def validate_order(step_context, order_id):
    step_context.logger.info(f"Validating order {order_id}")
    return {"order_id": order_id, "status": "validated"}

@durable_step
def process_payment(step_context, order_data):
    # 外部API連携などのビジネスロジック
    return {"status": "success", "transaction_id": "tx123"}

@durable_execution
def handler(event, context: DurableContext):
    # 1. 注文チェック
    order = context.step(lambda: validate_order(event))
    
    # 2. 決済実行(自動的にチェックポイントされる)
    payment = context.step(process_payment(order))
    
    # 3. 1時間の待機(リソース消費ゼロ)
    context.wait(3600)
    
    # 4. 完了通知
    result = context.step(lambda: send_notification(payment))
    
    return result

3 種類の待機操作(Wait States)

Durable Functions では、待機中に Lambda の実行(課金)を停止できます。

  1. Timer Wait: context.wait(seconds)。指定時間後に自動再開。
  2. Callback Wait: context.wait_for_callback()。外部システムからの承認などを待つ。
  3. Condition Wait (Polling): context.wait_for_condition()。特定の条件が True になるまで定期的にチェックする。

比較:Step Functions vs Durable Functions

「どちらを使うべきか?」という問いに対し、AWS は以下の基準を提示しています。

項目AWS Step FunctionsAWS Lambda Durable Functions
主な目的AWS 全体のオーケストレーションLambda 内のアプリ開発
記述方法ASL (JSON/YAML) / Visual Studioプログラミング言語 (TS, Python)
デバッグビジュアルグラフでの追跡コードレベルのデバッグ / ユニットテスト
統合220 以上の AWS サービスと直接連携SDK/コードによる柔軟な統合
最大実行期間1 年1 年

使い分けの指針:

  • Step Functions: 複数の AWS サービス(Sagemaker, Glue, ECS 等)を組み合わせる場合や、ワークフローを視覚的に可視化・管理したい場合。
  • Durable Functions: 複雑なループ、条件分岐、外部 API 連携など、「コードとして記述した方が自然な」 ロジックをステートフルに実行したい場合。

運用上の重要事項:呼び出しと設定

実行タイムアウトと保持期間

Durable Functions には 2 つの新しいタイムアウト概念があります。

  • Lambda Function Timeout (最大 15 分): 1 回のインボーク(実行)の制限。
  • Execution Timeout (最大 1 年): ワークフロー全体(開始から完了まで)の制限。

また、Retention Period(保持期間) は実行完了後に履歴データを保存する期間(デフォルト 14 日)を指します。

呼び出しの制約(Qualified ARNs)

Durable Functions を呼び出す際は、必ず バージョン または エイリアス を指定(Qualified ARN)する必要があります。

  • OK: my-function:1, my-function:prod, my-function:$LATEST
  • NG: my-function (アンクォリファイド)

これは、リプレイ中にコードが書き換わって非決定論的な動作になるのを防ぐため、特定のバージョンに実行を固定(ピン留め)するためです。


コスト構造

通常の Lambda 料金に加えて、以下の Durable 料金が発生します。(ap-northeast-1 の2025年12月時点の料金です)

  • Durable Operations: $10.60 / 100 万回(step, wait 等)
  • Data Written: $0.33 / GB(状態の保存)
  • Data Retained: $0.20 / GB-month(履歴の保持)

Step Functions (Standard) の状態遷移課金と比較して、ステップ数が多い複雑なロジックでは Durable Functions の方がコスト効率が高くなる可能性があります。

ユースケース:作業申請承認ワークフロー

(申請 → 承認待ち → 承認されたら作業実行 → 通知)

サンプルコード (Python)

from aws_durable_execution_sdk_python import (
    DurableContext,
    durable_execution,
    durable_step,
)
import json

# 1. 実際のビジネスロジックを step として定義
@durable_step
def validate_request(step_context, data):
    print(f"Validating request: {data['request_id']}")
    # 申請内容の正当性チェック
    return True

@durable_step
def execute_task(step_context, data):
    print(f"Executing task for: {data['request_id']}")
    # ここで実際の作業(リソース作成や設定変更など)を行う
    return {"status": "completed", "resource_id": "res-12345"}

@durable_step
def notify_user(step_context, message):
    print(f"Notification sent: {message}")
    # SNSやSES、Slackなどへの通知
    return "sent"

# 2. メインの Durable 実行ロジック
@durable_execution
def handler(event, context: DurableContext):
    """
    作業申請から承認、実行までを一元管理するハンドラ
    """
    request_data = event.get("data")
    
    # --- ステップ 1: 申請内容のチェック ---
    is_valid = context.step(lambda: validate_request(request_data))
    if not is_valid:
        return {"status": "rejected", "reason": "Invalid request format"}

    # --- ステップ 2: 外部の承認待ち (Wait for Callback) ---
    # ここで実行は一時停止し、課金も止まります。
    # callback_id を外部(Slackや管理画面)に送り、承認ボタンが押されるのを待ちます。
    approval_result = context.wait_for_callback(
        lambda callback_id: context.step(
            lambda: notify_user(f"承認が必要です。ID: {callback_id}")
        )
    )
    
    # 承認結果の判定
    if approval_result.get("action") != "APPROVE":
        context.step(lambda: notify_user("申請が却下されました。"))
        return {"status": "terminated", "reason": "User rejected"}

    # --- ステップ 3: 承認されたので作業実行 ---
    # 前のステップが完了していれば、リプレイ時はここから再開されます。
    task_result = context.step(lambda: execute_task(request_data))

    # --- ステップ 4: 最終通知 ---
    context.step(lambda: notify_user(f"作業が完了しました: {task_result['resource_id']}"))

    return {
        "status": "success",
        "execution_id": context.execution_id
    }

このコードをベースに、Durable Functions を導入する際のメリットを以下のように提案します。

1. 「やり直し」に強い設計

従来の Lambda では、途中でタイムアウトやエラーが起きると最初からやり直し(決済の二重実行など)のリスクがありました。 このコードでは、context.step で囲まれた部分は自動的にチェックポイントが作成されます。リトライ時、完了済みのステップはスキップされ、保存された結果が使われるため、安全に再開できます。

2. インフラ定義(ASL)からの解放

Step Functions を使う場合、JSON(ASL)で「次はこの Lambda、失敗したらここへ」と記述する必要がありました。 Durable Functions なら、上記のように if 文による条件分岐やエラーハンドリングを使い慣れた Python で直接書けるため、ロジックの見通しが劇的に良くなります。

3. ステート管理コストの削減

「夜間ジョブの進捗を Parameter Store や DynamoDB に記録して、どこまで終わったか管理する」といった実装が不要になります。Lambda 自体が「状態(どこまで終わったか)」をログとして持っているため、開発者はビジネスロジックに集中できます。

外部サービスから再開する際の実装パターン

外部システムが AWS 認証を持っていない場合、API Gateway をエンドポイントとして「中継用 Lambda」を用意するのが一般的です。

  1. Durable Lambda: callback_id を発行し、Slack や外部システムへ送付して wait する。
  2. 外部システム: ユーザーの操作等を受け、callback_id結果データ(Payload) を API Gateway へ POST。
  3. 中継 Lambda: AWS SDK を使用して、callback_idPayload を指定して complete_durable_task API を実行。

まとめ:サーバーレス開発の新しい選択肢

AWS Lambda Durable Functions は、マイクロサービスの堅牢性とモノリスの開発体験を両立させる強力なツールです。

  • 1 つの関数で長時間の処理が書ける
  • 使い慣れた言語でリトライや待機が制御できる
  • インフラ管理を意識せずステートフルなアプリが作れる

現時点では利用可能なリージョン(東京リージョン含む 15 リージョン)やランタイムに制限がありますが、今後のサーバーレスアーキテクチャのスタンダードの一つになることは間違いありません。

無料相談実施中
AWSを使用したサーバーレスアプリケーションの構築
サーバーレス開発内製化、AWSへの移行等
様々な課題について無料でご相談お受けいたします。
最新情報をチェックしよう!