下单成功但收据没到,用户点「重发」收到两封,密码重置邮件在链接过期二十分钟后才送到——一次成功的测试邮件完全说明不了集成在故障下会怎样。作者列出九个只在生产暴露的错误:把 API 返回成功当成投递成功(接受、投递、进收件箱、被阅读是四个不同阶段);在 HTTP 请求里同步发信,让服务商延迟拖垮结账;超时重试却不带幂等键,用户收到两张收据。修法是应用侧投递记录、事务内写 outbox、唯一约束加可恢复的租约、以及跨重试保持不变的幂等键。
🔑 核心要点
提交成功、收件服务器接受、进收件箱、被读,是四个不同事件,SMTP 本身不提供阅读回执。
业务变更与 outbox 记录必须在同一个事务里提交,否则进程崩溃就留下一封永远不会发出的邮件。
超时不等于被拒:不知道结果的重发必须复用同一个幂等键,每次重试都生成新 UUID 等于没做幂等。
数据库唯一约束与厂商侧幂等解决的是两个不同边界,两个都要有。
💡 金句
“A timeout means your application does not know the outcome. It does not prove that the provider rejected the message.” 译:超时意味着你的应用不知道结果,它并不能证明服务商拒收了这封邮件。