0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Lambdaの新機能 durable functions

0
Last updated at Posted at 2026-06-17

下記公式記事を見て実装

開発者はイベントハンドラーで「Steps」や「Waits」などのdurable operationsを使用して、進捗をチェックポイントし、料金を発生させることなく、待機期間中に実行を一時停止します。

簡単に言うと、Lambdaを途中で待ち状態にして、15分しばり&料金の発生を回避することができる。Step Functionsを書かずに、Lambdaコード内で長時間・複数step・callback待ちのワークフローが実装できる。生成AIの領域だと、human-in-the-loopやレスポンスの長いLLM等のAPI処理が入るシナリオに対応できる。

実装

Python版は下記SDKを使用する必要がある

pip install aws-durable-execution-sdk-python
aws configure sso
aws sso login --profile
sam build
sam deploy --guided
aws sts get-caller-identity
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Python Lambda Durable Functions onboarding sample

Globals:
  Function:
    Runtime: python3.14
    Timeout: 30
    MemorySize: 512
    CodeUri: src/

Resources:
  DurableOnboardingFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: durable-onboarding-python
      Handler: onboarding_handler.handler
      AutoPublishAlias: live
      DurableConfig:
        ExecutionTimeout: 86400        # 24 hours
        RetentionPeriodInDays: 7
      Environment:
        Variables:
          CALLBACK_BASE_URL: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}"
      Policies:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
        # 本番では DynamoDB / SES など必要分だけ追加

  CallbackFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: durable-onboarding-callback
      Handler: callback_handler.handler
      Events:
        VerifyApi:
          Type: HttpApi
          Properties:
            Path: /verify
            Method: GET
      Policies:
        - Statement:
            - Effect: Allow
              Action:
                - lambda:SendDurableExecutionCallbackSuccess
              Resource: "*"
              # 本番では対象の Durable Function に絞る

Outputs:
  DurableOnboardingAliasArn:
    Value: !Ref DurableOnboardingFunction.Alias

  CallbackUrl:
    Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/verify"
