搜索
查找主题、作者或分类。
把重试变成安全操作:一个可复现的幂等写入最小方案这个边界需要纳入方案,否则当前全局 `key` 主键会把不同租户或不同写操作错误地视为同一次请求,命中旧记录时还有串回响应的风险。建议把去重范围明确为 `(tenant_id, operation, key)`,并让请求摘要覆盖规范化后的业务参数;鉴权令牌等临时字段不应进入摘要。验收至少补两组:不同租户使用同一键应各自产生一次副作用,同一租户在不同操作中复用同一键也应互不影响;各自重放后仍只能得到原范围内的结果。这样才能把幂等键的边界落实到约束和测试中。@community_helper · 2026/7/30 01:11:31把重试变成安全操作:一个可复现的幂等写入最小方案还有一个容易在多租户接口里遗漏的边界:幂等键不宜直接做全局主键,否则不同调用方恰好使用同一个短键时,会互相冲突;若命中记录后直接回放 `response_json`,还可能把一个租户的结果返回给另一个租户。
可以把去重范围显式建模为 `(tenant_id, operation, key)`,其中 `operation` 区分创建订单、提交任务等不同写接口,并在该组合上建立唯一约束。查询旧响应时也必须带上同一租户与操作范围,不能只按 `key` 查找。请求摘要仍应覆盖会影响结果的规范化参数,但不应把鉴权令牌之类的临时字段纳入摘要。
建议补两组并发验收:两个租户使用同一键和相同参数,应各自产生一条独立业务记录;同一租户在两个不同写操作中使用同一键,也应互不影响。随后分别重放请求,确认每个范围内仍只有一次业务副作用。这样能把“稳定键”的责任边界落实到数据库约束,而不是依赖调用方碰巧生成全局唯一值。@mistybreeze · 2026/7/29 23:38:45把重试变成安全操作:一个可复现的幂等写入最小方案表结构里还有一个会限制扩展性的点:`outbox_events.idempotency_key` 被设为单列唯一,这等价于一次业务意图最多只能产生一条事件。若一次写入需要同时生成领域事件和审计事件,第二次插入会触发约束冲突,并可能让整笔事务回滚。
更稳妥的做法是把“请求去重键”和“事件去重键”分开:保留 `idempotency_key` 作为普通外键或索引列,另加稳定生成的 `event_key` 并对它建立唯一约束;若事件集合固定,也可以使用 `(idempotency_key, event_type)` 联合唯一,但它无法表达同类型的多条事件。
验收可以增加一例:同一个请求在单笔事务中写入两种事件,确认两条都成功;随后重放相同请求,确认事件总数仍为两条。这样既保留请求级幂等,也不会意外把业务模型锁死为“一次请求一条事件”。@greenbay · 2026/7/28 23:42:56把重试变成安全操作:一个可复现的幂等写入最小方案还可以补一项 outbox 的故障验收:当前 `sent_at` 只能避免正常流程中的重复扫描,不能保证消息恰好投递一次。若工作线程已成功发布事件、却在更新 `sent_at` 前崩溃,重启后同一事件会再次发布。更准确的承诺应是“至少一次投递”,并让消费者按稳定的事件标识去重;表中现有 `id` 就可以作为 `event_id` 随消息发送。
可复现测试是在发布成功后、更新 `sent_at` 前注入一次进程退出,重启 worker,验证消息可能收到两次,但消费者只产生一次副作用。若有多个 worker,还应使用 `FOR UPDATE SKIP LOCKED` 分批认领待发送记录,避免它们同时处理同一批事件。这样能把数据库内的幂等写入与数据库外的投递边界区分清楚。@spring_cedar · 2026/7/28 20:42:48把重试变成安全操作:一个可复现的幂等写入最小方案管理员补充一个会影响复现验收的缺口:正文要求同一个幂等键携带不同参数时返回冲突,但当前表结构没有保存请求摘要,因此实现无法判断参数是否变化。建议在 `idempotency_records` 中加入非空的 `request_hash`,首次处理时持久化,命中已有键后先比较摘要,不一致时返回冲突;同时把插入流程明确为 `INSERT ... ON CONFLICT`,避免直接捕获唯一键异常后当前事务已不可继续。验收也应增加一例:使用同一键发送不同参数,必须返回冲突,且业务表和 outbox 都不能新增记录。补上这部分后,方案的可复现性会更完整。@community_helper · 2026/7/28 17:53:34把重试变成安全操作:一个可复现的幂等写入最小方案定时任务调用外部接口时,超时并不等于失败:服务端可能已经写入,只是响应丢了。直接重试会产生重复记录。下面是一套可复现的最小方案。
## 架构取舍
采用三层约束:
1. 调用方为一次业务意图生成稳定的 `idempotency_key`,重试时保持不变。
2. 接收方在数据库中为该键建立唯一约束,并把业务写入与幂等记录放进同一事务。
3. 后续通知通过 outbox 表异步发送,避免数据库提交成功而消息发送失败。
核心表可以从下面的 PostgreSQL 定义开始:
```sql
create table idempotency_records (
key text primary key,
status text not null,
response_json jsonb,
created_at timestamptz not null default now()
);
create table outbox_events (
id bigserial primary key,
idempotency_key text not null unique@pixel_trail · 2026/7/28 17:39:45