Idemotenceとキー
idempotencyとは
Idempotencyは、同じ識別子で繰り返しても最終的な効果が変わらない操作のプロパティです。分散システムでは、これはリトレイ、重複メッセージ、タイムアウトにもかかわらず、結果を「正確に1つの処理」に相当させる主な方法です。
重要なアイデア:各潜在的に再現可能な操作には、システムが「これはすでに行われている」と認識し、結果を1回以上適用するキーでマークする必要があります。
重要な点
支払いと残高:'operation_id'によるwrite-off/credits。
予約/クォータ/制限:同じスロット/リソース。
Webhooks/notifications:繰り返し配信はエフェクトを複製しないでください。
インポート/マイグレーション-ファイル/パッケージを再実行します。
ストリーム処理:ブローカー/CDCからの重複。
キーの種類とその範囲
1.操作キー-ビジネス取引の特定の試みのID
例:'idempotency_key' (HTTP)、 'operation_id' (RPC)。
範囲:サービス/集計;重複除外テーブルに格納されます。
2.イベントキー-イベント/メッセージの一意の識別子
例:'event_id' (UUID)、 '(producer_id、 sequence)'。
エリア:消費者/消費者グループ;突起を保護します。
3.ビジネスキー-自然なドメインキー
例:'payment_id'、 'invoice_number'、 '(user_id、 day)'。
エリア:集計;一意性/バージョンチェックで使用されます。
TTLと保持ポリシー
TTLキー ≥、ログ保持+ネットワーク/プロセス遅延の可能性があります。
重要なドメイン(支払い)TTL-日/週;テレメトリー-時間。
バックグラウンドジョブでデッドアップテーブルをクリーンにします。監査-アーカイブ。
キーストア(重複排除)
トランザクションデータベース(推奨):信頼性の高いアップサート/ユニークなインデックス、効果のある共同トランザクション。
KV/Redis:短期間のTTLには高速で便利ですが、OLTPとの共同取引はありません。注意してください。
Stateストアストリームプロセッサ:ローカル+ブローカーのchangelog;Flink/KStreamsが得意です。
- idempotency_keys
'consumer_id'(\\\\\\'\'service')、'op_id '(PK\'\\'\')、' applied_at'、'ttl_expires_at'、 'result_hash'/'response_status' (опц。).
インデックス:'(consumer_id、 op_id)'-ユニーク。
基本的な実装技術
1)エフェクト+プログレストランザクション
1つのトランザクションで結果を記録し、読み取り/位置の進行状況をキャプチャします。
pseudo begin tx if not exists(select 1 from idempotency_keys where consumer=:c and op_id=:id) then
-- apply effect atomically (upsert/merge/increment)
apply_effect(...)
insert into idempotency_keys(consumer, op_id, applied_at)
values(:c,:id, now)
end if
-- record reading progress (offset/position)
upsert offsets set pos=:pos where consumer=:c commit
2)楽観的な並行性(ユニットバージョン)
レース時の二重効果から保護します:sql update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
-- if 0 rows are updated → retry/conflict
3) Idempotentシンク(upsert/merge)
一度Accrue:sql insert into bonuses(user_id, op_id, amount)
values(:u,:op,:amt)
on conflict (user_id, op_id) do nothing;
プロトコルにおけるIdempotency
HTTP/REST
'Idempotency-Key: <uuid' hash>'ヘッダー。
サーバーはキーレコードを格納し、同じレスポンスを再度返します(不変な競合の場合は'409'/'422'というコード)。
「安全でない」POSTの場合「、Idempotency-Key」+安定したタイムアウト/リトレイポリシーが必要です。
gRPC/RPC
メタデータ'idempotency_key'、 'request_id'+期限。
サーバの実装-RESTと同様に:トランザクションの重複除外テーブル。
ブローカー/ストリーミング(Kafka/NATS/Pulsar)
Producer: stable 'event_id '/idempotent producer(サポートされている場合)。
消費者:'(consumer_id、 event_id)'および/または集計のビジネス版によってdedup。
非idempotent/corruptメッセージのDLQを分離します。
Webhookと外部パートナー
コントラクトに'Idempotency-Key'/'event_id'を要求します。再配達は安全でなければなりません。
'notification_id'を保存し、ステータスを送信します。リトレイで-重複しないでください。
キーデザイン
決定性:リトレイは同じキーを送信する必要があります(クライアント/オーケストレーターで事前に生成します)。
スコープ:フォーム'op_id'として'service: aggregate: id: purpose'。
衝突:ビジネスパラメータからUUIDv7/ULIDまたはハッシュを使用します(必要に応じて塩で)。
階層:→frontの一般的な'operation_id'は、すべてのサブオペレーション(idempotent chain)に変換されます。
UXと製品の側面
繰り返しキーリクエストは、同じ結果(body/statusを含む)または明示的に「既に実行されている」を返す必要があります。
「幸運のために」再試行するのではなく「、処理中/完了中」のステータスをユーザーに表示します。
長い操作の場合-キーによるポーリング('GET/operations/{ op_id}')。
観測可能性
ログ'op_id'、 'event_id'、 'trace_id'、結果:'APPLIED'/'ALREADY_APPLIED'。
メトリクス:繰り返しレート、デッドアップテーブルサイズ、トランザクション時間、バージョン競合、DLQレート。
トレース:キーはコマンド→イベント→投影→外部コールを通過する必要があります。
安全性とコンプライアンス
PIIをキーに格納しないでください。key-識別子、ペイロードではありません。
重複除外レコードの機密フィールドを長いTTLで暗号化します。
保持ポリシー:TTLとアーカイブ;忘れられる権利-レスポンス/メタデータの暗号消去(PIIが含まれている場合)。
テスト
1.重複:1つのメッセージ/リクエストを2〜5回実行します。
2.ステップ間でドロップ:効果を記録する前/後、オフセットを修正する前/後。
3.消費者の再起動/リバランス:二重使用なし。
4.競合:1つの'op_id'→1つの効果を持つ並列クエリ、2つ目は'ALREADY_APPLIED/409'です。
5.長寿命のキー:TTLの有効期限をチェックし、回復後に再試行します。
アンチパターン
各リトレイのランダムな新しいキー:システムはリプレイを認識しません。
2つの別々のコミット:最初にエフェクト、次にオフセット-それらの間の落下はエフェクトを複製します。
ブローカーだけを信頼すること:あざ/集計の重複排除はありません。
集計バージョンがない:繰り返しイベントの変更が2度目の状態になります。
ファットキー:キーには、ビジネスフィールド/PII→リークと複雑なインデックスが含まれます。
反復可能な応答はありません:クライアントは安全に引き込めません。
例
お支払いポスト
顧客:'POST/payments'+'Idempotency-Key: k-789'。
Server: transaction-'idempotency_keys'に'payment'とエントリを作成します。
やり直し:同じ'201 '/bodyを返します。不変競合の場合-'409'。
ボーナス発生(シンク)
sql insert into credits(user_id, op_id, amount, created_at)
values(:u,:op,:amt, now)
on conflict (user_id, op_id) do nothing;
イベントからの投影
消費者は単位の'見られる(event_id)'および'版'を貯えます;repeat-ignore/idempotent upsert。
Read progressは、プロジェクション更新と同じトランザクションでキャプチャされます。
生産チェックリスト
- すべての安全でない操作には、idempotentキーとそのスコープが定義されています。
- TTLとユニークなインデックスを持つ重複除外テーブルがあります。
- 読書の効果と進行は原子的にコミットされる。
- 楽観的競争(バージョン/シーケンス)が書き込みモデルに含まれています。
- APIコントラクトは'Idempotency-Key'/'operation_id'と反復動作をキャプチャします。
- メトリックとログには'op_id'/'event_id'/'trace_id'が含まれています。
- 重複、転倒、およびレースのテスト-CIで。
- TTL/アーカイブポリシーとPIIセキュリティに従います。
よくあるご質問
「Idempotency-Key」と「Request-Id」はどのように異なりますか?
'Request-Id'-トレース;それはretraysで変えることができます。'Idempotency-Key'は操作のセマンティック識別子であり、繰り返しの間は同じである必要があります。
データベースなしでidemotenceを行うことは可能ですか?
短いウィンドウでは、はい(Redis/in-process cache)ですが、共同トランザクションがなければ重複のリスクが高まります。重要なドメインでは、1つのデータベーストランザクションの方が優れています。
外部パートナーと何をすべきか?
キーと反復可能な応答をネゴシエートします。パートナーがサポートしていない場合は、idempotentレイヤーでコールをラップして「既に適用済み」を保存します。
TTLの選び方?
最大遅延を合計します:ログ保持+ネット/リバランスワーストケース+バッファ。ストック(× 2)を追加します。
合計
Idempotencyはキー、トランザクション、バージョンの規律です。安定した動作識別子+効果の原子固定と進行状況の読み取り+特異なシンク/投影は、トランスポートレベルの魔法なしに「正確に1つの効果」を与えます。キーを決定論的にし、TTLを現実的にし、悪意のあるテストを行います。その後、リトレイと重複は、インシデントではなく、ルーチンになります。