幂等与重试
每次实时调用都必须带 Idempotency-Key。它不是限流上的客套,而是让重试变得安全的前提。网络可能在供货方已经被请求之后才中断;没有这个 Key,想知道一次检测究竟跑没跑,唯一的办法就是再跑一次——再付一次钱。
填一个你能重新生成的值:UUID、订单号,或者本次工作的哈希。服务端以你的账号为作用域,所以不同用户之间不会撞车。
| 相同 Key | 请求体 | 结果 |
|---|---|---|
| 新 Key | 任意 | 正常执行并计费 |
| 相同 Key | 完全相同 | 返回已存储的响应,replayed=true,不会产生新扣费 |
| 相同 Key | 有改动 | 409 idempotency_conflict——Key 绑定在它第一次携带的请求体上 |
| 相同 Key | 完全相同,但首次调用还在执行 | 409 request_in_progress,并带 Retry-After |
Idempotency-Key 最长 191 字节,重放记录默认保留 30 天。
replayed 与 cached 不是一回事
Section titled “replayed 与 cached 不是一回事”它们在响应里长得像,含义完全不同:
| 字段 | 位置 | 含义 |
|---|---|---|
replayed | 顶层 | 这次调用根本没到供货方——你重放了一个 Key |
cached | 每条 result 内部 | 这个产品的结论来自结果缓存 |
重放的调用会完整复现原响应,包括每条 result 的 cached 值。两者都不改变你被扣了多少:重放不会动钱,缓存命中仍按产品的单次价格计费。
只有已定论的结果才会作为事实留存。失败和 unknown 不会被缓存——之后对同一标识、同一产品的检测会重新问供货方,而不是复述一个没有结论的答案。
失败之后怎么重试
Section titled “失败之后怎么重试”| 你看到的现象 | 该怎么做 |
|---|---|
| 超时,或 HTTP 500 | 用原来的请求体和原来的 Key 重发。结果是不确定的,这正是 Key 存在的意义 |
409 request_in_progress | 等 Retry-After,再用相同 Key 和相同请求体重发 |
409 idempotency_conflict | 这个 Key 已经绑了别的请求体。换一个新 Key——不要改完请求体还接着用旧 Key |
429 realtime_rate_limited | 等 Retry-After。什么都没提交,这个 Key 仍然没用过,可以安全重发 |
503 realtime_route_unavailable | 什么都没提交。稍后用同一个 Key 重试 |
唯一绝对不能做的事:超时之后用新 Key 提交同样的工作。如果第一次其实已经落地,你刚刚为同一次检测付了两遍钱。
本节涉及的错误
Section titled “本节涉及的错误”| HTTP | code |
|---|---|
| 400 | idempotency_key_required——缺少请求头 |
| 409 | request_in_progress——原始调用仍在执行 |
| 409 | idempotency_conflict——该 Key 首次使用时携带的是另一个请求体 |