重復策略和等效性
1)為什麼需要它
在網絡中,故障是常態:計時器,瞬態錯誤,網絡翻轉,擁塞。只有在以下情況下,Retrai才能提高可靠性:1.重復是安全的(等效的),
2.遵循重復之間的摘錄,
3.尊重上癮的限制/配額和「健康」。
目標是在業務運營級別上表現出effectively-once行為,而無需進行虛假的雙打和比賽。
2)交付語義的分類法
最多:不重復、丟失風險(拼寫、火災和遺失)。
On-least-once:可能重復→需要消費者的平均水平(大多數隊列,webhook)。
Effectively-once:復制是可能的,但重復數據消除是正確的(密鑰、事務、outbox)。
3)何時撤回,何時不撤回
回溯是有道理的:'408','429'(遵守'Retry-After'),'425'(Too Early),'499'(client closed on the shirt),'5xx','504',網絡定時/斷裂,'502'在網關上,「connection reset」。
不重新定義請求:「400/ 401/403/404/422」。
有爭議的案例:「409沖突」(通常不復制;首先閱讀操作狀態/重新確認意圖)。
4)Taymauts,backoff和jitter
4.1規則
首先是定時,然後是轉發:每個請求都應該有「最後期限」。
Exponential backoff: 'delay_n=base 2^n',限制'max_delay'。
Jitter具有約束力:為「鈍同步波」的互換添加隨機性。
4.2 Jitter模板
Full jitter: 'sleep=rand (0, base2^n)'是最佳整體選擇。
Decorrelated jitter:「sleep=min(max_delay,rand(base,sleep_prev3))」-用於長時間對話。
Equal jitter: 「sleep=base2^n/2+rand (0, base2^n/2)」是柔和的變化。
4.3 Retry-budget
限制回廊的份額:- `retry_budget_per_min = max(α success_rps, floor β)`;通常'α=0。1–0.2`.
- 當預算用盡時-切換為fail-fast/circuit breaker 「open」。
5)與rate限制和Circuit Breaker互動
尊重「Retry-After」,「RateLimit-Reset」,並計入支持。
在高「5xh」/taymauts-下調retrae頻率和總並發性。
Circuit breaker:
半場開放:允許有限的樣本。
打開:立即拒絕(節省資源)。
Closed:通常的工作。
在寫作操作中,最好以明確的線索返回409/503,而不是旋轉激進的回程。
6)寫作操作的冪等性
6.1總的想法
相同的意圖→一個結果。基礎是等容性的鑰匙和執行記錄的存儲。
6.2個HTTP合同
客戶端發送標題:
Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
服務器:
- 首次成功時,將保留(鑰匙→結果,狀態,身體哈希);
- 重播時,返回以前的響應和標題「Idempotency-Replay:true」;
- 在身體沖突中(相同的鑰匙,但不同的負載)是「409沖突」。
6.3存儲和TTL
表/鍵值:'idempotency_key'、'request_hash'、'result'、'status'、'expiry_at'。
TTL=可能的重播和延遲交付窗口(通常為24-72小時付款)。
「idempotency_key」索引;對於高負載-散列哈希。
6.4示例模式(SQL)
sql
CREATE TABLE idempo_store (
key UUID PRIMARY KEY,
req_hash BYTEA NOT NULL,
status INT NOT NULL,
response JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expiry_at TIMESTAMPTZ NOT NULL
);
6.5處理程序的偽代碼
pseudo handle_write(req):
k = req. headers["Idempotency-Key"]
h = hash(req. body)
rec = idempo_store. get(k)
if rec and rec. req_hash == h:
return rec. status, rec. response, {"Idempotency-Replay": "true"}
if rec and rec. req_hash!= h:
return 409, problem("IDEMPOTENT_CONFLICT")
begin tx result = apply_business_mutation (req) # change status upsert once (idempo_store, key = k, req_hash=h, status = 201, response = result, expiry = now () + 2d)
commit
return 201, result
7)「effectively-once」模式"
Transactional Outbox:記錄業務事件,並通過背景中繼器從相同的DB事務發送消息;消費者是同等的。
消費者的收件箱/處理表:我們保留「event_id」以忽略配音。
Kafka上的exactly-once ≠業務上的exactly-once:即使在生產者/消費者的EOS中,應用邏輯仍然必須是偶然的。
補償性事務(Saga):如果步驟中繼並引起副作用,我們將系統恢復為不變。
8)私人案件: 付款和金融交易
Strong idempotency:密鑰綁定到操作邏輯(例如'external_payment_id')。
在PSP上進行重復數據消除:存儲「merchant_reference」 →在重試時,PSP將返回以前的結果。
Retrai「來自客戶」:僅在「Idempotency-Key」中允許,否則可能會雙重註銷。
競爭性:執行期間的「帳戶/工具/合同」鎖定;重播時返回409/423。
可觀察性:度量「idempo_replay_total」,「idempo_conflict_total」。
9)Webhooks和外部挑戰
HMAC簽名和時間窗口;首先檢查,然後處理。
發件人轉發:指數backoff+jitter,「max_attempts」和DLQ。
消費者-偶數:「event_id」 →表/內存緩存;「整潔」的秩序不能保證。
代碼:2xx=成功,4xx=不重復,5xx/taymout=重復。
10)隊列和背景任務
缺省情況下,只有重復→是不可避免的。
存儲「task_id」/「event_id」和運行時狀態;雙打-短路「replay」。
DLQ和poison-messages:嘗試計數器、隔離、手動解析。
競爭性限制(信號)和偶發性鍛煉者。
11)轉化和「自然」鑰匙
自然鍵(賬號+日期+文件號)增強了對重復的抵抗力。
更改方案/版本時,在「Idempotency-Key」或查詢哈希中包含版本密鑰。
12) HTTP標題和客戶端提示
"Idempotency-Key","Idempotency-Replay","Retry-After","Prefer:wait=<sec>'(長期運營),'If-Match'/'ETag'(樂觀鎖定)。
409在密鑰沖突中與有效的「Retry-After」 425/429/503。
對於「長」操作-接受異步狀態(「202接受」+「位置」到狀態資源)。
13)測試和混亂場景
Negative Test: 雙發,重播與不同的身體,時鐘旋轉.
違反命令:「t2」早於「t1」。
註射Taymauts/「RST」/「EOF」,半心半意查詢(慢發)。
掉落的idempotency存儲庫→失敗的封閉行為(故障優於雙重註銷)。
14)度量標準和Alertes
`retries_total{reason}`, `retry_budget_used{route}`, `backoff_seconds_bucket`.
`idempo_replay_total`, `idempo_conflict_total`, `duplicate_detected_total`.
路線上409/425/429/5xx的份額;p95/p99「成功的時間」。
Alerts: burn-rate retrais預算,水平沖突激增,DLQ增長。
15)反模式
連續重述所有錯誤。
缺乏抖動→同步回旋波。
長壽鑰匙沒有TTL和清潔。
在副作用(outbox幹擾)commit後保存結果。
沒有「trace_id」/「idempotency_key」 →邏輯是不可能的。
在寫作操作中具有攻擊性的平行回程。
16)準備就緒支票清單
[……]單一政策:我們要回溯什麼,不是什麼;代碼和提示客戶端。
- 指數backoff+full jitter;設置為「retry_budget」。
- 合同'Idempotency-Key'+與TTL存儲結果。
- 事件的Outbox/Inbox;DLQ;競爭限制。
- 與電路斷路器集成,尊重「Retry-After」。
- 度量/重復/沖突指標。
- 混沌測試套件和網絡故障仿真。
- 客戶文檔:後退示例和狀態。
17) TL;DR
Retrai僅與冪等性一起使用。引入「Idempotency-Key」和結果存儲庫,應用帶有抖動和背包的指數反沖,尊重「Retry-After」,與電路斷路器集成。對於事件-outbox/inbox;對於支付-嚴格的重復數據消除和鎖定。測量復古和沖突,測試復制品和taymout。