Vegas Engineering by FORBIS 記事一覧
← 記事一覧へ戻る
Reliability ・ 第6回

二重処理は、こうして防ぐ — キュー可視性タイムアウトとリトライの整合、そして冪等キー

「同じ注文が2件できる」はなぜ起きるのか。可視性タイムアウトとリトライ時間の関係から、防ぎ方までを設計として整理する。

Vegas 開発チーム ・ 2026-07-03 ・ 読了 7分

STRUCTURE構成:キューの先で「重い同期処理」を呼ぶ

非同期処理の定番構成は、メッセージキュー → コンシューマ → その中で実処理、という並びだ。Vegas でも、注文確定のような重要な副作用を伴う処理を、この形で流している。問題はコンシューマの中身にある。ここで呼ぶのが負荷によって時間の伸びる「重い同期処理」で、しかも一時的な失敗に備えてリトライまで行う場合、処理全体の所要時間は状況次第で大きく揺れる。

FAILURE失敗機序:可視性 < 処理時間 で再配信が起きる

事故は「時間の逆転」で起きる。可視性タイムアウトが、リトライを含む実処理の最大所要時間より短いとどうなるか。ワーカーがまだ処理を続けている最中にタイムアウトが切れ、メッセージが再可視化され、別のワーカーへ再配信される。二台目は、一台目がまだ完了していないことを知らないまま、同じ入力で同じ処理を始める。結果として同じ副作用が二度実行され、注文が2件生成される、といった重複が生まれる。

ワーカーA ワーカーB 実処理(リトライ込み・継続中) 可視性タイムアウト(短い) 切れる→再配信 同じ入力を再処理 二重生成
図:可視性 < 処理時間だと、処理継続中に再配信され二重に走る

FIX対策:時間の整合、冪等キー、部分失敗の報告

防ぎ方は三段構えで考えると漏れがない。

ワーカーA 実処理(リトライ込み) 可視性タイムアウト(処理時間以上) 完了→削除 再配信なし
図:可視性 ≥ 処理時間なら、完了・削除が先に来て再配信は起きない

CODE冪等キーによる重複排除

時間の整合は前提だが、それだけに頼るのは危うい。処理時間は見積もりを超えうるし、外部要因で伸びることもある。最後の砦として、処理側で冪等キーによる重複排除を必ず入れておく。

consumer.pyPython
def handle(message):
    key = message.idempotency_key   # リクエスト固有のID
    if already_processed(key):      # 既に処理済みなら
        return                      # 何もせず終了(副作用ゼロ)
    result = process(message)       # 重い同期処理を実行
    mark_processed(key, result)     # キーを記録し二度目を弾く
    return result

LESSON教訓:整合で見る、冪等性は前提

リトライ設計とキュー設定は、別々の設定値としてではなく「時間の整合」として一体で見るべきだ。片方を変えたらもう片方の最悪値を必ず問い直す。そして冪等性は「あれば安心」の保険ではなく、自動処理やAI連携が同じ入力を何度でも投げうる時代の前提条件だ。副作用を持つ処理には、最初から冪等キーを設計に織り込んでおきたい。

TAKEAWAY

可視性タイムアウトは「リトライ込みの最大処理時間以上」に。その上で冪等キーによる重複排除を処理側に必ず置く。時間の整合と冪等性、この二つが揃って初めて二重処理は防げる。