[アップデート] Lambda 関数が再帰ループを検出して停止するようになりました

Lambda 関数の再帰ループ検出は、Lambda 関数、SQS、SNS 間の連携のみ対応しています。DynamoDB や S3 にはまだ対応していませんのでご注意ください。
2023.07.19

コーヒーが好きな emi です。

2023/7/13 に、Lambda 関数の再帰ループを検出して停止するアップデートが発表されました。

「recursive loops」は「再帰ループ」のことで、Lambda 関数が無限に呼び出され続けてループしてしまう状態のことです。これを検知して停止してくれるというアップデートになります。

概要

Lambda 関数で処理したデータをどこかに連携する際、その連携操作がトリガーになってまた同じ Lambda 関数が起動してしまう、そして繰り返されて無限に課金されてしまう……このような状態を Lambda の再帰ループと呼びます。設定ミスによってまれに発生し、翌朝 AWS 料金通知を見てびっくりするような金額が課金されていた、という話をよく聞きます。

例えば Lambda 関数が イベントソースマッピング で Source キューからメッセージを取得し、処理したデータを Destination キューに格納するはずが、設定ミスによって Source キューにデータを戻す実装になってしまった場合再帰ループとなり、延々と課金され続けてしまう、といったケースが考えられます。

今回のアップデートでは、Lambda 関数がイベントを SQS または SNS に送信するとき、関数が呼び出された回数を追跡します。
同じトリガーイベントによって引き起こされる一連の Lambda 呼び出しをリクエストチェーンと呼び、同じリクエストチェーン内で関数が 16 回を超えて呼び出される場合、Lambda はそのリクエストチェーン内の次の関数呼び出しを停止し、イベントをデッドレターキューまたは設定済みの障害時の宛先に送信します。
内部的には X-Ray トレースヘッダーを使用しているそうです。
この機能の使用に追加料金はかかりません。
また、以下も通知されます。

  • トラブルシューティングの手順が記載された AWS Health Dashboard 通知
    • Lambda が再帰呼び出しを停止してからこの通知が表示されるまでに最大 3 時間かかる場合があります
  • AWS アカウントの プライマリアカウント連絡先、代替オペレーション連絡先 に再帰ループ電子メールアラート
    • AWS アカウントの関数ごとに 24 時間で最大 1 通のメールを送信します
    • Lambda が再帰呼び出しを停止してからこの電子メールアラートを受信するまでに最大 3 時間かかる場合があります

Lambda 関数の再帰ループ検出は、すべてのアカウントに対してデフォルトで有効になっています。
もし意図的に Lambda 関数で再帰ループを発生させたい場合は、Lambda 関数の再帰ループ検出機能をオフにしたい旨を AWS サポートにお問い合わせください。

注意:DynamoDB や S3 にはまだ対応していません

現在、Lambda 関数、SQS キュー、SNS トピックの間の再帰ループを検出できます。
再帰ループしてしまうよくある例として、S3 イベントをトリガーに Lambda を起動して処理したデータをまた同じ S3 に PUT してしまい、またトリガーとなってループしてしまう…というのがあります。こういったケースにはまだ対応していないのでご注意ください。

サポートされている AWS サービス

繰り返しますが、現在、Lambda 関数、SQS キュー、SNS トピックの間の再帰ループを検出できます。
DynamoDB や S3 などの別の AWS サービスがループの一部を形成している場合、Lambda は現在それを検出して停止することができません。
以下の図は現在 Lambda が検出できるループの例です。

サポートされている AWS のサービスと SDK より図を引用

サポートされている AWS SDK

Lambda が再帰ループを検出するには、Lambda 関数で次のいずれかの SDK バージョン以降を使用する必要があります。

ランタイム 最低限必要な AWS SDK バージョン
Node.js 2.1147.0 (SDK version 2)
3.105.0 (SDK version 3)
Python 1.24.46 (boto3)
1.27.46 (botocore)
Java 8 および Java 11 1.12.200 (SDK version 1)
2.17.135 (SDK version 2)
Java 17 2.20.81
.NET 3.7.293.0
Ruby 3.134.0

サポートリージョン

  • アジアパシフィック (香港、ジャカルタ、大阪、ムンバイ、ソウル、シンガポール、シドニー、東京)
  • アフリカ (ケープタウン)
  • カナダ (中部)
  • ヨーロッパ(フランクフルト、アイルランド、ロンドン、ミラノ、パリ、ストックホルム)
  • 南米 (サンパウロ)
  • 米国東部 (オハイオ、北部バージニア)
  • 米国西部 (オレゴン、北部カリフォルニア)

