正確に一度のセマンティクス
正確に一度は本当に何ですか
「正確に一度」は、しばしば2つの異なることを意味すると理解されています:- 配達:メッセージは消費者に丁度一度渡されます。
- 処理:最終的な副作用(データベースへの書き込み、残高の変更、別のイベントの発行)は、配達や試みが多かった場合でも、正確に一度発生します。
分散システムでは、セマンティクスの処理について話す方が信頼性が高い。正確に一度の配信は困難です(複製と複製は避けられません)、しかし、結果として生じる状態は、単一の治療と同等にすることができます。
EOSが必要な場合とそうでない場合
EOSが必要な場合:- 現金取引と残高:ダブルライトオフは許可されていません。
- ライセンス/クォータ会計、請求カウンター。
- 取り返しのつかない外部コール(例えば、ワンタイムキーアクティベーション)。
- 効果はリバーシブルまたは補償可能です(サガ、リターン)。
- 一時的な重複は店頭/ログで許可されています。
- パス全体を通じてトランザクションをドラッグするよりも、アイデンポテントシンクを提供する方が安価です。
モデル: エンドツーエンド対ホップバイホップ
Hop-by-hop EOS:各セクション(source→processor→receiver)は、そのアクションを正確に一度適用することを保証します。
エンドツーエンドのEOS:チェーン全体が「事実」から「副作用」まで、結果は単一の治療と同等であることを保証します。
実際には、エンドツーエンドは各ホップのトランザクションと/またはidempotencyの組み合わせによって達成されます。
基本的なビルディングブロック
1.Idempotent操作
操作キーで同じクエリを繰り返すと、同じ結果が得られます。
Ключи: 'idempotency_key'/'event_id'/'operation_id'
実装:TTLによる「見た」操作の表≥入力ログの表示。
2.読み取りプロセス書き込みトランザクション
1つの原子単位の作業では、副作用と読み込み進行(オフセット/位置)の両方が記録されます。これは階段の間に落ちるとき「幽霊」を排除します。
3.バージョン管理/シーケンス
集計のためにバージョン/カウンタが格納されます。'expected_version'が一致する場合にのみ、変更が適用されます。同じイベントの繰り返しは、バージョン→エフェクトを一度増加させません。
4.重複排除(Deduplication)
'(consumer_id、 event_id)'またはトランザクションの自然な'business_id'のインデックス。
実装パターン
1)オフセット固定を用いるトランザクションログ+トランザクションシンク
ストリーム処理に最適です。
ログから読みます(確認されたエントリのみ)。
加工を行います。
- a)シンク(データベース/テーブル)にエフェクトを書き込みます),
- b) 「read to offset N」(同じデータベース内)を修正しました。
- コミットします。再起動すると、すべてが割引されます(オフセットがシフトされます)、または何もありません。
プロパティ:繰り返し実行は有害ではありません。メッセージが2回読まれたとしても、実際には「正確に1回」です。
2) Outbox+idempotent消費者
トランザクション・プロデューサー・サービス。
1つのデータベーストランザクションで:ドメインレコードを変更し、イベントをアウトボックスに書き込みます。
リピーターは同じ'event_id'でイベントをバスに配信します。
消費者は偶発的にイベントを適用します('event_id'によってデダップされます)。
特性:生産者は事実が失われないことを保障します;消費者は正確に1つの効果を保証します。
3) Kafka/FlinkのようなシステムのEOS(概念的に)
Idempotent producer:リトリートを送信する際のテイクから保護します。
プロデューサーのトランザクション:トピックのエントリのグループ+コンサマシフトはアトミックにコミットされます。読者は'read_committed' isolationを使用します。
処理側はstateストアを格納し、トランザクションと共にコミットします。
プロパティ:ストア/ドラッグを再起動すると、ダブルエフェクトにはなりません。下流の「見えない」を複製します。
4) upsert/mergeによるIdempotentの「シンク」
シンクは'operation_id'/'event_id'を取り、'UPSERT……存在しないところ。
副作用(例えば、accrual)は「、すでに適用されていない」チェックで原子的に実行されます。
プロパティ:分散トランザクションなしで、ストレージとエッジで安価なEOSメソッド。
主な実装の詳細
トランザクションID
繰り返しの決定論的でなければなりません(引き込み時に新しいUUIDを生成しないでください)。
安定したスコープ(消費者/ユニット/システム)を持っています。
重複排除テーブル
「consumer_id」、 「operation_id」、 「applied_at'、」 ttl_expires_at'。
'(consumer_id、 operation_id)'のインデックス。
TTL ≥最大リピートウィンドウ(ログ保持+潜在的な遅延)です。
楽観的な競争
書き込みモデルでは、ユニットのバージョンを保存します。
event/commandを適用する場合は、'WHERE version=:expected'を使用します。重複するとバージョンが増加しません。
注文/注文
EOSは「まったく同じ順序」ではありません。"バッチキー(すべての集計イベント→1つのバッチ)および/または'シーケンス'の比較を通して一貫性を確保します。
Idempotent外部呼び出し
安全でないメソッド(たとえば、HTTP webhookをサードパーティのサービスに送信するなど)については「、Idempotency-Key」を追加し、パートナーにサポートを要求します。
頻繁なトラップ
1つの場所でのみEOS:シンクがidempotentであるが、idempotencyなしでセカンダリイベントを放出すると、ダウンストリームで「正確に何回も」取得できます。
2つのコミット:最初にデータベースで、次にブローカーでオフセットコミット-それらの間の落下は重複エフェクトを作成します。
Raw CDCs out: DBスキームを変更すると、消費者の独自性が損なわれます。
不安定なキー:'operation_id'は時間/ランダムに依存し、リトレイ中に変更されます。
コストとトレードオフ
レイテンシ:トランザクション/分離読み取り→p95/p99の成長。
オーバーヘッドストレージ:重複排除テーブル、ステートストア、トランザクションログ。
運用の複雑さ:トランザクションタイムアウト、スレッドのリバランス、セッションのスタック。
診断:より多くの状態(「in kamit」、 「visible as read_committed,」 「rolled back」)。
ポイントごとにEOSを選択してください:重要な集計と効果;残りの部分を不公平と補償でカバーします。
一度限りのテスト
1.フォールトインジェクション:ステップ間のプロセスのドロップ「効果を記録」と「オフセットを記録」。
2.重複:同じメッセージを2〜5回ダウンロードし、1つの効果を確認します。
3.再起動とリバランス:ワーカーの停止/再起動、二重処理の不在を確認します。
4.ネットワークフラッピー:トランザクションの中間タイムアウト、コミットの再試行。
5.負荷テスト:キューの成長→「トランザクションで永遠に」に劣化がないかどうか。
ミニテンプレート(擬似)
オフセット固定を伴うIdempotentシンク
pseudo begin tx if not exists(select 1 from dedup where consumer_id=:c and op_id=:id)
then apply_effect(...) -- upsert / merge / add_one_time_action insert into dedup(c, id, applied_at) values(:c,:id, now)
end if update offsets set pos=:pos where consumer_id=:c commit
ユニットバージョンのコマンド
pseudo begin tx update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
if row_count=0 then error CONCURRENT_MODIFICATION commit
安全性とコンプライアンス
重複排除テーブルのPII/PCI:最小値を保存し、生データの代わりにトークンを使用します。
監査:ログ'operation_id'、 'trace_id'、結果(APPLIED/ALREADY_APPLIED)。
ストレージポリシー:デッドアップテーブルのTTL、オフセット/ログのアーカイブ。
アンチパターン
「Real-once delivery-once delivery」:エフェクトの偶発性なしに、トランスポートプロトコルレベルで重複を除外しようとする試み。
すべてのグローバル分散トランザクション:すべてのサービスを通じたXA/2PCは脆弱で遅いです。
非idempotent副作用の混合(例えば、オフセットコミットの前に送信された電子メール)。
操作キーの欠如:ペイロードの「固有性」に依存しています。
生産チェックリスト
- 各クリティカルエフェクトには、idempotentキーがあります。
- オフセット/読み取り位置は、エフェクト付きの1つのトランザクションで固定されます。
- インデックスされた重複除外テーブル;TTL ≥ログ保持。
- 集計のために楽観的競争(バージョン/シーケンス)が有効になります。
- スレッド/トピックは「Comp Only」モードで読み込まれます(利用可能な場合)。
- CI/CDに重複テストとドロップテストが存在します。
- ダッシュボード:繰り返しの共有、トランザクションの失敗、時間のブロック、ラグ。
- Idempotency-Key/retries/timeoutsのインテグレータドキュメント。
FAQ(よくある質問)
EOSはトランザクションなしで提供できますか?
多くの場合、はい-シンクの(upsert/merge)と集計のバージョニングのidempotencyを介して。トランザクションは保証を簡素化しますが、コストは増加します。
誰もが正確に一度必要ですか?
いいえ、そうではありません。それは高価です。補償ができない/高価な場合には、ポイントごとに適用します。
EOSに文字/Webhookを関連付ける方法は?
コミットの前に通知をバッファし、エフェクトを修正した後に送信します。'notification_id'を保存し、idempotentを送信します。
より重要なのは、配送または処理ですか?
処理します。配達は繰り返されるかもしれません。最終的な状態は正しいと唯一のものでなければなりません。
[結果]
正確に一度は効果の正しさについてであり、配線に重複がないことについてではありません。それはidempotencyの組み合わせによって達成されます、効果と読書の進歩の原子固定、合理的な分割とバージョンの規律。エラーのコストが受け入れられないEOSを適用し、フォールでその現実をテストし、テストを行います-輸送への信念はありません。