👏

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の再接続による再送」が起こったと判断しています。

もし似たような構成をお使いの方で、同様の現象に遭遇したことがあれば、コメント等で教えていただければ幸いです。

DELTAテックブログ

Discussion