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,我们也会在 Telegram 回复您。
WhatsApp 可选
格式:+国家代码 + 号码(例如:+86XXXXXXXXX)。

点击按钮即表示您同意数据处理。