Lambda 関数の同時実行数を1にしても冪等性の担保から逃れることは出来ない

noid11
7 min readAug 9, 2019

--

TL; DR

1つのイベントから2回 Lambda 関数が起動する場合、1つの Lambda 関数が起動して終了した後に、もう1つの Lambda 関数が起動してくる可能性があるため。

冪等性の担保から逃れることは出来ない!

そもそも AWS Lambda の同時実行数とは

AWS Lambda のスケーリング単位は同時実行数です (詳細については「スケーリング動作について」を参照)。ただし、すべてのシナリオで無期限にスケーリングすることは望ましくありません。たとえば、同時実行数をコスト上の理由や、イベントのバッチの処理にかかる時間や、ダウンストリームリソースに合わせて制御できます。そのために、Lambda にはアカウントレベルと機能レベルの両方で同時実行数の制限を制御する機能が用意されています。

AWS Lambda のスケーリングは AWS が管理してくれるので楽ちんですが、無限にスケールできるわけではありません。クラウド破産怖いし、Lambda 関数の連携先がスケールについてこれずシステム止まる!みたいな可能性もある訳ですね。

で、Lambda 関数が同時に実行できる数に制限がありますと。

AWS Lambda には、2種類の同時実行数制限があって、 AWS アカウントレベルか、 Lambda 関数レベルで制限が設定できます。

Lambda 関数は稀に2回起動する!関数レベルの同時実行制限を1に設定すれば2回起動を防げるのでは!?

そんなふうに考えた時期が私にもありました。残念ながら防げません。

・冪等性はお客様のコードで確保する必要がある

- AWS Lambda で保証しているのは最低1回実行することであり1回しか実行しないことではない

- 同一イベントで同一 Lambda ファンクションが2回起動されることがまれに発生する

- DynamoDB を利用するなどして冪等性を担保する実装を行うこと

p59 より。

For asynchronous invocation, Lambda adds events to a queue before sending them to your function. If your function does not have enough capacity to keep up with the queue, events may be lost. Occasionally, your function may receive the same event multiple times, even if no error occurs. To retain events that were not processed, configure your function with a dead-letter queue.

API Reference より。

Lambda 関数を非同期呼び出しする場合、エラーが発生しなくても Lambda 関数が同じイベントを複数回受け取ることがある的な事が書いてあります。

そこで、冪等性の担保が面倒なので Lambda 関数の同時実行数を1にすれば、まれな2回起動を防げるのでは!?!?という事ですね。

これは同じイベントを2回受信した際に、それぞれの Lambda 関数が同時に起動するという暗黙的な期待をしています。

同じイベントを2回受信した場合、1つ目の Lambda 関数が終了してから2つ目の Lambda 関数が起動する可能性があり、その場合は同時実行ではないので普通に動きます。Lambda 関数レベルの同時実行数制限では防げません。

ツラい障害を起こしてしまう前に、冪等性の担保と向き合いましょう・・・

じゃあ冪等性をどうやって担保すれば良いのか

先のスライドには DynamoDB などを利用しようという事が書いてありましたね。同じような情報が公式ナレッジにあります。

関数ロジックの例

この Lambda 関数ロジックの例は、ほとんどのケースに適用します。

1. 入力イベントの固有属性の値を抽出します。(たとえば、トランザクションまたは購入 ID など。)

2. 属性値がコントロールデータベース (Amazon DynamoDB テーブルなど) に存在するかどうかを確認します。
注: アーキテクチャに AWS サービスを追加すると、追加費用が発生する場合があります。詳細については、「Amazon DynamoDB 料金」および「AWS 料金」をご参照ください。

3. 一意の値が存在する場合 (重複イベントを示す)、実行を正常に終了します (つまりエラーをスローすることがない)。一意の値が存在しない場合は、通常の実行を継続します。

4. 関数の動作が正常に終了したら、コントロールデータベースにレコードが含まれます。

5. 実行を終了します。

処理を特定する ID を発行して、処理前に DynamoDB にそのID が存在するか確認して、存在しない場合には処理を実行し、処理終了時に ID を DynamoDB に書き込んでおく・・・という感じですね。

DynamoDB を使わない構成でも問題無いと思いますが、データ容量・パフォーマンス・料金とか考えると DynamoDB との相性が非常に良いという感じですね。

ついでに Lambda 関数設計のベストプラクティスも記載されてます。参考になる。

ベストプラクティス

等冪性を考慮して Lambda 関数を設計する際には、次のベストプラクティスを念頭に行ってください。

・アプリケーションを開発する前に、等冪性の実装を計画する。

・重複イベントを処理するときは、Lambda コードが正しく終了することを確認する。エラーをスローすると、Lambda または関数を呼び出す別のサービスによって、何度も再試行が行われる可能性があります。

・必要に応じて Lambda 関数のタイムアウト設定を変更し、実行が完全に、正しく処理されるようにします。別のサービスを使ってデータを保持し、重複するイベントを制御するには、HTTPS エンドポイントへの API 呼び出しが必要になる場合があります。この場合、デフォルトの 3 秒よりも長い時間がかかることがあります。

・できる限り関数をテストして最適化し、実際のシナリオと要求率をシミュレートします。これは、等羃性ロジックがタイムアウト、過度のレイテンシー、ボトルネックなどの副作用を引き起こすのを防ぐために重要です。

・エスカレーションが容易でスループットが高い DynamoDB などのサービスを使用して、セッションデータを保存します。

以上、 Lambda 関数の同時実行数を1にしても冪等性の担保からは逃れられないという話でした。

--

--