[AWS]知っておいたほうがいいLambda関数の呼び出しタイプとリトライ方式まとめ
社内などから指摘があり修正しました。「Lambda関数の呼び出しタイプにてRequestResponse(同期)の場合は冪等性は考慮不要」という記述をしましたが、誤った記載となっており申し訳ありませんでした。冪等性の考慮についてはアプリケーション依存によるところが大きく、RequestResponse(同期)でもアプリやSDKにてリトライを実施し冪等性を考慮する場合があります。記事としては、冪等性という視点ではなくリトライという視点で修正しました。
コンニチハ、千葉です。社内から色々アドバイスをもらって完成した元気玉方式のエントリーです!
はじめに
Lambda関数は実行トリガーとしてAPI GatewayやS3、CloudWatch Eventsなどを指定することができます。Lambda関数には呼び出しタイプ(同期、非同期、ストリーム)があるのですが実行トリガーによって呼び出しタイプが異なり、それによってAWS側でリトライが実行されるのか、ユーザー側でリトライを実装する必要があるかが変わってきます。
私が誤ってたのですが「リトライ処理がないRequestResponse(同期)タイプでは冪等性の考慮は不要」と書いたのですが、「RequestResponse(同期)の場合は、Lambdaでリトライを実施しないため、アプリにてリトライを実装しかつ冪等性も考慮する必要がある」ということです。
インフラレベルの再実行と、アプリケーションレベルの再実行は意味が違うので、同じ文脈のなかで書くと書き手も読み手も混乱しがちです。場面を分けて書いたほうが良いかと。
リトライと冪等性のデザインパターン - Blog by Sadayuki Furuhashi
続・リトライと冪等性のデザインパターン - リトライはいつ成功するか - Blog by Sadayuki Furuhashi
続々・リトライと冪等性のデザインパターン - あらゆる操作を冪等にする方法 - Blog by Sadayuki Furuhashi
結局、冪等性というのは処理を何度実行しても同じデータ(処理結果)になるという話であって、アーキテクチャとかミドルウェアとかアプリケーションとかそういうのは手段にすぎないです。
リンク先のブログ、めっちゃ勉強になりました。 ということで、Lambdaの話しをしたかったので、本エントリーではアプリの冪等性についてはではなく、Lambdaのリトライという視点で書きたいと思います。
Lambda関数の呼び出しタイプ
前提知識としてLambda関数の呼び出しタイプがあり、この呼出しタイプによりリトライ方式が変わってきます。 Lambda関数を実行(Invoke)するときに InvocationType を指定することができます。InvocationTypeには、Event、RequestResponse、DryRunを指定することができ、デフォルトはRequestResponseとなります。
Event
InvocationTypeにEventを指定すると非同期実行になります。非同期呼び出しでは、Lambda関数が直接実行されるのではなく、キューイングされたのち実行されます。 キューイングされるためEventタイプでInvoke APIを実行すると、API実行後にすぐにレスポンスが返りステータスコードは202となります。(例えば関数が1分かかっても、処理完了を待たずに応答が返ります)
Lambda関数自体は、キューから実行されます。呼び出しに失敗した場合は自動的に2回リトライが実施されます。(初回起動を含めると最大3回実行)
実行が3回失敗したあと、イベントは破棄されますが、デッドレターキュー (DLQ) を指定した場合はSQSやSNSに送信することができます。例えば、失敗時にメールで送信するや、SQSキューに入れることで1日後にバッチで実行など対応できるようになります。(【新機能】AWS LambdaがDead Letter Queueをサポートしました #reinvent)
RequestResponse
RequestResponseを指定すると同期実行になります。キューイングされず実行されます。AWS側でのリトライ処理はなく実行は1回となります。RequestResponseタイプでInvoke APIを実行すると、関数が実行され、処理完了後にレスポンスが返ります。正常時のステータスコードは200となります。
リトライ処理については suzuki.ryoからの指摘
同期であっても、エラー時のリトライは、呼び出し元(SDK)とかで リトライ走った場合の考慮は必要な筈。
とのことでドキュメントを確認しました。ステータスコード429が返ってきた場合は、アプリ側で再試行の処理が必要になります。ただしAWS SDKを利用する場合はリトライが実装されています。※SDKでのリトライはSDKの実装状況によるので、利用しているSDKの仕様をご確認ください。
Lambda 関数を直接 (AWS SDK 経由または API Gateway 経由で)、呼び出した場合、クライアントではエラーが表示され再試行を選択できます。API ゲートウェイ を通じて Lambda を呼び出す場合は、Lambda の応答エラーを API ゲートウェイ のエラーコードに必ずマップする必要があります。
DryRun
今回のリトライの話からそれますが、InvocationTypeにはDryRunを指定できます。 DryRunを指定すると関数を実行せずに必要な権限がついているかを確認できます。これは、クロスアカウントでの実行時の権限確認などできます。
ストリームベースのイベントソース
DynamoDB、Amazon Kinesis StreamsとLambdaを連携する場合はストリームベースとなります。ストリームイベントの場合は、Lambdaサービスがストリームをポーリングし、Lambda関数を呼び出します。ポーリングのタイミングはKinesis Streamsは1秒間に1回、DynamoDBは1秒間に4回となります。(Kinesis で AWS Lambda を使用する、Using AWS Lambda with Amazon DynamoDB※英語で確認)
またLambda関数起動時のレコード取得数を設定可能です。Lambda関数イベントソース作成時にBatchSizeを指定できるのですが、これは1回のLambda関数起動時のレコード取得数を指定できます。例えばBatchSizeを100にすること、1回のLambda関数の起動でKinesisから最大100レコード取得できるようになります。(AWS ストリームベースのサービス向けのイベントソースマッピング)
Lambda関数が失敗した場合は、データの有効期限が切れるまでエラーが発生した関数をリトライします。リトライ中は新しいレコードの読み込みされずに、エラーになったレコードがリトライされ続けます。これは、ストリームを順番に処理するための仕組みになります。また、ストリームイベントではLambdaのDLQが非対応なため注意が必要です。(AWS Lambdaデッドレターキューによるロバストなサーバーレスアプリケーションの設計)
データの有効期限については、Kinesis Streamsはデフォルト24時間、最大7日。これは、Kinesis Streams のデータ有効期限の設定に依存します。DynamoDBは24時間になります。
参考:コンシューマーの再試行、DynamoDB ストリーム のデータ保持期限
イベントソースまとめ
RequestResponse(同期)で実行されるイベントソース
Lambda関数の実行トリガーは同期型になります。
- AWS CLIや各SDKにてLambda関数をInvokeした場合(デフォルト動作)
- API Gateway(デフォルト動作)※参考AWS Lambda 関数の API GatewayAPI を作成する
- Cognito
- Alexa
- Lex
Event(非同期)で実行されるイベントソース
Lambda関数の実行トリガーは非同期型になります。
- AWS CLIや各SDKにてInvocationType=EventでInvokeした場合
- API Gateway(ヘッダでEvent指定時)※参考AWS Lambda 関数の API GatewayAPI を作成する
- AWS IoT
- CloudWatch Events(スケージュル含む全てのトリガー)
- CloudWatch Logs
- CodeCommit
- S3
- SNS
- SES
- KinesisFirehose
- CloudFormation(Lambdaを利用したカスタムリソース)
- AWS Config
ストリーム型で実行されるイベントソース
Lambda関数の実行トリガーはストリーム型になります。
- DynamoDB(ストリーム型)[^ストリーム]
- Kinesis Stream(ストリーム型)[^ストリーム]
最後に
Lambda関数の起動方式が複数あるため、まとめました。結構ディープだったので時間がかかりましたがクリアになりました。実行方式がありますが、非同期・ストリームについてはAWS側でリトライ処理が実装されていることで空振りせずLambdaを実行することができます。またリトライ処理にて複数回実行される可能性もあるため、冪等性が必要な場合はアプリケーションで考慮しましょう。