本课问题:客户端没有得到成功确认时,怎样判断状态并避免盲目重复操作?
前置:知道请求、写入、提交、响应是不同阶段。
本课交付:两种相同失败提示的不同结果、一次保留操作编号的重试,以及有限的信任声明。预计 40–60 分钟。
暂时只研究样本 D。记录当前列表里已有的测试文字和数量,登记预测:如果点击保存后出现失败,刚输入的文字是否一定没有写入?
实验页面有两个受控故障:甲、乙。它们都会返回同样的 HTTP 503 和同样的提示。先不要读故障分支,选择故障甲,输入独特文字 D-故障甲 并保存。
页面应该显示“结果未确认”,保留待核对操作。随后点 D 的“再读一次”,检查有没有同一条文字,而不是只看提示是否变成绿色。
记录甲的观察后,先预测并点击“重试同一次保存”,核对成功后的列表,把它记为新基线;重试的含义在下文展开。随后选择故障乙,使用另一段文字 D-故障乙。先登记预测,再保存、记录失败提示,再读回。
两次失败的状态码和提示相同,但读回结果可能不同。因此,“客户端看见失败”不能直接等同于“服务端没有发生写入”。
甲在 store.save 之前返回 503,未写入新记录。
乙先完成写入提交,再返回相同 503,客户端没拿到成功确认,但记录已经存在。
这里使用 503 人工模拟“未得到成功确认”,并未模拟真实 TCP 丢包;真实断网还会出现其他错误和时序。
发起请求 → 服务收到 → 写入 → 提交 → 响应 → 客户端得到确认
↑ 甲停止 ↑ 乙不给成功确认
“写入是否发生”和“确认是否送达”是两个问题。真实系统里,响应可能丢失,客户端也可能在接收之前关闭。调查时要找与你要证明的承诺对应的边界。
注意:本实验在故障后先保持旧列表,直到你明确读回。旧列表不是最新状态的证明,只是最后一次成功读取的结果。
选择“重试同一次保存”。这个按钮沿用原来的文字和 operation 编号,且关闭本次故障注入。若 D 已存在同编号、同文字,服务返回已有结果,不再增加一条。
在故障乙之后,预期重试成功但数量不增加。若故障甲尚未写入,重试才会添加一条。记录前后数量、文字和编号,三者一起核对。
普通“保存”代表新操作,会产生新编号;同样文字也可能是读者有意再次保存,因此不能仅凭文字相同就随意去重。
若同一编号却换成另一段输入,实验会拒绝。稳定的编号必须配合明确的输入契约,不能拿任意编号覆盖另一件事。
D 把操作编号和记录一起保存,能覆盖本实验的相同编号重试。它没有证明所有网络、并发、外部副作用和长期过期策略下都“恰好执行一次”。例如支付、发邮件等副作用,需要继续调查各自的事务边界。
同样,“再读一次能看到记录”说明了此次读取结果,不证明备份可恢复,也不证明其他用户无权读取。本实验没有账号系统。
面对未确认状态,界面不应把它伪装成“已保存”,也不应假装知道一定没写入。可用的做法是保留输入和操作编号,允许核对与重试,把已知范围说清楚。
如果让 AI 改这个界面,不要只要求“把错误提示做得友好”。给它具体验收:故障甲/乙怎样显示、哪些信息不能清空、重试是否重复、什么情况下恢复成功状态。生成代码之后仍由你核对这些行为。
提交两次相同提示、不同写入结果的证据;提交一次乙之后的同编号重试,证明未新增重复记录;列出两个本实验未验证的保证。
最后写一句有限承诺,例如:“在本机实验、相同实验编号和数据目录、同操作编号与输入下,D 支持本次失败后的去重重试;尚未验证磁盘损坏与外部系统副作用。”这比笼统地说“系统可靠”更有用。