GH GambleHub

Exactly-once vs At-least-once

1)為什麼要討論語義

交付語義定義了接收者在失敗和後退時看到消息的頻率:
  • 最多-沒有重播,但是可能會丟失(很少被接受)。
  • One-least-once-我們不會丟失,但我們可以重復丟失(大多數經紀人/隊列的默認值)。
  • Exactly-once-根據觀察到的效果,對每個消息進行一次精確處理。

關鍵真理:在沒有全局交易和同步一致性的分布式世界中,無法實現「純」端到端的異常一事。我們有效地建立了exactly-once:允許在運輸中重復使用,但是我們使處理相等,以便觀察到的效果「好像曾經」。


2)故障模型和重復出現的位置

重播是由於以下原因而出現的:
  • 損失ack/commit(制作人/經紀人/consumer「沒有聽到」確認)。
  • 領導者連任/復制,網絡中斷後的恢復。
  • 任何地點的Taymautov/Retraev(kliyent→broker→konsyumer→sink)。

結果:不能依靠運輸的「交付獨特性」。管理效果:寫入DB,註銷,發送信件等。


3) Exactly-once在供應商和它實際上是什麼

3.1 Kafka

給出磚塊:
  • Idempotent Producer (`enable.idempotence=true')-在撤退時防止生產者側面的拍攝。
  • 交易-以原子方式將消息發布到多個批次,並推銷消費期權(讀取過程寫作模式沒有「跳過」)。
  • Compaction-通過鍵存儲最後一個值。

但是「鏈條末端」(合成:DB/付款/郵件)仍然需要冪等。否則,處理器雙擊將產生雙擊效果。

3.2 NATS / Rabbit / SQS

默認情況下為帶有ack/redelivery的at-least-once。Exactly-once是在應用程序級別實現的:按鍵,滯後源,upsert。

結論:單向運輸≠單向效應。後者是在處理程序中完成的。


4)如何在once-least-once之上有效地構建一個有效的exactly-once

4.1 Idempotency key (idempotency key)

每個命令/事件都帶有自然鍵:「payment_id」,「order_id#step」,「saga_id#n」。處理程序:
  • 檢查「已經見過嗎?」是具有TTL/還原功能的滯後源(Redis/DB)。
  • 如果看到-重復先前計算的結果或進行無操作。
Redis草圖:
lua
-- SET key if not exists; expires in 24h local ok = redis.call("SET", KEYS[1], ARGV[1], "NX", "EX", 86400)
if ok then return "PROCESS" else return "SKIP" end

4.2個底座中的Upsert(等效合成器)

記錄是通過UPSERT/ON CONFLICT進行的,並帶有版本/金額驗證。

PostgreSQL:

sql
INSERT INTO payments(id, status, amount, updated_at)
VALUES ($1, $2, $3, now())
ON CONFLICT (id) DO UPDATE
SET status = EXCLUDED.status,
updated_at = now()
WHERE payments.status <> EXCLUDED.status;

4.3事務性Outbox/Inbox

Outbox:業務事務和記錄「發布事件」發生在單個DB事務中。背景發布者讀取outbox並發送給經紀人→狀態與事件之間沒有差異。
收件箱:對於傳入的命令,我們在運行前保留「message_id」和結果;重復處理可以看到記錄,並且不會重復副作用。

4.4串聯鏈處理(read→process→write)

Kafka:交易「閱讀了正版,→在一個原子單元中記錄了→ commit」的結果。
無交易:「首先記錄結果/收件箱,然後記錄結果」;在裂縫時,副本將看到Inbox並終止。

4.5 SAGA/賠償

當無法實現等效性(外部提供商註銷了資金)時,使用補償性操作(refund/void)和等效外部API(使用相同的「Idempotency-Key」的重復「POST」給出了相同的總和)。


5)什麼時候足夠了

使用按鍵壓縮更新緩存/實例化視圖。
可接受重復增量的計數/度量(或將增量存儲為版本)。
二次寫作不重要的註釋(最好還是放下鑰匙)。

規則:如果雙打沒有改變業務含義,或者很容易檢測到→ at-least-once+部分保護。


6)生產力和成本

Exactly-once(甚至「有效」)成本更高:多余的記錄(Inbox/Outbox),密鑰存儲,交易,更難診斷。
At-least-once便宜/更簡單,在throughput/p99上更好。
估價:雙打價格×雙打賠率與防守成本。


7)配置和代碼示例

7.1 Kafka制作人(等效性+交易)

properties enable.idempotence=true acks=all retries=INT_MAX max.in.flight.requests.per.connection=5 transactional.id=orders-writer-1
java producer.initTransactions();
producer.beginTransaction();
producer.send(recordA);
producer.send(recordB);
// также можно atomically commit consumer offsets producer.commitTransaction();

7.2 Concumer with Inbox(偽代碼)

pseudo if (inbox.exists(msg.id)) return inbox.result(msg.id)
begin tx if!inbox.insert(msg.id) then return inbox.result(msg.id)
result = handle(msg)
sink.upsert(result)     # идемпотентный синк inbox.set_result(msg.id, result)
commit ack(msg)

7.3 HTTP Idempotency-Key(外部API)


POST /payments
Idempotency-Key: 7f1c-42-...
Body: { "payment_id": "p-123", "amount": 10.00 }

使用相同密鑰的重復開機自檢→相同的結果/狀態。


8)可觀察性和指標

「duplicate_attempts_total」-捕獲了多少次(根據Inbox/Redis)。
'idempotency_hit_rate'是通過冪等性「保存」的重復的比例。
'txn_abort_rate'(Kafka/DB)是回滾的比例。
'outbox_backlog'-發布滯後。
'exactly_once_path_latency {p95, p99} 'vs'at_least_once_path_latency'-開銷。
Logs審核:「message_id」、「idempotency_key」、「saga_id」、「attempt」捆綁。


9)花花公子測試(遊戲日)

發貨重播:制片人在人工定時下轉播。
「Sink和ack」之間的裂痕:確保Inbox/Upsert防止雙打。
Pere-Deliver:增加經紀人的再分配;檢查你的祖父。
外部API的相等性:具有相同鍵的重復開機自檢是相同的響應。
領導者變更/網絡中斷:檢查Kafka 交易/consumer行為。


10)反模式

依靠運輸:「我們有一個exactly-once的Kafka,這意味著你可以沒有鑰匙」-不。
錄音前沒有行動:無所事事,但無所事事→損失。
缺少DLQ/靜音轉播:無休止的重播和風暴。
隨機UUID代替自然鍵:沒有什麼可重復的。
將Inbox/Outbox與無索引的Prod Table混合:熱鎖和p99尾巴。
在外部提供商中無需等效的API的業務運營。


11)選擇支票清單

1.雙重價格(金錢/法律/UX)與保護價格(潛在性/復雜性/價值)。
2.是否有自然事件/操作密鑰?如果沒有-想出一個穩定的。
3.Sink是否支持Upsert/轉化?否則-Inbox+補償。
4.需要全局交易嗎?如果沒有-分段到SAGA。

5.需要接力/長接力?Kafka + Outbox.需要快速RPC/低延遲?NATS + Idempotency-Key.

6.多重性與配額:鍵/空間隔離。
7.可觀察性:包括可見度和backlog度量。


12) FAQ

Q: 能否實現「數學」exactly-once終結?

答:僅在狹窄的場景中,單一一致性存儲和事務始終如一。通常,沒有;通過相容性有效地使用。

Q: 什麼更快?

A: At-least-once.Exactly-once添加了超過p99的交易/密鑰存儲和成本→。

Q: 在哪裏存儲等容密鑰?

A:使用TTL或Inbox表(PK=message_id)的快速堆棧(Redis)。對於付款-更長(天/周)。

Q: 如何選擇TTL dedup keys?

A:最低=最長重新交付時間+操作庫存(通常為24-72小時)。對於金融-更多。

Q: 如果我在Kafka有按鍵匹配,需要鑰匙嗎?

A:是的。Compaction將減少存儲,但不會讓你的sync保持靜止。


13)結果

At-least-once是基本的可靠傳輸語義。
Exactly once作為一種業務效果是在處理器級別實現的:Idempotency-Key,Inbox/Outbox,Upsert/版本,SAGA/補償。
選擇是一種權衡成本↔雙重風險↔易操作性。設計自然鍵,使膠片保持靜止,增加可觀察性,並定期進行比賽日-然後即使在暴風雨和故障的情況下,您的吹笛也可以預測和安全。

Contact

與我們聯繫

如有任何問題或支援需求,歡迎隨時聯絡我們。我們隨時樂意提供協助!

開始整合

Email 為 必填。Telegram 或 WhatsApp 為 選填

您的姓名 選填
Email 選填
主旨 選填
訊息內容 選填
Telegram 選填
@
若您填寫 Telegram,我們將在 Email 之外,同步於 Telegram 回覆您。
WhatsApp 選填
格式:國碼 + 電話號碼(例如:+886XXXXXXXXX)。

按下此按鈕即表示您同意我們處理您的資料。