DynamoDB Streamでイベントが再送されてしまった件
株式会社DELTAの三浦です!
今回は担当したスマートクリニックシステムで起きたDynamoDB Streamの怪をまとめます。
こんな人向け
DynamoDB Streamを使っている方
何に困ったか
担当したスマートクリニックシステムは、クリニックの診療予約から、予約のリマインドをLINEに配信します。
ある日、ユーザーから「キャンセルしたはずの予約のリマインドが届く」という問い合わせがありました。
ログを確認すると、2件の予約がキャンセルされた痕跡がありました。
- 1つ目の予約:4/1に予約、その後キャンセル。4/2に再予約・1秒後にキャンセル、1時間空けてキャンセル・1秒後に予約確定
- 2つ目の予約:4/1に予約、4/2に再予約・10分後キャンセル、1時間空けてキャンセル
システム上、予約確定後に同じ日時で再確定させることはできません。
また、ユーザー側から1秒間隔で予約・キャンセルを繰り返すことはできないはずです。
1つ目の予約の最後には、予約より先にキャンセルされているのも不可解です。
システムの流れ
今回の処理の流れとしては以下の通りです。
予約が確定
→DynamoDBに予約データを挿入
→DynamoDB Streamが発火
→SNSへ通知が飛ぶ
→SQSへキューが飛ぶ
→DynamoDB Streamイベントによって各Lambdaが発火
先に結論
原因は、DynamoDB StreamとLambdaとのEventSourceMappingが再作成されたことで、すでに処理済みのDynamoDBイベントが再び発行され、Lambda関数が発火したことでした。
原因となった事象
今回問い合わせが来たのは7/16でしたが、予約自体は4/1に作成されたものでした。実はこの翌日、別の機能追加のためにデプロイを行っており、その際にDynamoDB Stream周辺でエラーが発生していました。
事象が発生したのは、CloudFormationでインフラを再デプロイした直後でした。その際、以下のようなエラーが出ていたようです。
Resource handler returned message: "The event source arn (...) and function ("xxx-prod-onUpdateYYYYY") provided mapping already exists."
このエラーは、「指定されたStream ARNとのEventSourceMapping(接続設定)はすでに存在しているため、新たに作成できない」という内容を表します。
なぜ過去のイベントが再送されたのか?
AWS Lambdaの仕様として以下のようなものがあります。
- EventSourceMapping を再作成した場合(たとえ同じ Stream ARN でも)、
- Lambda 側は「このStreamと初めて接続された」と認識し、
- DynamoDB Stream が保持している最大24時間分の過去イベントを再送する
このため、CloudFormationなどの再デプロイ時に、EventSourceMappingの一時削除→再作成が起きた可能性が高く、これが今回の再送の直接的なトリガーだったと推測しています。
まとめ
DynamoDB Stream を使った構成では、「一度処理したイベントが再送される」可能性を考慮して設計する必要があります。
特に、
- Streamが削除されていなくても
- EventSourceMappingの再作成だけでも
- Lambdaから見ると“初めて接続されたStream”扱いになる
という点は、見落としやすい仕様かと思います。
今回は確実な原因の証拠は残っていなかったものの、構成と挙動からの推定により「EventSourceMappingの再接続による再送」が起こったと判断しています。
もし似たような構成をお使いの方で、同様の現象に遭遇したことがあれば、コメント等で教えていただければ幸いです。
Discussion