主题
异步任务状态库、消息队列与 SSE 之间的一致性边界怎么设计?
题型:深入 · 明确哪些能强一致,哪些只能重放、对账或最终收敛。
建议回答
状态库应保存 Job 和节点的权威业务状态,消息队列传递“执行某次尝试”的意图,SSE 则是状态变化的用户侧投影。Worker 完成节点后,先用条件更新提交状态和产物引用,再发布事件;若担心数据库提交成功但事件未发出,可以使用 Outbox 或状态变更扫描补发。客户端不能依赖每个瞬时进度事件都送达,重连后应读取快照,并按 eventId 或 sequence 去重增量事件。
消息一般是至少一次投递,重复执行由 attemptId、幂等键和条件迁移吸收。事件顺序只在同一 Job 内保证,跨 Job 不要求全局有序;completed 必须表示后端状态与产物都已提交,连接正常关闭本身不能代表任务成功。取消要从命令接口写入 cancel_requested,再传播给排队任务、Worker 和可中止的模型请求;无法立即中止的外部生成需要允许结果回传,但不得覆盖已取消任务的有效状态。
慢消费者不能让 Worker 或全局事件总线无限缓存。可合并高频进度、设置每连接缓冲上限并丢弃可重建的中间进度,但 failed、canceled、completed 和产物就绪等关键事件必须可查询或重放。
关键权衡
- 持久化所有细粒度事件有利于回放,但会增加写放大和存储成本。
- 先发 SSE 再提交状态体验更快,却可能让前端看到最终无法查询的“幽灵成功”。
- 强制中止可节省资源,但某些外部模型不支持取消,只能隔离迟到结果并对账成本。
易错点
- 不要承诺数据库、队列和浏览器之间存在端到端 Exactly-once。
- 不要把 SSE 连接断开解释成任务取消,断线和业务取消是两个状态。