幂等性

每个会修改数据的请求都必须使用,也是超时之后依然有效的唯一机制。

每个会修改数据的请求都必须携带一个幂等键:

Idempotency-Key: 5f1c3d9e-0b47-4c2a-9d3e-6a1b8c2f4e77

每个逻辑操作使用一个 UUID。不是每次重试一个——关键就在于重试时发送的是同一个键。

它能为你解决什么

你的请求超时了。连接在你的进程和我们的进程之间的某处断开了。你不知道付款是否已经创建。

用同一个键重试,你会得到同样的答复:第一次尝试时存储的响应,逐字节相同,状态码也相同。不会产生第二笔付款。

没有这个机制,“刚才那笔成功了吗”这个问题就没有安全的答案,因为唯一的查证方法——再创建一次——恰恰是你想要避免的事。

规则

常见错误

常见的错误是在重试循环内部生成键:

// 错误写法——每次尝试都是一个新操作
for (let i = 0; i < 3; i++) {
  await post('/v1/payments', body, { 'Idempotency-Key': crypto.randomUUID() });
}
// 正确写法——整个操作只用一个键,每次尝试都复用它
const key = crypto.randomUUID();
for (let i = 0; i < 3; i++) {
  try { return await post('/v1/payments', body, { 'Idempotency-Key': key }); }
  catch (e) { if (!retryable(e)) throw e; }
}

如果你有自己的订单号,就用它——order_1837 比新生成的 UUID 更适合做键,因为进程重启后它依然不变。

速率限制与幂等性

速率限制器被刻意安排在幂等层之前运行。如果在键被占用之后才写入 429,它就会成为这个键永久存储的答复,毁掉你用来查明超时的付款是否成功的唯一机制。所以 429 永远不会被存储,用同一个键重试依然有效。