付款页面转了很久,最后提示超时。你心里大概会冒出两个问题:到底成功没有?再点一次,会不会重复扣钱?
网络里的麻烦就在这里。没有收到成功消息,不一定代表事情没做成。可能订单已经建立,只是回来的消息在路上丢了。
为了解决这类问题,软件里有个生僻词:“幂等性”。可以把它理解成,同一件事重复尝试,最终不应因此多产生一份业务结果。
比如设想购买一份报告,第一次提交后,系统已经生成订单,但页面没显示出来。再次提交时,系统如果认得这是同一次购买尝试,就能找回原订单,而不是又新增一笔。
难点在于,它得认出“同一件事”。
Stripe 的接口使用幂等键识别同一请求的重试,还会检查同键请求的参数是否一致。这个键可以粗略理解为一次操作的识别号码。资料:Stripe
这和简单禁止重复点击不同。用户可能真的想买两份,也可能想给另一个人再买一份。如果只凭“同一用户一分钟内不能再买”来拦截,正常需求也会被挡住。
前端按钮变灰也只能解决一部分问题。网络重发、后台任务重新执行、两个请求几乎同时到达,都不一定经过那颗按钮。系统内部同样需要处理业务身份和最终记录之间的关系。
而且,创建订单、扣除额度、发送邮件,可能是不同步骤。订单只建了一次,后面的通知仍有可能发两次。一次购买涉及多少实际影响,就有多少需要辨认的操作边界。
幂等也不是永久记住一切。具体接口的识别记录有保存规则,例如 Stripe 文档允许在符合时间条件后清理键。很晚才到来的重复任务,不能仅靠一段临时记录兜住,还可能需要核对真实业务账目。
我喜欢这个概念,因为它承认了现实中的不确定:人会着急重按,网络会丢消息,系统会中断。可靠的软件,需要容纳这些正常发生的情况。
它不要求用户学会网络原理才敢点按钮。对用户来说,最朴素的期待始终是:我只想买一次,哪怕为了确认多按了一次,系统也应该有办法弄清楚我真正办的是哪一件事。