検証

以下 Lambda 関数の再帰ループ検出についての AWS ブログを参考に、再帰ループする Lambda 関数と SQS キューを作成します。

GitHub のサンプルコードは以下を利用します。

AWS SAM CLI を使って環境を構築します。
私は開発環境として Cloud9 を利用しました。

Cloud9 環境の作成

  • Name:sam-environment
  • Description:sam-environment

Cloud9 環境ができたら、「開く」をクリックします。

画面下部のターミナルでコマンドを入力して操作していきます。

SAM CLI のバージョンアップ

今回 Java 17 を利用するため、SAM CLI のバージョンは SAM CLI v1.82.0 以上が必要です。
作成した Coud9 で利用できる SAM CLI のバージョンを見てみると、1.72.0 でした。

kitani.emi:~/environment $ sam --version
SAM CLI, version 1.72.0
kitani.emi:~/environment $

以下のコマンドを順番に実行し、バージョンアップしました。

  • 最新のインストーラーをダウンロード
    wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip

  • ダウンロードした zip ファイルを Unzip

    unzip aws-sam-cli-linux-x86_64.zip -d sam-installation

  • アップデートを実行

    sudo ./sam-installation/install --update

SAM CLI のバージョンを見てみると、1.91.0 になりました。

kitani.emi:~/environment $ sam --version
SAM CLI, version 1.91.0
kitani.emi:~/environment $

アプリケーションの構築

git リポジトリのクローンを作成します。

git clone https://github.com/aws-samples/aws-lambda-recursion-detection-sample

プロジェクトディレクトリに移動します。

cd aws-lambda-recursion-detection-sample

SAM CLI を使用してアプリケーションを構築します。

sam build --use-container

SAM CLI を使用してリソースを AWS アカウントにデプロイします。

