2026-09-27·by Sijie Wang#standmeet#architecture#design#events

message-loss-guarantees

丢信:每一跳的保证

上级:events

状态: 已在 v0.1.76 发布(2026-09-27)—— 设计与落地记录见 StandMeet 仓库的 docs/design/event-bus-outbox-webhooks.md。

从写入到送达,每一跳都要说清楚“掉了会怎样”。原则:至少一次,重复由幂等消化;真正送不到的进 discarded、告警、可手动重试,不存在静默丢失。

每一跳

每一跳的保证

跳掉了会怎样保证
① 写入 → outbox事务回滚则业务和事件一起消失不可能“改了没事件”或“有事件没改”
② outbox → 任务relay 中途崩溃领取、入队、打标记同一事务;崩了就整体回滚,重启后重领
② 唤醒丢了监听重连期间丢了一条 NOTIFY1 分钟的 events relay sweep 会戳醒 relay;代价是延迟,不丢事件
② 某行反复失败relay 在同一行上一直出错逐行重试;失败 5 次就隔离、放到一边并触发 events_poisoned;永远不会被删掉
③ 任务 → 执行worker 跑到一半被杀任务卡在 running 超过阈值会被救回重试(River rescuer);重复执行由幂等消化
③ 持续失败对方一直挂退避到上限 → discarded → 告警 + 面板可手动重试,不静默
④ 对方收了却丢了消费方返回 200 但自己没处理好我们无法控制。owner 可以用 events.list 查看事件流。不跑定时对账
重复至少一次必然有重复webhook-id = 事件 id,消费方按它去重;进程内订阅方幂等
邮件SMTP 250 只是“已接收”不是“已送达”Message-ID 是 <事件 id@standmeet>;退信处理不在本计划范围
给 owner 的通知超过突发上限同一 owner 每小时超过 5 条访客申请有意丢弃并记日志,洪水不会每次都给 owner 发信;每条申请在后台都看得见。重试保留自己的名额
没有邮件 supplier永远发不出去任务完成并记日志,不告警
数据库本身丢数据磁盘故障和业务数据同一套备份(backup.sh),不额外承诺

第 ② 跳在设计审查中出过一个真的丢信 bug:最初的 relay 按序号游标读 outbox,交错提交的事务会永远落在游标后面。修法是按行领取、不推游标:relay-claims-rows-not-cursor。

重复是代价,幂等来付

  • webhook:每次重试带同一个 webhook-id(= 事件 id),消费方按它去重。
  • 进程内订阅方必须幂等。一条注册表驱动的 UT 给每个已注册订阅方投递同一事件两次,断言效果只发生一次(no-bypass-by-structure)。
  • 搜索索引:upsert 天然幂等。
  • 邮件:SMTP 没有幂等键。顺序是“先发送,再标记”(notified_at、replied,或删掉预约通知行),发完还没标记就崩,重试会多发一封。邮件带 Message-ID <事件 id@standmeet>,多数邮箱会把同 ID 的重复合并。这是至少一次的代价,远比原来“失败即丢”好。

重试怎么排期、怎么分类,见 retry-has-one-owner。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →