重试之前,先确认操作有没有发生
超时只能说明客户端没有及时收到结果,不能证明服务端没有完成操作。让重试安全,需要为一次用户意图确定稳定身份,并保存可复用的处理结果。
向下阅读
点击提交后,页面一直没有回应。用户不知道请求是没发出去、正在处理,还是已经成功,只是响应在路上丢了。此时再点一次,看起来像是在重复同一个动作;对服务端来说,它却可能是一笔新的请求。
超时描述的是通信状态,不是业务结果。网络断开时,数据库也许已经写入;浏览器刷新时,支付或留言也许已经完成。把“没有收到确认”当成“什么都没发生”,是重复创建记录、重复扣款与重复触发任务的常见起点。
一次意图,需要一个稳定的身份
可以在用户开始一次操作时生成幂等键,并让这次操作的所有重试沿用同一个键。服务端收到请求后,先检查这个键是否已经有处理记录:如果已有结果,就返回原结果;如果正在处理中,就返回可理解的处理中状态;只有第一次到达的请求才执行实际动作。
幂等键应该对应用户的一次意图,而不是每次网络尝试。若重试时重新生成,服务端看到的仍是几个彼此无关的新操作。反过来,用户修改内容并再次提交时,通常已经是新的意图,应当使用新的键。
记录结果与执行动作要一起考虑
如果业务动作已经提交,幂等记录却没有保存,下一次请求仍可能重复执行。若先写入“成功”,实际动作却在随后失败,客户端又会拿到一条虚假的成功结果。记录状态与业务变更需要有明确的一致性边界;在数据库内可以用事务保证,在外部服务之间则需要设计处理中、完成和失败的恢复路径。
并发请求也不能被忽略。两个携带相同键的请求可能几乎同时到达。唯一约束或等效的原子占位,能让其中一个成为执行者,另一个读取处理状态,而不是让两边都通过“先查再写”的检查。
前端要诚实表达未知状态
等待中的按钮可以暂时禁用,减少误触,但这不能单独保证数据安全。请求超时后,界面应该承认结果尚未确认,并提供沿用原操作身份的重试方式。对访客来说,“正在确认”比“失败了”更准确,也能避免他们为了恢复而重新提交一遍。
幂等键不是身份验证,也不应该包含秘密。它只负责关联重复尝试;权限检查、输入校验和资源归属仍然需要各自成立。记录还需要设定保留时间,并避免不同用户或不同操作意外共用同一身份。
重试机制的目标不是让请求永远再来一次,而是让同一次意图即使经过多次传输,也只产生一次业务效果。先把结果的不确定性说清楚,再把重试连接回原来的操作,可靠性才真正落到访客能感受到的地方。