SQSのVisibilityTimeout、190秒必要なのに180秒で運用してた

  • AWS
  • SQS
  • Lambda

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

Lambda + SQS + DLQの構成を見直してて、SQS側のVisibilityTimeoutが足りてないことに気づいた。

構成

SQS(1) → Lambda
SQS(1) --redrive--> SQS(2) (DLQ)
  • Lambda timeout: 30秒
  • SQS(1) VisibilityTimeout: 180秒
  • SQS(1) MaximumBatchingWindowInSeconds: 10秒
  • DLQ(SQS(2)) VisibilityTimeout: 未設定

AWSの推奨式

AWS公式ドキュメント(Lambdaのevent source mapping設定ページ)に、こう書いてある。

キューのVisibilityTimeoutは、関数タイムアウトの少なくとも6倍にする。バッチウィンドウを使ってるなら、そこにMaximumBatchingWindowInSecondsを足す。

式にするとこう。

VisibilityTimeout >= 6 × FunctionTimeout + MaximumBatchingWindowInSeconds

理由はシンプルで、Lambdaがスロットリングされてリトライする分の時間を確保しておかないと、処理中のメッセージが再配信されて二重処理になる。バッチウィンドウがある場合はその待ち時間もタイムアウトの計算に乗せないと同じことが起きる。

うちの数字で計算する

6 × 30 + 10 = 190

190秒必要なところ、SQS(1)は180秒で運用してた。10秒足りてない。バッチウィンドウ分をそのまま忘れてた形。

DLQ側はどう決めるか

DLQのVisibilityTimeoutは、Lambdaの6倍ルールとは別軸で考える必要がある。基準になるのは「DLQを実際に処理するのは誰か」。

  • 同じような設定のLambdaでDLQを処理するなら、同じ式(6倍+バッチウィンドウ)をそのまま適用する
  • 人が確認して手動で再投入するような運用なら、6倍ルールは関係ない。人が1件確認・対応するのにかかる最長時間+安全マージンで決める
  • 別のバッチ処理や別Lambdaで拾うなら、そのコンシューマのタイムアウトとバッチウィンドウで計算し直す

DLQは「失敗したメッセージの置き場」でしかなくて、そこから先の処理方式次第で必要なVisibilityTimeoutが変わる、という当たり前と言えば当たり前の話だった。

ついでに気づいたこと

ソースキューのリドライブポリシーのmaxReceiveCountは最低5に設定することもAWSの推奨に入っている。今回はDLQへの再配信条件そのものは見直してなかったので、次はここも確認する。

まとめの数字

  • SQS(1): 180秒 → 190秒以上に変更が必要
  • DLQ(SQS(2)): 処理方式が決まってから、同じ式か別基準で計算する