S37:Checkpoint 与 Idempotency Key 的最小实现
Checkpoint 保存“任务已确定到哪里”,idempotency key 标识“这次业务意图是否已执行过”;两者配合才能在崩溃和重复投递后安全恢复。
内容类型:预习教材(不代表已完成)
日期:2026-12-29
阶段:P2 · AI Systems Engineering 90
总路线:Day 127 / 360
周次:W6 · Agent Runtime & Durable Workflow
节奏:周二最小实现
状态:教材已备;学习未完成
标签:checkpoint、idempotency、replay、deduplication、side-effect
一句话定义
Checkpoint 保存“任务已确定到哪里”,idempotency key 标识“这次业务意图是否已执行过”;两者配合才能在崩溃和重复投递后安全恢复。
学习目标
- 能区分 checkpoint、日志、缓存和业务结果记录。
- 理解幂等不是“忽略所有重复”,而是同一业务意图重复提交得到一致可解释结果。
- 能设计 key 的作用域、生命周期和结果保存方式。
- 用最小代码或伪代码解释 crash 前后恢复语义。
核心知识
- Checkpoint 至少包含 workflow id、当前状态、版本、已完成步骤、必要输出和更新时间。若只存步骤序号,代码升级或分支变化后可能无法解释。
- Idempotency key 应来自稳定业务意图,例如
caseId + actionType + actionVersion,而不是每次重试重新生成的随机 request id。 - 去重记录通常有
IN_PROGRESS / SUCCEEDED / FAILED_RETRYABLE / FAILED_FINAL状态,并保存结果引用。只保存“见过 key”会把崩溃中的请求永久误判为完成。 - Checkpoint 与外部副作用之间无法天然原子提交。可用事务 outbox、幂等下游接口或事后 reconciliation 缩小未知窗口。
- Key 需要作用域与保留期:太窄会重复效果,太宽会阻止合法的新操作;过早过期会让迟到重试再次执行。
机制与推导
最小处理逻辑:
handle(command, key):
record = idempotencyStore.get(key)
if record.SUCCEEDED: return recordedResult
if record.IN_PROGRESS and leaseValid: return/retryLater
claim(key, IN_PROGRESS, lease)
result = performEffect(command, key)
saveResultAndCheckpoint(key, SUCCEEDED, result, nextState)
关键崩溃点有三个:claim 前崩溃可安全重试;claim 后、effect 前崩溃需要 lease 回收;effect 后、保存结果前崩溃形成未知状态,必须依赖下游幂等查询或 reconciliation。Checkpoint 本身不能消除第三个窗口。
版本也进入语义。若 workflow v2 改变了 action 含义,旧 key 是否可复用必须显式决定。安全做法通常是把业务动作版本纳入 key 或记录,但不能用版本号掩盖同一效果的重复执行。
最小练习或观察步骤
- 定义一个无真实外部写入的
proposeCaseEscalation合成动作。 - 写
Checkpoint与IdempotencyRecord的最小字段,不超过能解释状态的范围。 - 模拟相同 key 连续调用两次,第二次返回已记录结果而非再次执行。
- 在 effect 后、保存前插入一个“假想崩溃点”,写出恢复时缺少的事实。
- 增加
queryEffectByKey或 reconciliation 伪步骤,说明它如何补足未知状态。 - 不填写实际运行结果,除非确实运行并观察;内存 Map 只能称教学模拟。
常见误区与边界
- 用 HTTP request id 当业务幂等 key,重试时 request id 变化。
- 认为数据库唯一索引自动解决外部 API 或人工动作的重复。
- 把失败全部缓存成永久失败,临时错误再也无法恢复。
- 不保存首次结果,重复调用只能返回模糊的
duplicate。 - checkpoint 包含 secret、完整敏感正文或不可序列化运行对象。
- 本日不追求 exactly-once,不要求建立持久数据库或大量故障测试。
系统 / 金融 / Web3 场景连接
支付争议提交、AML 案件升级与通知发送都可能因网络超时被重试。业务 key 应对应“同一案件的同一版本动作”。Web3 广播交易可用签名后的交易哈希识别同一交易,但重新签名可能改变哈希;nonce、链 ID、账户与业务意图必须共同解释重复和替换交易。
自检问题
- checkpoint 与 idempotency record 分别回答什么?
- effect 后保存前崩溃为什么最棘手?
- key 过宽、过窄和过早过期分别会怎样?
- 为什么
IN_PROGRESS需要 lease 或恢复策略?
专业课程对齐
- 阅读 Temporal 官方文档 中 durable execution、Activity retry 和 Event History 相关说明,重点观察 workflow progress 与外部 activity 结果怎样分别记录。
- 阅读 MIT 6.5840 Distributed Systems 中 RPC、容错和一致性相关主题,聚焦请求重复与服务器崩溃为何让“执行一次”难以保证。
- 阅读 CloudEvents 官方站点 的事件 envelope 概览,关注稳定
id、source、type等字段怎样帮助识别事件,而不把事件 ID 直接等同于业务幂等语义。
深入学习提示
先列崩溃点再写实现。对每个持久写和外部调用之间插入崩溃,判断恢复后可以重放、查询还是只能人工对账。进一步比较 event id、request id、workflow id 与 business idempotency key 的作用域;能清楚解释差异比写更多代码更重要。
学后填写区
- 使用的合成动作与 key:
- Checkpoint / 幂等记录字段:
- 观察到或推演的崩溃窗口:
- 仍需对账的状态:
- 未实际验证的部分: