ようへいの日々精進XP

よかろうもん

サーバーレス環境 (主に AWS Lambda) における緩い感じの処理完了待ちを実装する考察

tl;dr

おはようございます. どうも, 処理待ち隆史 (反町隆史) です.

Lambda を使った RDS スナップショットのコピー自動化を実装するにあたって, コピーの完了を待つ (処理完了待ち) という処理をどのように実装するか考えてみた際のメモです.

検討

2 案

処理完了待ちを実装するにあたり, 以下のような手法を検討しました.

  1. Lambda のタイムアウトを利用する
  2. 状態を保持する 何か を用意して, 別の Lambda で処理のプロセスを監視する

Lambda のタイムアウト

まず, Lambda のタイムアウトを利用する手法です.

タイムアウト (15 分) 以内に確実に処理が終わることが保証されているのであれば, この手法が一番手軽なのかもしれません. ところが, 実際にスナップショットコピーを動かしてみたところ 15 分以上掛かってしまいコピー完了を確認することが出来ない状況になりました. (コピー自体は完了していることをマネジメントコンソールで確認しています)

まさにこんな気持ちになりました.

別の Lambda で処理のプロセスを監視する

以下のような仕組みを考えました.

f:id:inokara:20190216194044p:plain

(1) 〜 (3) までの処理は, 今回は RDS のスナップショットコピーという処理になっていますが, 似たような処理完了を待つような処理を当てはめることが出来ると考えています.

コピー用の Lambda 関数はスナップショット作成完了の通知を受けると, スナップショットをコピーする処理がを実行します. 正常にコピー処理が開始されると, SQS キューにに処理を開始した旨のメッセージをキューイング (4) します. メッセージの内容は以下のような JSON となります.

{"snapshot": "コピーするスナップショット名", "count": "処理待ち用 Lambda 関数が呼ばれた回数"}

JSON は以下のような内容となります.

  • snapshot キーの値にはコピーするスナップショット名が入ります, ここには処理完了を監視する対象物が定義されます
  • num キーの値には処理待ち用 Lambda 関数が何回呼ばれたかが定義され, これは関数が呼ばれる度にインクリメントされます

ポイントは num キーとなります. これは, 処理が永遠に完了しない場合, しきい値を設けてサーキットブレーカー的に待機処理 (処理が完了することを監視する処理) を終了する為に利用します.

キューイング以降, 処理待ち用の Lambda 関数は SQS キューのメッセージを受け取ると, メッセージを削除後, スナップショットのコピーが完了しているかどうかをチェックします. スナップショットのコピーが完了していたら完了した旨を通知して Lambda 関数は終了します. コピーが完了していなければ, 改めて SQS キューにメッセージを放り込みます.

処理待ち用の Lambda 関数はメッセージを利用して SQS キューとピンポンしている状態になりますが, 永遠にピンポンするわけにはいかないので, 処理待ち用 Lambda 関数が呼ばれた回数を記録しておいて, しきい値を超えたら待機処理を終了するような流れになります.

サンプル

サンプルとして...

実際に雰囲気を伝えたくて, 以下のような処理の流れを考えてみました.

f:id:inokara:20190216222028p:plain

S3 バケットにオブジェクトが放り込まれたら Lambda 関数が起動するやつです. そして, 放り込まれたオブジェクトが削除されるまで待機するという, 場合によっては, 永遠に待ち続けるような処理です.

コード

雑な感じで.

github.com

俺たちの Serverless Framework を利用します.

bundle exec rake deploy

でデプロイします. Slack に通知したい場合には, Incoming Webhook の URL を KMS で暗号化した内容を SLACK_ENCRYPTED_URL に記載します.

雰囲気

上記のコードの実行例です.

https://github.com/inokappa/serverless-waiter/blob/master/docs/images/2019021601.gif?raw=true

test.json ファイルを S3 にアップロードすると, PUT イベントをトリガーにして Lambda 関数が起動して SQS キューにメッセージが放り込まれます. メッセージは以下のような内容となります.

{"bucket":"serverless-waiting","key":"test.json","count":3}

count3 になっているので, 3 回目の削除待ち用 Lambda 関数が呼ばれていることになります. この実装例では, 10 回削除待ち用 Lambda 関数が呼ばれると削除待ちを終了するようになっています.

/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 START RequestId: f983604b-281a-5f35-93cf-34a90b14cb80 Version: $LATEST
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Deleted queue message.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] 10 回目の待機です.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Lambda 関数内でオブジェクトが削除されるまで待機します.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Lambda 関数内でオブジェクトが削除されるまで待機します.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Lambda 関数内でオブジェクトが削除されるまで待機します.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Lambda 関数内でオブジェクトが削除されるまで待機します.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Lambda 関数内でオブジェクトが削除されるまで待機します.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Lambda 関数内でオブジェクトが削除されるまで待機します.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 [INFO] Lambda 関数内でオブジェクトが削除されるまで待機します.
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 Error raised from handler method
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52 {
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52   "errorMessage": "[ERROR] 待機制限 (10 回) を超えたので, 待機を終了します.",
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52   "errorType": "Function<RuntimeError>",
/aws/lambda/serverless-waiter-dev-waiter 2019/02/16/[$LATEST]9855e4f958a04501abaa541cd3823e52   "stackTrace": [

尚, 削除待ち用 Lambda 関数内でも指定した回数待機する処理になっています.

以上

緩い感じで

処理完了待ちを実装してみました. 実際に運用していますが, 今のところ処理待ちが滞っている等のトラブルはありません. また, 実装例も何度か試していますが意図したような挙動を確認しています. ただ, Lambda と SQS 間における例外処理等が不十分な状態が懸念され, 完璧な処理完了待ちになっていない可能性がありますので, あくまでも緩い感じの実装になっていることをお許し下さい.

コスト?

  • Lambda 関数は月 1,000,000 件のリクエストは無料枠となり, よっぽどおかしな関数でなければ無料枠内でおさまると考えています
  • SQS は 100 万リクエストまでは無料枠の範囲内ですので, Lambda と SQS 間でおかしなピンポンがなければ無料枠の範囲でおさまると考えています

ということで

良い処理完了待ちライフを.