プレイブック

支払いの効果的な 1 回処理 (1)

1 回限りのメッセージ送信は嘘です。多層防御を冪等性、重複排除、送信トレイ、調整と組み合わせた場合に、一度だけ効果的なビジネス成果を達成するにはどうすればよいでしょうか?

分散型決済エンジン

一部 20 の 22

取得と完了の間のギャップを埋める一連の分散型支払いアーキテクチャ。

Distributed payment engine architecture diagram

前のセクションでは、回復パイプラインが最初に自動化の原則に基づいて動作し、証拠に基づいたランブックが一意性の壁で機能することを見てきました。このセクションでは、パイプラインの基礎となるメッセージングの保証と、業界で最も一般的な誤解である 1 回限りの配信について詳しく説明します。

ブローカーの売り手は「1 回限りのセマンティクス」を約束します。実際のところ、分散システムでは、メッセージが正確に 1 回処理されることが物理的に保証されていません。ネットワークがダウンし、ワーカーがクラッシュし、ブローカーが再送信されます。問題は「メッセージが何回届いたか」ではありません。 ジョブ結果は何回発生しましたか。```text Exactly-once messaging → imkansız (at-least-once + failure = duplicate) Effectively-once outcome → mümkün (defense in depth)


## 最初に説明した概念```text
📦 At-Least-Once Delivery
Mesaj en az bir kez ulaşır; ağ hatasında tekrar gönderilebilir.

📦 Effectively-Once
Mesaj birden fazla kez işlense bile iş sonucu (charge, refund, order) yalnızca bir kez gerçekleşir.

📦 Defense in Depth
Tek bir mekanizmaya güvenmek yerine, birden fazla bağımsız koruma katmanı.

📦 Idempotent Consumer
Aynı mesajı ikinci kez aldığında aynı sonucu üreten, yan etki yaratmayan tüketici.
```1 回限りのメッセージングは​​ブローカーの機能ではありません。実質優先の制度設計です。

## たった一度だけ、なぜ嘘をつくのか

ワーカーはメッセージを受信し、処理して確認応答を送信しますが、その確認応答はネットワーク内で失われます。ブローカーはメッセージを再送信します。ワーカーが 2 回目の処理を行います。これが少なくとも 1 回の配信の性質です。ブローカーがどれほど「1 回だけ」と主張しても、消費者側での重複は避けられません。```text
Worker mesajı işler → sonuç: Captured
Ack ağda kaybolur
Broker mesajı tekrar gönderir
Worker tekrar işler → sonuç: ??? (çift charge riski)
```問題はメッセージが何回届くかではありません。それが再臨の時に起こることです。 2 回目の訪問で副作用がなければ、1 回の効果が得られたことになります。

## 多層防御: 1 層だけでは不十分

事実上、ビジネスの成果は補完的なレイヤーの組み合わせです。どちらかだけでは十分ではありません。これらをすべて組み合わせると、「少なくとも 1 回到着するメッセージは最大 1 回は影響を与える」という結果が得られます。```text
Katman 1: Idempotency key (API)
  → aynı key ile ikinci charge isteği reddedilir

Katman 2: Consumer dedup (messaging)
  → aynı message id ikinci kez işlenmez

Katman 3: DB uniqueness constraint
  → aynı idempotency key ile ikinci satır yazılamaz

Katman 4: Outbox pattern
  → event yalnızca transaction commit ile birlikte yayınlanır

Katman 5: Reconciliation
  → katman 1-4'ün kaçırdığı drift'i periyodik olarak düzeltir
```1 つの層に障害が発生すると、もう 1 つの層が引き継ぎます。冪等キーを省略すると、一意性制約が適用されます。制約が省略された場合のリコンシリエーションが修正されました。

## 各レイヤーの質問は異なります

|レイヤー |質問 |保護中 |
| --- | --- | --- |
|冪等性キー |また同じ意図ですか? | API レベルでの二重請求 |
|消費者向け重複排除 |また同じメッセージですか? |メッセージング レベルでの二重トランザクション |
|一意性制約 |同じキーの 2 行目? | DB レベルの二重レコード |
|送信ボックス |イベントはコミットなしで公開されましたか? |紛失または早期イベント |
|和解 |ローカルとPSPは互換性がないのでしょうか? |逃げるものすべて |

## 冪等コンシューマの書き方

べき等コンシューマのルール: 同じ入力、同じ出力。 2 回目の呼び出しでは副作用はありません。```text
PaymentCaptured webhook (messageId=wh_991)
  → dedup tablosuna bak: wh_991 işlendi mi?
  → evet → skip (log: duplicate suppressed)
  → hayır → finalize, dedup tablosuna yaz