sam deploy --guided
  • 実行結果
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $ sam deploy --guided
    
    Configuring SAM deploy
    ======================
    
            Looking for config file [samconfig.toml] :  Not found
    
            Setting default arguments for 'sam deploy'
            =========================================
            Stack Name [sam-app]: 
            AWS Region [ap-northeast-1]: 
            #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
            Confirm changes before deploy [y/N]: y
            #SAM needs permission to be able to create roles to connect to the resources in your template
            Allow SAM CLI IAM role creation [Y/n]: Y
            #Preserves the state of previously provisioned resources when an operation fails
            Disable rollback [y/N]: N
            Save arguments to configuration file [Y/n]: n
    
            Looking for resources needed for deployment:
            Creating the required resources...
            Successfully created!
    
            Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1w8wlu79sqe51
            A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False
            Uploading to sam-app/7570e3b35726fb62755206ec815b0fc5  9485162 / 9485162  (100.00%)
    
            Deploying with following values
            ===============================
            Stack name                   : sam-app
            Region                       : ap-northeast-1
            Confirm changeset            : True
            Disable rollback             : False
            Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-1w8wlu79sqe51
            Capabilities                 : ["CAPABILITY_IAM"]
            Parameter overrides          : {}
            Signing Profiles             : {}
    
    Initiating deployment
    =====================
    
            Uploading to sam-app/7cfea9f7e810e1fc7390d9bfe61ca699.template  2259 / 2259  (100.00%)
    
    
    Waiting for changeset to be created..
    
    CloudFormation stack changeset
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Operation                                            LogicalResourceId                                    ResourceType                                         Replacement                                        
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    + Add                                                LambdaFunctionMySQSEvent                             AWS::Lambda::EventSourceMapping                      N/A                                                
    + Add                                                LambdaFunctionRole                                   AWS::IAM::Role                                       N/A                                                
    + Add                                                LambdaFunction                                       AWS::Lambda::Function                                N/A                                                
    + Add                                                SqsDLQ                                               AWS::SQS::Queue                                      N/A                                                
    + Add                                                SqsSourceQueue                                       AWS::SQS::Queue                                      N/A                                                
    + Add                                                SqsTargetQueue                                       AWS::SQS::Queue                                      N/A                                                
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:123456789012:changeSet/samcli-deploy1689767157/3f2c76ac-625a-41bd-9767-ee41c51fba58
    
    
    Previewing CloudFormation changeset before deployment
    ======================================================
    Deploy this changeset? [y/N]: y
    
    2023-07-19 11:49:29 - Waiting for stack create/update to complete
    
    CloudFormation events from stack operations (refresh every 5.0 seconds)
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    ResourceStatus                                       ResourceType                                         LogicalResourceId                                    ResourceStatusReason                               
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    CREATE_IN_PROGRESS                                   AWS::SQS::Queue                                      SqsDLQ                                               -                                                  
    CREATE_IN_PROGRESS                                   AWS::SQS::Queue                                      SqsTargetQueue                                       -                                                  
    CREATE_IN_PROGRESS                                   AWS::SQS::Queue                                      SqsDLQ                                               Resource creation Initiated                        
    CREATE_IN_PROGRESS                                   AWS::SQS::Queue                                      SqsTargetQueue                                       Resource creation Initiated                        
    CREATE_COMPLETE                                      AWS::SQS::Queue                                      SqsDLQ                                               -                                                  
    CREATE_COMPLETE                                      AWS::SQS::Queue                                      SqsTargetQueue                                       -                                                  
    CREATE_IN_PROGRESS                                   AWS::SQS::Queue                                      SqsSourceQueue                                       -                                                  
    CREATE_IN_PROGRESS                                   AWS::SQS::Queue                                      SqsSourceQueue                                       Resource creation Initiated                        
    CREATE_COMPLETE                                      AWS::SQS::Queue                                      SqsSourceQueue                                       -                                                  
    CREATE_IN_PROGRESS                                   AWS::IAM::Role                                       LambdaFunctionRole                                   -                                                  
    CREATE_IN_PROGRESS                                   AWS::IAM::Role                                       LambdaFunctionRole                                   Resource creation Initiated                        
    CREATE_COMPLETE                                      AWS::IAM::Role                                       LambdaFunctionRole                                   -                                                  
    CREATE_IN_PROGRESS                                   AWS::Lambda::Function                                LambdaFunction                                       -                                                  
    CREATE_IN_PROGRESS                                   AWS::Lambda::Function                                LambdaFunction                                       Resource creation Initiated                        
    CREATE_COMPLETE                                      AWS::Lambda::Function                                LambdaFunction                                       -                                                  
    CREATE_IN_PROGRESS                                   AWS::Lambda::EventSourceMapping                      LambdaFunctionMySQSEvent                             -                                                  
    CREATE_IN_PROGRESS                                   AWS::Lambda::EventSourceMapping                      LambdaFunctionMySQSEvent                             Resource creation Initiated                        
    CREATE_COMPLETE                                      AWS::Lambda::EventSourceMapping                      LambdaFunctionMySQSEvent                             -                                                  
    CREATE_COMPLETE                                      AWS::CloudFormation::Stack                           sam-app                                              -                                                  
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
    CloudFormation outputs from deployed stack
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Outputs                                                                                                                                                                                                         
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Key                 SourceSQSqueueURL                                                                                                                                                                           
    Description         Source SQS queue URL                                                                                                                                                                        
    Value               https://sqs.ap-northeast-1.amazonaws.com/123456789012/sam-app-SqsSourceQueue-dTq6zLUKqj21                                                                                                   
    
    Key                 TargetSQSqueueName                                                                                                                                                                          
    Description         Target SQS queue                                                                                                                                                                            
    Value               sam-app-SqsTargetQueue-PRZBseIbw2Al                                                                                                                                                         
    
    Key                 LambdaFunction                                                                                                                                                                              
    Description         Lambda Function ARN                                                                                                                                                                         
    Value               arn:aws:lambda:ap-northeast-1:123456789012:function:sam-app-LambdaFunction-h8mvRekt9xxs                                                                                                     
    
    Key                 SourceSQSqueueName                                                                                                                                                                          
    Description         Source SQS queue                                                                                                                                                                            
    Value               sam-app-SqsSourceQueue-dTq6zLUKqj21                                                                                                                                                         
    
    Key                 TargetSQSqueueURL                                                                                                                                                                           
    Description         Target SQS queue URL                                                                                                                                                                        
    Value               https://sqs.ap-northeast-1.amazonaws.com/123456789012/sam-app-SqsTargetQueue-PRZBseIbw2Al                                                                                                   
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
    
    Successfully created/updated stack - sam-app in ap-northeast-1
    
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $

再帰ループさせる

デプロイできたら、Source SQS キューの URL をメモしておきます。

# Source SQS キューの URL をメモしておく
https://sqs.ap-northeast-1.amazonaws.com/123456789012/sam-app-SqsSourceQueue-dTq6zLUKqj21

それでは SQS キューにメッセージを送り、Lambda 関数を再帰ループさせます。

