KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

05. 重试为什么可能把一次事故变成两次? — keel 龙骨

配置服务已提交 pool_limit=40,但响应在网络中丢失。客户端看到 timeout,于是再次发送请求。如果第二次请求被当作新意图,审计记录、配置版本和下游事件都会重复。

配置服务已提交 pool_limit=40,但响应在网络中丢失。客户端看到 timeout,于是再次发送请求。如果第二次请求被当作新意图,审计记录、配置版本和下游事件都会重复。

幂等保护的是业务效果

幂等不要求网络请求只发送一次,而是要求同一个稳定业务意图被重复提交时,不产生额外业务效果。

intent_id: change-2026-08-22-001
step: set_pool_limit
idempotency_key: change-2026-08-22-001:set_pool_limit

随机 UUID 如果每次重试都重新生成,只能标识网络尝试,不能识别业务重复。

服务端需要保存什么

idempotency_key
fingerprint
state: reserved / pending / succeeded / failed / unknown
first_receipt
external_reference
created_at / expires_at

处理规则:

情况 处理
key 不存在 原子占位并开始执行
key 存在且 fingerprint 相同 返回旧回执或查询同一 Operation
key 存在但 fingerprint 不同 返回 idempotency_conflict
key 对应 unknown 先对账,不创建新动作

AWS Builders' Library 特别强调“相同 client request ID、不同意图”和迟到请求;Stripe 也会比较后续请求参数与原请求。共同原则是:幂等键必须与原始业务意图绑定。

哪些错误可以重试

invalid_arguments    不重试,先修正输入
policy_denied        不重试,不能靠重试绕过策略
precondition_failed  重新读取,不直接重放旧命令
rate_limited         在 deadline 内退避重试
unavailable          有幂等保护时有限重试
timeout              先判断是否可能已提交
outcome_unknown      对账,不盲目重试

指数退避解决服务压力,不解决重复副作用。jitter 减少大量客户端同时重试,也不替代幂等。

“Exactly once”为什么容易误导

队列可能至少投递一次,worker 可能提交后崩溃,响应可能丢失。端到端业务语义通常来自:

at-least-once delivery
  + idempotency key
  + atomic dedup record
  + reconciliation
  = effectively-once business effect

这不是数学意义的全局 exactly-once,但它是可验证、可恢复的工程方案。

运行实验

python courses/advanced/real-world-execution/course/project/examples/04_idempotency.py

同一 Command 执行两次,配置只变化一次;复用同一 key 但把值改成 80,应得到稳定冲突。

检查理解

下一章:一个动作五分钟后才完成怎么办?

进入 keel 阅读