```完了ステップ自体は冪等である必要があります。支払いがすでに `Captured` である場合、イベントは再度発行されず、注文も再度作成されるべきではありません。 Dedup テーブルはメッセージング層を保存します。端末ステータス制御によりビジネス ロジックが保護されます。

## 送信ボックス: イベントと状態は同じトランザクション内にあります

送信ボックス パターンは、「状態は更新されたがイベントは公開されていない」または「イベントは公開されたが状態は更新されていない」というジレンマを解決します。イベントはローカル トランザクションとともに送信トレイ テーブルに書き込まれます。別のパブリッシャーが送信ボックスを読み取り、ブローカーに送信します。```text
BEGIN TRANSACTION
  UPDATE payment SET status=Captured
  INSERT INTO outbox (event=PaymentCaptured, paymentId=8812)
COMMIT
  → publisher outbox'ı okur → broker'a gönderir
  → gönderim başarılı → outbox satırını siler
```Outbox は 1 回限りのメッセージングを提供しませんが、状態とイベントの間の一貫性を保証します。和解は、アウトボックスから逃げた人々を捕まえます。

## 混同されやすい区別```text
❌ Broker exactly-once = sistem exactly-once
✓ Broker dedup + consumer idempotency + DB constraint = effectively-once

❌ Idempotency key her yerde yeterli
✓ Idempotency key API'yi korur; webhook ve async worker ayrı katman ister

❌ Effectively-once = perfect system
✓ Effectively-once = iş sonucu bir kez; tutarsızlık penceresi reconciliation ile kapanır
```## 配信保証とビジネス成果の比較

|保証 |それは何を約束しますか?支払いには十分ですか? |
| --- | --- | --- |
|最大 1 回 |メッセージが失われると、失われる可能性があります。いいえ — 支払いの紛失 |
|少なくとも 1 回 |メッセージは少なくとも 1 回は複製される可能性があります。いいえ、一人で |
| 1 回限り (ブローカー) |理論的には、実際には消費者側で問題が発生します。いいえ |
|実質的に 1 回 (システム) |ジョブ結果は 1 回 |はい |

## 実質的に 1 回限りのチェックリスト

1. API チャージ リクエストは冪等キーで保護されていますか?
2. Webhook および非同期コンシューマーにメッセージ ID の重複はありますか?
3. 支払いテーブルの冪等キーに一意性制約はありますか?
4. 状態変更とイベント ブロードキャストは、送信ボックス パターンと同じトランザクション内で行われますか?
5. ファイナライズハンドラはターミナル状態で再動作せずにスキップしますか?
6. 調整は、レイヤー 1 ~ 5 で見逃されたドリフトを定期的にスキャンしますか?

## この記事で覚えておくべきこと

1. 1 回限りのメッセージ送信は嘘です。少なくとも 1 回 + 失敗 = 重複は避けられません。
2. 多層防御により、効果的に優先されたビジネス結果が可能になります。1 層だけでは十分ではありません。
3. 各保護層は異なる質問に答えます。彼ら全員が協力しなければなりません。
4. 和解は最後の層です。以前のレイヤーを置き換えるのではなく、補完します。

> たとえブローカーが「1 回だけ」と約束したとしても、支払いシステムはそれを信じるべきではありません。信じるべきなのは、ビジネスの成果を効果的に徹底的に構築することです。

次のセクションでは、この 22 部構成のプロセス、すなわち実稼働決済エンジン設計チェックリストの統合に進みます。

FAQ

よくある質問

少なくとも 1 回の配信とは何ですか?

メッセージは少なくとも 1 回は到着します。ネットワークエラーの場合は再送信可能。

実質的に 1 回とは何ですか?

メッセージが複数回処理された場合でも、ジョブの結果 (請求、返金、注文) は 1 回だけ発生します。

「ブローカー 1 回限り = システム 1 回限り」は正しいですか?

ブローカーの重複排除 + コンシューマの冪等性 + DB 制約 = 実質的に 1 回

このセクションでは何を修正しますか?

ブローカーの売り手は「1 回限りのセマンティクス」を約束します。実際のところ、分散システムでは、メッセージが正確に 1 回処理されることが物理的に保証されていません。ネットワークがダウンし、ワーカーがクラッシュし、ブローカーが再送信されます。問題は「メッセージが何回届いたか」ではありません。 **ジョブ結果は何回発生しましたか**。 1 回限りのメッセージ送信は嘘です。少なくとも 1 回 + 失敗 = 重複は避けられません。前のセクションでは、回復パイプラインが最初に自動化の原則に基づいて動作し、証拠に基づいたランブックが一意性の壁で機能することを見てきました。このセクションでは、パイプラインの基礎となるメッセージングの保証と、業界で最も一般的な誤解である 1 回限りの配信について詳しく説明します。

学んだエンジニアリング原則

  • 1 回限りのメッセージ送信は嘘です。効果的に一度の作業結果が可能です。
  • 多層防御: 冪等性、重複排除、一意性、アウトボックス、調整が連携して機能します。
  • 保護の各層は異なる質問に答えます。 1層では不十分です。

続きを読む

続きを読む

シリーズの次のシリーズ

エッセイ

本番決済エンジンの設計

22 部構成のシリーズの総合: チェックアウト オーケストレーターとプロバイダー ゲートウェイを備えた運用決済エンジンのアーキテクチャ チェックリスト。

シリーズの次のシリーズ

同じシリーズ

Paylaş