与效果一起去重
至少一次投递意味着消费者必须假设存在重复。把消息 id 与业务变更放在同一事务。插入冲突说明消息已处理,直接确认;事务失败则两者回滚,broker 可以重投。
BEGIN;
INSERT INTO processed_messages (consumer, message_id) VALUES ('payments', $1);
UPDATE accounts SET balance = balance + $2 WHERE id = $3;
COMMIT;
唯一约束是仲裁者,而不是内存集合。使用 broker 的消息 id 或生产者提供的事件 id,不要用负载哈希。
保持窗口有界
已处理 id 不能永久保存。保留时间长于 broker 最大重投窗口,按时间分区,并归档或删除旧行。如果消息可能在窗口后投递,broker 配置必须限制重投。
处理非事务效果
邮件、webhook 和第三方 API 无法加入数据库事务。使用同一事务写入 outbox 行,再由中继带重试地发送效果。外部调用应使用供应商支持的幂等键使其幂等。
验证重复路径
预期结果:同一消息 id 投递两次只留下一个业务效果,事务失败则不留效果,以便后续重投成功。用运行处理器两次并查询效果表的集成测试确认,并在业务更新与确认之间模拟崩溃。检查去重行和效果一起提交或回滚。