はじめに
筆者はあるシステムのバッチ処理の開発をする中で、複数のAWS Lambdaを連携させたり、Amazon SQSを組み合わせたりする構成を設計する機会がありました。
そのときに、Lambda → Lambda の呼び出しを「同期」にするべきか「非同期」にするべきかについて考える機会があったので整理してみました。
結論
早速、結論から述べるとLambda → Lambda を直接「同期呼び出し」でつなぐ構成は基本的に避ける。 もしそのようなアーキテクチャになっている場合は、一度設計を見直すことを心掛けています。
なぜLambda同士の同期呼び出し(Sync)は避けるべきなのか?
LambdaからLambdaを同期的に呼び出すと、呼び出し側のLambdaは「相手の処理が終わるまで待機」することになります。これにはいくつかデメリットがあげられます。
1. コストが実質的に二重にかかる
同期呼び出しの間、呼び出し元のLambdaも起動したまま待機しています。
そのため、同期呼び出しでは
- 呼び出し元: 待っている時間も課金対象
- 呼び出し先: 実際の処理時間分が課金対象
という状態になります。
結果として、実質的な実行コストが2倍に膨らんでしまいます。
2. クォータ(同時実行数)を二重に消費する
Lambda にはリージョンごとの同時実行数制限があります。同期呼び出しを行うと、1つのリクエストで2つの Lambda の同時実行枠を消費します。
バッチ処理などで大量のリクエストが発生した場合、同時実行枠を実質的に二重に消費することになり、不要にクォータを占有してしまいます。
3. タイムアウトやリトライ設計が複雑になる
同期呼び出しでは、呼び出し元と呼び出し先のタイムアウト値の関係を慎重に設計する必要があります。
例えば、呼び出し元のタイムアウトが呼び出し先より短いと、処理自体は正常に進んでいても、呼び出し元が先にタイムアウトして失敗扱いになってしまう可能性があります。
また、同期呼び出しの場合は呼び出し元でリトライを実装することを前提とし、呼び出し先の冪等性を考慮したうえで、同じ処理が重複して実行されないよう配慮が必要です。
じゃあどうするのか
単純に「処理を渡して終わり」にできるのであれば、Lambda間は非同期でつなぐのが基本になるでしょう。一方で、呼び出し量が多い場合やリトライ回数・間隔を細かく制御したい場合は、SQSをは挟んで疎結合にする構成がよいと思います。
まとめ
- 非同期呼び出し: 単純な切り離しが可能な場合
- SQS経由: Lambda間の呼び出しデータ量が多く、リトライやエラーハンドリング(DLQ など)をしっかり制御したい場合
「Lambda同士を同期呼び出しで連携させる」という設計は、不要なコスト増やスロットリングの原因などにもつながりやすいため、避けていくのが基本となるでしょう。