aws lambda invoke `  --function-name "arn:aws:lambda:ap-northeast-1:*********:function:durable-onboarding-python:live" `  --invocation-type Event `  --cli-binary-format raw-in-base64-out `  --payload '{\"user_id\":\"user-001\",\"email\":\"user@example.com\",\"name\":\"User1\"}' `  response.json

下記コードは、ユーザー登録後にメール認証を待ち、認証リンクがクリックされたらユーザーを有効化する Lambda Durable Functions のサンプルです。

1. オンボーディング本体 Lambda
   - Durable Function
   - 入力検証
   - ユーザープロファイル作成
   - メール認証 callback 待ち
   - ユーザー有効化
   - Welcomeメール送信

2. メール認証 callback Lambda
   - 通常の Lambda
   - Function URL または API Gateway から呼ばれる
   - callbackId を受け取り、Durable Function に認証成功を返す
onboarding_handler.py
import os
from typing import Any

from aws_durable_execution_sdk_python import (
    DurableContext,
    StepContext,
    durable_execution,
    durable_step,
)
from aws_durable_execution_sdk_python.config import Duration, WaitForCallbackConfig
from aws_durable_execution_sdk_python.types import WaitForCallbackContext


CALLBACK_BASE_URL = os.environ["CALLBACK_BASE_URL"]


@durable_step
def validate_input(step_context: StepContext, event: dict) -> dict:
    email = event.get("email")
    name = event.get("name")
    user_id = event.get("user_id")

    if not email or not name or not user_id:
        raise ValueError("email, name, user_id are required")

    return {
        "user_id": user_id,
        "email": email,
        "name": name,
    }


@durable_step
def create_user_profile(step_context: StepContext, user: dict) -> dict:
    """
    本番では DynamoDB などに保存する。
    注意: Durable Step はリトライされる可能性があるので、
    user_id を idempotency key にして二重作成を防ぐ。
    """
    step_context.logger.info(
        "Creating user profile",
        extra={"user_id": user["user_id"], "email": user["email"]},
    )

    # 例:
    # dynamodb.put_item(
    #   TableName="Users",
    #   Item=...,
    #   ConditionExpression="attribute_not_exists(user_id)"
    # )

    return {
        **user,
        "status": "PENDING_EMAIL_VERIFICATION",
    }


@durable_step
def activate_user(step_context: StepContext, user_id: str) -> dict:
    """
    本番では DynamoDB の status を ACTIVE に更新する。
    """
    step_context.logger.info("Activating user", extra={"user_id": user_id})

    return {
        "user_id": user_id,
        "status": "ACTIVE",
    }


@durable_step
def send_welcome_email(step_context: StepContext, email: str) -> dict:
    """
    本番では SES / SendGrid / Mailgun などで送信する。
    """
    step_context.logger.info("Sending welcome email", extra={"email": email})
    return {"sent": True}


@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
    context.logger.info("Start onboarding")

    user = context.step(
        validate_input(event),
        name="validate-input",
    )

    profile = context.step(
        create_user_profile(user),
        name="create-user-profile",
    )

    def submit_verification_email(
        callback_id: str,
        callback_context: WaitForCallbackContext,
    ) -> None:
        verification_url = (
            f"{CALLBACK_BASE_URL}/verify"
            f"?callbackId={callback_id}"
            f"&userId={profile['user_id']}"
        )

        # 本番では callback_id をログにそのまま出さない方が安全。
        callback_context.logger.info(
            "Sending verification email",
            extra={"email": profile["email"], "user_id": profile["user_id"]},
        )

        # 例: SESでメール送信
        # ses.send_email(
        #   Source="no-reply@example.com",
        #   Destination={"ToAddresses": [profile["email"]]},
        #   Message={
        #     "Subject": {"Data": "Please verify your email"},
        #     "Body": {"Text": {"Data": f"Click: {verification_url}"}},
        #   },
        # )

        print(f"[DEV ONLY] verification_url={verification_url}")

    verification_result = context.wait_for_callback(
        submitter=submit_verification_email,
        name="wait-for-email-verification",
        config=WaitForCallbackConfig(
            timeout=Duration.from_hours(24),
            heartbeat_timeout=Duration.from_minutes(30),
        ),
    )

    if not isinstance(verification_result, dict) or not verification_result.get("verified"):
        return {
            "status": "EMAIL_VERIFICATION_FAILED",
            "user_id": profile["user_id"],
            "verification_result": verification_result,
        }

    activated = context.step(
        activate_user(profile["user_id"]),
        name="activate-user",
    )

    context.step(
        send_welcome_email(profile["email"]),
        name="send-welcome-email",
    )

    return {
        "status": "COMPLETED",
        "user": activated,
    }

Durable Function本体に対して、メール認証リンクをクリックされた時にcallbackを返すLambda
=> 待っているDurable Functionを再開させる係

callback_handler.py
import json
import boto3

lambda_client = boto3.client("lambda")


def handler(event, context):
    params = event.get("queryStringParameters") or {}

    callback_id = params.get("callbackId")
    user_id = params.get("userId")

    if not callback_id:
        return {
            "statusCode": 400,
            "body": "callbackId is required",
        }

    result = {
        "verified": True,
        "user_id": user_id,
    }

    lambda_client.send_durable_execution_callback_success(
        CallbackId=callback_id,
        Result=json.dumps(result).encode("utf-8"),
    )

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "text/html; charset=utf-8"},
        "body": "<h1>Email verified</h1><p>You can close this page.</p>",
    }

動作

Lambda関数の実行後、メールに認証URLが送信されるので、それをクリックするとメール認証が成功する。
※サンプルコードではメール送信の関数がコメントアウトされているので、CWからURLを確認。

ユーザー登録開始
    |
    v
[Durable Function本体]
    |
    | 1. validate-input
    | 2. create-user-profile
    | 3. 認証メール送信
    | 4. wait-for-email-verification で一時停止
    v

ユーザーがメール認証リンクをクリック
    |
    v
[Callback受付Lambda]
    |
    | send_durable_execution_callback_success
    v

[Durable Function本体が再開]
    |
    | 5. activate-user
    | 6. send-welcome-email
    v
オンボーディング完了

正常に認証されると下記画面がブラウザに表示される。
image.png

CWログにも下記のように表示される。
image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?