👋

Amazon SQSでメッセージが2重に処理される

2023/07/05に公開

症状

RailsのActive JobのアダプターとしてAmazon SQSを使用しているのですが、あるジョブを開始してから30秒後に同じジョブが再度実行されるという問題が起きました。

原因

SQSの設定の「可視性タイムアウト」の設定値が30秒だったことが原因でした。

SQSではコンシューマーがキューからメッセージを取得しても、障害を考慮してメッセージは自動では削除されません。
メッセージを削除するにはDeleteMessageを呼び出す必要があります。
DeleteMessageが呼ばれずに「可視性タイムアウト」に設定した時間がすぎると、他のコンシューマーがキューから同じメッセージを取得して処理を開始します。

今回はActive Jobが「可視性タイムアウト」の時間内に完了せず、aws-sdk-rails側でDeleteMessageが呼ばれなかったと思われます。

対処方法

以下の対処方法が考えられます。

1. 「可視性タイムアウト」の値を伸ばす

「可視性タイムアウト」の値をメッセージの最大処理時間を考慮した十分な長さに変更します。
Terraformを使用している場合は、aws_sqs_queueリソースのvisibility_timeout_secondsを変更します。

2. ChangeMessageVisibilityを呼び出す

必要に応じてChangeMessageVisibilityを呼び出して新しいタイムアウトを設定します。

処理時間が予想できない場合は死活監視の仕組みを用意し、「可視性タイムアウト」が経過する前にChangeMessageVisibilityを呼び出して新しいタイムアウトを設定し直すことができます。

aws-sdk-railsを使用してActive JobのアダプターとしてAmazon SQSを使用している場合は、以下のコードで変更できると思います(試していません)。

Aws::Rails::SqsActiveJob.config.client.change_message_visibility({
  queue_url: "String",
  receipt_handle: "String",
  visibility_timeout: 30,
})

詳細についてはこちらを参照してください。

参考資料

Amazon SQS可視性タイムアウト

Discussion