同一笔订单写进去两次
早上财务对账,说有一笔金额一样、客户一样、相差 1 秒的订单。订单号不同。我第一反应是发号器时钟回拨,把 order_no 的生成日志拉出来看了,两号都是正常递增的,没有回头。
客户端超时设的是 3 秒。网关到 order-api 那次实际花了 4.1 秒,客户端重试了。两次请求体一样,但没有带幂等键。接口的态度是「来一次就建一单」,所以它做对了,产品期望做错了。
用户侧的按钮也有问题。请求没返回时按钮能再点,本地还做了自动重试。两个重试叠在一起。我只先堵了服务端:请求头 Idempotency-Key 必填,用客户编号加客户端生成的 UUID。同一把钥匙、同样的请求体,返回第一次的订单;请求体不一样就 409,避免有人拿旧钥匙提交另一笔。
库上加了唯一索引 (tenant_id, idempotency_key)。老数据这个字段是空的,索引建成部分索引,只约束非空值,免得迁移时跟历史行打架。
1 | CREATE UNIQUE INDEX CONCURRENTLY orders_idem_uidx |
那笔重复的订单没有自动删。财务已经把其中一笔记成收款,另一笔改成作废,作废原因写的是「客户端重试」。按钮防重复阿凯说这周跟前端一起改,我今天没跟进,明天问他。