【ITニュース解説】Duplicate Reminder Processing: Prefer Idempotency Beyond 5-Minute Queue Deduplication
2026年10月17日に「Dev.to」が公開したITニュース「Duplicate Reminder Processing: Prefer Idempotency Beyond 5-Minute Queue Deduplication」について初心者にもわかりやすく解説しています。
ITニュース概要
通知がメッセージキューで重複配信されるのを防ぐには、キューの5分間重複排除機能に頼らず、アプリで「べき等性」を実装する。通知ごとに一意のIDを永続ストアに記録し、送信前にIDを確認。処理済みなら再送せず、完了後のみACKすることで、システム全体の重複を防ぐ。
ITニュース解説
システム開発において、ユーザーにメールやSMSで通知を送る機能は非常に一般的だが、この通知が誤って複数回届いてしまうと、ユーザーは混乱し、システムへの信頼を損ねてしまう可能性がある。この問題は、システムがメッセージを処理する仕組みに深く関わっており、特に「メッセージキュー」と呼ばれる技術を使っている場合に発生しやすい。
メッセージキューは、システム間で情報を非同期にやり取りするための仕組みだ。例えば、ユーザーが商品を購入した際に「発送しました」という通知を送る場合、注文処理のシステムが直接メールを送信するのではなく、一度メッセージキューに「発送通知を送ってください」というメッセージを投げ込む。別の「ワーカー」と呼ばれるプログラムがキューからこのメッセージを受け取り、実際にメール送信サービスを使ってユーザーに通知を送る。
ここで問題になるのが、「少なくとも一回(at-least-once)」というキューの処理原則だ。これは、キューに入ったメッセージは「最低でも一回は」ワーカーに処理されることを保証するが、「一回だけ」とは限らないことを意味する。もしワーカーがメッセージを処理し終えた後、キューに対して「このメッセージは処理完了しました」という確認応答(ACK)を送る前に何らかの障害で停止してしまったらどうなるだろうか。キューはワーカーがメッセージを処理した証拠がないため、安全のために同じメッセージを別のワーカーに再送しようとする。その結果、ユーザーには同じ発送通知が二重に届いてしまうのだ。これは特別なケースではなく、メッセージ処理と外部への影響(メール送信などの「副作用」)が分離されているシステムでは常に起こりうる状況だ。
このような重複配信を減らすために、多くのメッセージキューシステムには「重複排除」機能が備わっている場合がある。これは、例えば「過去5分間に同じメッセージがキューに入っていたら、それは無視して重複とみなす」といった機能だ。これは一時的なフィルターとしては有用で、システムの負荷を減らす効果も期待できる。しかし、この5分間という期間はあくまで一時的なものであり、システム障害からの復旧に時間がかかったり、ネットワークの遅延などでメッセージの再送が5分以上遅れたりすると、この重複排除は機能しなくなる。つまり、キューの重複排除機能は「一時的な軽減策」であって、「根本的な解決策」ではない。
では、どうすればこの重複配信を確実に防ぎ、ユーザーに同じ通知が二重に届かないようにできるのだろうか。その鍵となるのが「べき等性(Idempotency)」という考え方だ。べき等性とは、ある操作を何度繰り返しても、その結果が一度実行した場合と同じになることを保証する性質を指す。例えば、「残高を100円増やす」という操作はべき等ではないが、「残高を500円にする」という操作は何度実行しても最終的な残高は500円になるため、べき等であると言える。
メッセージ送信の文脈でべき等性を実現するためには、各通知メッセージに「一意の識別子(Idempotency Key)」を与えることが重要だ。例えば、「ユーザーID」「リマインダーID」「通知予定時刻」といった情報を組み合わせて、その通知がシステムにとって一意のものであることを示すキーを生成する。このキーは、各通知メッセージが持つ「論理的なID」となる。
次に、このべき等性キーを使って、通知が既に処理されたかどうかを記録する「送信用台帳(send ledger)」と呼ばれる永続的なデータストアを用意する。ワーカーがキューからメッセージを受け取ったら、まずこの台帳にアクセスし、そのべき等性キーが「処理中」または「処理済み」として記録されていないかを確認する。
具体的な処理の流れは次のようになる。
- ワーカーはキューからメッセージを受け取る。
- ワーカーはメッセージから一意のべき等性キーを生成する。
- ワーカーはこのキーを「クレーム(claim)」する形で台帳に記録する。これは「この通知は私が今から処理します」と宣言するようなものだ。この記録はアトミック(原子性を持つ)に行われる必要があり、複数のワーカーが同時に同じキーをクレームしようとしても、一人のワーカーだけが成功するようにする。
- もし台帳にそのキーが既に「完了済み」として記録されていれば、ワーカーはメール送信などの実際の処理を行わずに、すぐにキューにACKを返してメッセージの処理を終える。これで重複送信は防げる。
- もし台帳にそのキーが「処理中」として記録されており、そのクレームがまだ有効な期間であれば、それは別のワーカーが既に処理している可能性があるので、現在のワーカーはその処理を避け、後でキューにメッセージを再試行させる。
- ワーカーが実際のメール送信処理などを安全に完了し、その結果が永続的に保存されたことを確認した後、台帳のキーの状態を「完了済み」に変更する。
- 最後に、キューにACKを返してメッセージの処理を終える。
この仕組みでは、メール送信といった外部への副作用が「完全に完了し、その証拠が台帳に記録された後」でなければ、キューへのACKが返されない点が非常に重要だ。もしワーカーがメール送信後に台帳を更新する前に停止しても、キューはメッセージを再送するが、新しいワーカーは台帳を見て既に「完了済み」であれば送信をスキップできる。
べき等性キーを生成する際には、ユーザーIDや機密情報を含む可能性もあるため、安全に扱う必要がある。元の記事では、これらの情報を直接保存するのではなく、HMAC-SHA256という技術を使って安全なハッシュ値を生成する方法が紹介されている。これにより、元の情報を漏らすことなく、一意のキーとして利用できる。
この送信用台帳は、単なるアプリケーションの一時的なデータと考えるべきではない。これは、メッセージ配信の信頼性を保証するための重要な「コントロールプレーン」であり、システムのサービスレベル目標(SLO)に直結する。そのため、十分な容量、データの保持期間、そして障害発生時の復旧計画をしっかりと考慮して運用する必要がある。
システムが正しく動作しているかを監視する際も、単に「メッセージが再送された回数」だけを見るのではなく、「配信を試みた回数」と「実際にユニークなメッセージとして送信が完了した回数」の不一致を監視することが重要になる。これにより、本当に重複送信が発生しているのか、それとも単なる正常な再試行の範囲なのかを区別できるようになる。
このようなべき等性の考え方は、Google Cloud Pub/Sub、Apache Kafka、Temporalなど、どのメッセージングシステムを利用するかにかかわらず、アプリケーションの設計において非常に重要だ。キューの提供する5分間の重複排除機能は負荷軽減には役立つが、正確性を保証するのは、あくまでアプリケーション側で実装するべき等性である。
システムの信頼性を高めるためには、正常な動作だけでなく、障害発生時の挙動を徹底的にテストすることも不可欠だ。例えば、ワーカーがメッセージ処理後にACKを返す前に停止した場合、メッセージが5分以上遅れて再送された場合、外部サービスが一時的にエラー(HTTP 429など)を返した場合など、さまざまなシナリオを想定して、べき等性が正しく機能することを確認する必要がある。また、システムをロールバックする(前のバージョンに戻す)際にも、この送信用台帳のデータを誤って削除したり変更したりしないよう細心の注意を払うべきだ。なぜなら、それがユーザーに通知が届いた唯一の永続的な証拠だからだ。
このように、メッセージキューを使ったシステム開発では、一時的な重複排除機能に頼るだけでなく、通知の「べき等性」をアプリケーションレベルでしっかりと実装することが、信頼性の高いシステムを構築するための鍵となる。