题目重点:为会产生副作用的 API 设计幂等键记录,区分第一次执行、处理中、成功重放和同键不同请求冲突。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练请求有 tenantId、endpoint、idempotencyKey、payload;同一作用域内键在 TTL 内唯一,作用域/TTL 必须与客户端协议明确。
- 对同键且请求指纹相同的完成请求返回原 status/body;同键但 payload 不同返回冲突,而不是默默复用。
- 业务副作用与幂等记录需处于同一事务或借助 outbox/状态机达到一致;单机内存 map 仅可演示状态转移,不能抗重启。
解题路线
- 先规范化会影响结果的请求字段、计算稳定指纹,查询唯一键 (tenant,endpoint,key)。数据库 insert-if-absent 成功者成为唯一执行者;失败者读取已有指纹和状态。
- 状态可为 IN_PROGRESS、SUCCEEDED、FAILED_RETRYABLE/FAILED_FINAL;同键同指纹且 SUCCEEDED 返回已保存响应,IN_PROGRESS 返回可重试的处理中状态或等待结果。
- 执行者在事务中完成业务写与状态/响应保存;若外部支付等服务无法同事务,向下游传递独立幂等键并用 outbox/对账处理未知结果,不能靠本地 map 保证外部只扣一次。
- 同键不同请求指纹拒绝,避免客户端误把不同订单当重试;TTL 过期后的语义需限制客户端最长重试期,过期删除与业务可回放时间一致。
- 并发相同 key 通过唯一约束/锁防双执行;执行者崩溃后的 IN_PROGRESS 用租约/恢复对账区分已成功与未执行,不能无条件重跑。测试交错请求、响应丢失、进程重启、TTL 边界和外部副作用未知结果。
手动走一遍
创建订单请求 K 第一次提交成功,但客户端没收到响应;同一 payload+K 重试,服务器返回原订单 ID,不再建第二单。另一个 payload 复用 K,应得到冲突。两个相同 K 同时到达时,唯一约束只允许一个成为执行者,另一个等待或得到处理中回应。
规模与取舍
幂等表读写按唯一索引预期 O(log N),状态存储量约请求率×保留 TTL;热点同键会在执行者完成前积累等待者。外部副作用恢复成本依下游幂等/查询 API,无法从题目推断固定 SLA。
易错点与追问
容易失分的边界
- 只存 key 是否见过,不存请求指纹、执行状态和响应。
- 业务副作用成功但幂等记录失败,重试会重复执行。
- 把响应超时当业务失败,贸然再次调用外部付款。
- 同 key 的不同 payload 静默返回旧结果。
- 内存 map 重启后丢失,却承诺跨进程幂等。
继续深挖
- 下游付款成功但本地超时,如何确认与恢复?
- 是否缓存 4xx/5xx 响应,哪些错误可重试?
- 幂等键过期后客户端迟到重试怎样处理?
- 跨区域双写如何防同键并发执行?