aws sqs send-message --queue-url (Source SQS キューの URL) --message-body '{"orderId":"111","productName":"Bolt","orderStatus":"Submitted"}'
  • 実行結果
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $ aws sqs send-message --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/sam-app-SqsSourceQueue-dTq6zLUKqj21 --message-body '{"orderId":"111","productName":"Bolt","orderStatus":"Submitted"}'
    {
        "MD5OfMessageBody": "669fad69f0c9db1819b6f35c879d9265", 
        "MessageId": "878310fa-95e4-47b1-9bf8-ea3636fe8a96"
    }
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $

結果を見る

Lambda 関数コンソールから該当の Lambda 関数を選択し、モニタリングタブでメトリクスを確認します。

Invocations (呼び出し)の Count が 16 回で止まっています。

この時私が間違えて 2 回 SQS キューにメッセージを送るコマンドを実行してしまったので(一回目はコマンドを間違えた)線グラフになっていますが、一発でうまくいくと点になるはずです。

Recursive invocations dropped のメトリクスを確認します。これは再帰的な関数呼び出しが棄却(ドロップ)されたことを表すメトリクスです。

これも私が間違えたためにグラフ上は 2 回になっています。
しかし、ちゃんと再帰ループは破棄されたようです。

通知

翌朝 Health Dashboard を確認すると、通知が来ていました。

メールでも通知が来ていました。

【Action Required】Recursive loops Detected and Stopped for Lambda Functions in your AWS Account:123456789012

Hello,

AWS Lambda has detected that one or more Lambda functions in your AWS Account: 123456789012 are being invoked in a recursive loop with other AWS resources. To prevent unexpected charges from being billed to your AWS account, Lambda has stopped the recursive invocations for the following functions:

arn:aws:lambda:ap-northeast-1:123456789012:function:sam-app-LambdaFunction-h8mvRekt9xxs at time Wed Jul 19 12:15:00 UTC 2023


What do I need to do?

Please review your functions and their trigger configurations to identify any unintentional recursive patterns. To prevent further recursive invocations of your function, follow the instructions in the AWS Lambda Developer Guide [1].

If your design intentionally uses recursive patterns, please contact AWS Support [2] to turn off recursive loop prevention for Lambda.

[1] https://docs.aws.amazon.com/lambda/latest/dg/invocation-recursion.html
[2] https://aws.amazon.com/support

Sincerely,
Amazon Web Services

環境の削除

SAM で作成された以下 3 つの CloudFormation Stack を順番に削除していきます。

  • sam-app
  • aws-sam-cli-managed-default
  • aws-cloud9-sam-environment-xxxxxxxxxx

sam-app の削除

sam delete
  • 実行結果
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $ sam delete
            Enter stack name you want to delete: sam-app
            Are you sure you want to delete the stack sam-app in the region None ? [y/N]: y
            Are you sure you want to delete the folder sam-app in S3 which contains the artifacts? [y/N]: y
            - Deleting S3 object with key sam-app/7570e3b35726fb62755206ec815b0fc5                                                                                                                                                                      
            - Deleting S3 object with key sam-app/7cfea9f7e810e1fc7390d9bfe61ca699.template                                                                                                                                                             
            - Deleting Cloudformation stack sam-app
    
    Deleted successfully
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $

aws-sam-cli-managed-default の削除

SAM はバージョニングが有効な S3 バケットが作成されオブジェクトが存在しているため、先にバケットを空にしておいてください。

sam delete
  • 実行結果
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $ sam delete
            Enter stack name you want to delete: aws-sam-cli-managed-default
            Are you sure you want to delete the stack aws-sam-cli-managed-default in the region None ? [y/N]: y
            - Deleting Cloudformation stack aws-sam-cli-managed-default
    
    Warning: Cannot resolve s3 bucket information from command options , local config file or cloudformation template. Please use --s3-bucket next time and delete s3 files manually if required.
    
    Deleted successfully
    kitani.emi:~/environment/aws-lambda-recursion-detection-sample (main) $

aws-cloud9-sam-environment-xxxxxxxxxx の削除

Cloud9 の IDE 画面を閉じ、環境を削除しておきます。

CloudFormation Stack の画面で aws-cloud9-sam-environment-xxxxxxxxxx を削除します。

終わりに

再帰ループは短時間で課金が跳ね上がるため、非常に怖いものです。今回のアップデートは Lambda 使いの皆様お待ちかねだったのではないでしょうか。
更に S3 や DynamoDB などにも対応していくと良いですね。

参考