Appearance
Worker 失败后怎样重投而不重复副作用:Idempotent Worker、Retry Queue 与 DLQ
前置:Run/Node 发给 Worker 后会发生什么:Queue、Ack、Redelivery 与 At-least-once
核心问题:同一个 dispatch_id 被消费三次时,哪一层负责保证业务效果只提交一次,什么时候应该进入 DLQ?
Broker Delivery ID 不是业务 Operation ID
同一个逻辑 Node 可能被 broker 重新发布成新消息。真正稳定的 identity 应来自 Runtime,例如:
text
operation_id = run_id + node_id + logical_attemptBroker 的 message/delivery id 只用于诊断 delivery,本地幂等记录围绕 operation_id。
Worker 入口先查事实,再做副作用
text
receive message
→ load node + operation record
→ already SUCCEEDED? return existing result / ack
→ not eligible? ack/discard stale message
→ claim current lease
→ execute
→ durable commit result
→ ack如果 Tool 是远端副作用,还要使用 Tool 支持的 idempotency key 或 reconciliation;Worker record 只能防止“已知成功后再次执行”,无法消除“远端成功但本地未知”的窗口。
Retry Queue 应保存“下一次何时、为什么”
一次失败至少归类:
text
TRANSIENT → retry after backoff
RATE_LIMITED → retry respecting Retry-After / budget
DEPENDENCY_DOWN→ retry/defer
INVALID_INPUT → terminal, do not retry
PERMISSION → terminal / require intervention
UNKNOWN_SIDE_EFFECT → reconcile firstRetry message/envelope 要携带稳定 operation identity、attempt、next_eligible_at 和错误分类。不要每次重试重新生成业务 idempotency key。
DLQ 不是垃圾桶
进入 DLQ 的消息至少需要:
- run/node/operation identity;
- 原始 message metadata;
- normalized error + attempt history;
- last known runtime version;
- correlation/trace id;
- 是否可能已产生外部副作用。
并且必须有:告警、查询、人工决策、reconciliation、redrive。
Redrive 不是“点一下重新发消息”。在重驱前要重新检查:
text
Run 是否已 cancel/terminal?
Tool schema 是否变化?
权限/approval 是否仍有效?
side effect 是否需要 reconciliation?Failure Modes
Retry 创建新的 Operation ID
后果: 下游把每次 attempt 当新业务动作。
处理: logical operation 稳定,attempt 单独递增。
DLQ 只存 payload,没有执行证据
后果: 无法判断能不能安全 redrive。
处理: 保存错误、版本、trace、side-effect uncertainty,并把 redrive 当显式 command。
如何验证
| 场景 | 必须成立 |
|---|---|
| 同消息 redeliver 3 次 | 已成功 operation 不再次产生副作用 |
| 429 后 retry | operation id 不变,attempt 增加,遵守 delay |
| invalid input | 不进入无限 retry loop |
| DLQ redrive 前 Run 已 cancelled | 不重新执行 |
自测
- 同一个
dispatch_id被消费三次,哪一个稳定 identity 应贯穿重试,才能保证业务效果只提交一次? - 一条消息已经多次失败且确认不是瞬时错误。什么时候应进入 DLQ,而不是继续无限 retry?进入后还需要保存什么诊断信息?