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

saturation-degrades-gracefully

饱和降级:慢下来、排队、告警

上级:events

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

饱和时慢下来、排队、告警,不丢数据、不崩进程、不拖垮访客请求。每种饱和一条 UT,用注入的故障模拟,断言降级行为。已实现的饱和套件覆盖:连接池耗尽、磁盘满且 relay 退避、端点 429 冷却、Meili 挂掉后排空、积压暴增告警、等待者上限、队列饥饿、任务超时。

饱和状态

场景、行为与 UT 断言

饱和场景期望行为UT 断言
连接池耗尽worker 等连接有超时,到点记为 retryable;访客请求的连接余量始终保留把池占满后投任务:任务不崩、转 retryable;预留的请求连接仍可用
worker 全忙新任务排队,不开新 goroutine;各队列互不饥饿webhook 队列全堵时 index 队列任务仍按时完成
下游限额打满(端点 429)snooze 到 Retry-After,不消耗次数;端点记上 failing_since,新投递等过 5 分钟冷却连续 429 不会把任务耗到 discarded;冷却期内没有额外请求打出
我们自己的邮件限流命中snooze 到窗口结束,不丢弃超过限额的邮件在下个窗口全部发出,数量一致
磁盘满 / Postgres 拒绝写业务写入和事件一起失败,返回可读错误;relay 退避(2 秒起翻倍,封顶 1 分钟)而不是忙转模拟写失败:没有“改了没事件”;relay 重试间隔递增
Meili 整个挂了索引任务退避重试(Meili 客户端自带重试已关);写入不受影响;恢复后积压自动排空恢复后所有积压任务 completed,没有 discarded(在重试窗口内)
积压暴增(批量导入)同一批内同主体的索引任务合并(webhook 不合并);未扇出超 1000 条或超 5 分钟触发 events_backlog任务数 ≤ 上限;告警字段被置位
请求内等待者满了超过上限的请求直接返回回执,不排队第 65 个等待者立刻得到 indexed: false
CPU 饱和 / 任务超时任务硬超时取消、转 retryable;不影响其它队列超时任务被取消且可救回

每一行背后的机制在兄弟节点里:snooze、冷却与退避见 retry-has-one-owner;队列上限、连接池预算、超时和等待者上限见 concurrency-control;合并与告警阈值见 storage-bounds;业务写入和事件同一事务见 two-sources-of-events。这组 UT 计入 events-test-plan。

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 →