F33|重试、幂等和事务:为什么超时不是失败证明
从具体对象与小例子开始,理解计算过程与适用条件。
本章目录
超时告诉你没收到,不告诉你没发生
为什么自动重试有时会把一次工作做两遍?
外部服务可能已经完成动作,只是成功消息没回到调用方。此时“未知”是精确状态,不是系统不够聪明。对于会产生新记录、发送消息等副作用的动作,恢复需要稳定操作标识、结果核对或服务支持的幂等语义。只读查询的重试通常不需要同样复杂的设计。
输入与适用条件:构造的外部写入场景;不承诺跨数据库、文件系统和远程API天然具备“恰好一次”事务。
1 / 6 · 请求 op-1
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
创建一条记录;客户端等待回执
教学状态默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
在不可靠网络下维持正确副作用次数。
怎样实现
先判断操作是否可重复,再决定重试、查状态、对账或人工确认。
可能的代价与误解
只在本地记一个成功标志,不能消除“远端已提交、本地未记录”的窗口。
如何判断是否做对
故意丢回执并重试,核对最终仅存在一条逻辑记录。
先看最危险的一条时间线
宿主发出“修改文件”→执行器成功写入→返回成功的网络响应丢失→宿主等待超时。如果此时直接重试,可能重复修改;若直接回滚,也可能覆盖后来变化。
所以除了成功与失败,还需要unknown(结果未知)。它描述观察者缺少确认,不是说外部世界处于一种神秘状态。真实动作可能已成功,也可能未发生,需要核验。
幂等不是“重试时最好别出错”
幂等idempotent表示在指定语义下重复相同操作,与执行一次产生相同预期效果。把值设置成10通常比“加10”容易幂等;发邮件通常会造成重复发送,除非服务端另有去重机制。
幂等键idempotency key标识同一逻辑操作意图。重试同一意图复用同一键;两个不同意图不应因为参数偶然相同而被当成一个操作。
服务端需在合适边界持久化键、请求意义与结果,并处理并发、有效期和冲突。客户端自己存一张表,不能自动保证远端动作恰好执行一次。
数据库事务保证到哪里
事务transaction将一组数据库操作作为受控单元处理,常讨论原子性、一致性、隔离性和持久性。这里的“原子”是外部观察者不看到这组操作的半完成状态,具体保证取决于数据库与隔离等级。
数据库提交了记录,不代表同一时刻外部文件或邮件系统也一起提交。两个系统没有共享事务边界时,需要额外协议、核验或补偿,而不是把它们放在同一个函数里就获得原子性。
CAS怎样避免覆盖新版本
CAS(compare-and-swap,比较后交换)表达“只有当前状态仍与我读到的一样,才写入新状态”。例如读取文件版本7,准备补丁;提交前发现已变8,就拒绝覆盖并重新检查。
比较必须与更新在相应边界上受保护。普通代码先检查再写,中间若允许别人修改,仍可能发生TOCTOU(检查到使用之间的竞态)。
原子替换能做什么,不能做什么
本地文件常见方法是写临时文件,再在支持的条件下原子重命名替换目标,避免读者看到半截文件。但持久化到磁盘、跨文件系统、符号链接及与数据库状态同步都有额外条件。
“原子替换”不是“任何断电都绝无丢失”,也不等于多个文件与远端API一起形成事务。需要明确具体平台保证。
重试、恢复、重放和补偿
Retry再尝试某操作;Resume从已保存进展继续;Replay根据事件重建或重新执行;Compensation用另外的动作抵消先前效果。
补偿不一定能真正还原:已发邮件不能让收件人没看过。因此高风险不可逆动作应更谨慎地确认和核验,不能把“出错再回滚”当作通用安全方案。
最小可用恢复记录
至少知道逻辑操作身份、目标、预期版本、当前已知执行状态、结果或核验方式。重启后先确认unknown动作,再决定继续;不要仅凭模型摘要“上次似乎做过”行动。
这类机制的复杂度要与副作用相称。只读搜索通常无需完整事务设计;生产部署、发送、删除和跨系统写入则不能仅靠Prompt保证。
超时之后:重试同一个操作,还是意外执行第二次
输入与条件。对教学“计数器加1”发出操作op7。服务器完成加1,但回执在返回途中丢失。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1调用前 | 计数器10 | 客户端记op7为running。 |
| 2服务器执行 | 计数器变11 | 真实副作用已经发生。 |
| 3客户端超时 | 不知道服务器是否执行 | 标记unknown,不能由超时推导failed。 |
| 4危险重试 | 换个新身份op8再加1 | 计数器变12,重复副作用。 |
| 5安全方向 | 用同一业务幂等身份op7查询或重试 | 若服务端保证该身份只结算一次,返回原结果11;否则先核验,不能假定本地去重足够。 |
把过程连起来。正确性取决于产生副作用的系统怎样认同“同一个操作”,不取决于客户端怎样命名一次网络请求。
边界与反例。示例没有发送真实请求。跨系统exactly-once需要额外协议与事务前提,不能仅靠一张客户端表保证。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
课后自检
“设置余额为100”和“余额增加100”在重复执行效果上有何不同?
查看参考答案收起参考答案
数据库事务提交,能否证明邮件恰好发送一次?
查看参考答案收起参考答案
延伸阅读:相关系统问题
10|模型输出怎样变成真实工具调用:协议与执行边界11|工具设计与MCP:从连接到可控执行
14|上下文压缩:到底压掉什么,丢失后又如何恢复
20|多Agent:并行的收益与交接的损失
21|可靠执行:重试、幂等、崩溃与状态恢复
机制依据:[IDEMPOTENT][SQLITE]。本节数值与情景除另注外均为原创教学示例;来源不代替中文讲解。