KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

03. 租约、幂等和重复投递怎样一起工作? — keel 龙骨

可靠投递通常是 at-least-once:消息可能重复,worker 可能在外部调用成功后崩溃,旧 worker 还可能在新 worker 接管后迟到。专业系统不假设“恰好一次”,而是让重复和迟到都变成可验证的状态转换。

可靠投递通常是 at-least-once:消息可能重复,worker 可能在外部调用成功后崩溃,旧 worker 还可能在新 worker 接管后迟到。专业系统不假设“恰好一次”,而是让重复和迟到都变成可验证的状态转换。

租约不是锁

Lease 是带过期时间的处理权和 fencing token:

pending --claim(token, until)--> claimed
claimed --ack(token, before until)--> fired | failed
claimed --clock > until--> 可被另一个 worker 接管

确认必须同时匹配 wakeup_id、worker_id、lease_token 和未过期时间。只检查 worker 名称会让旧实例在接管后覆盖新实例的结果。项目的 ack() 对过期 token 明确拒绝,并用测试证明 worker A 的迟到确认不会改变 worker B 的状态。

幂等边界

幂等键要绑定业务意图和参数指纹,而不是随手生成的请求 UUID。事件、外部写操作和通知分别需要自己的 key;一个运行可以有多个投递 attempt,但同一个业务 effect 只能落一次。载荷相同应返回已有结果,载荷不同必须报冲突。

这与 Real-world Execution 中的 fingerprint、receipt、unknown result 和 reconciliation 直接相连:请求超时不能证明外部系统没有执行,必须先进入 unknown 再查询或补偿。

副作用的放置

LangGraph interrupts说明恢复会重新执行节点,因此 interrupt 前的写操作必须幂等。更稳妥的边界是:先持久化意图和 outbox,再执行外部 adapter;外部确认后写 receipt。课程项目用标准库演示状态和去重,生产替换点是事务 outbox、消息 broker 和真正的 fencing/租约服务。

本章实践

  1. 重复调用 schedule(),验证事件和 wakeup 数量不增加。
  2. 用两个 worker 领取同一个请求,让第一个 lease 过期。
  3. 让旧 worker 在接管后 ack,断言收到明确错误且最终状态只出现一次。
  4. 为一个外部 action 生成两个不同参数的相同 key,断言冲突而不是静默覆盖。

本章检查点

下一章:知识库怎样从“能搜到”走向可引用?

进入 keel 阅读