ARC 3理解复制、分区与修复第 10–14 章

第 13 章 —— ChangeStream 的背压与溢出策略

本章目标: 本章完成后,开发者能够为 ChangeStream 显式选择容量、事件来源和溢出策略,并把丢弃、阻塞与回调超时变成可观察结果。

学习目标

  1. 区分集合收敛与 ChangeStream 事件投递。
  2. 解释有界缓冲为何是稳定性边界。
  3. 比较 DROP_OLDESTDROP_NEWESTBLOCKERROR
  4. 根据消费者用途选择本地/远端事件来源与失败策略。

前置条件

案例进度

Metrics Adapter 订阅 route-hints,把变化投给一个变慢的诊断面板。集合本身仍在正常更新,但消费者速度低于生产速度。团队需要选择:丢旧事件、丢新事件、短暂阻塞,还是明确报错。

缓冲区满时的策略选择

ChangeStream 的有界缓冲与溢出策略

ChangeStreamOptions 默认包含本地和远端事件,缓冲容量为 1024,溢出策略为 DROP_OLDEST,阻塞上限 1 秒,订阅回调上限 5 秒。默认值使流保持有界,但默认值并不替代业务选择。

策略 满时行为 更适合 主要代价
DROP_OLDEST 丢最旧事件,接纳最新事件 UI 刷新、当前状态提示 中间变化不完整
DROP_NEWEST 保留已有队列,丢新事件 先到先处理的短队列 当前状态可能更陈旧
BLOCK 等待消费者腾位直到 timeout 可承受短抖动的受控消费者 生产线程等待;超时仍会丢
ERROR 抛结构化溢出异常 不允许静默损失的验证/控制路径 调用方需要处理失败

这些策略改变的是观察流,不是已经提交的集合值。ChangeStream 丢一条事件后,register.get(key) 仍可能返回最新值;消费者若需要当前状态,应重新读取集合,而不是假设事件序列完整。

来源过滤也是契约

事件 origin 区分本地操作与远端 delta。只观察 includeLocal=true 适合确认本节点动作;同时观察 local/remote 适合诊断收敛;两者都关闭没有意义,因此构造器会拒绝。

对贯穿案例:

慢回调不能无限占住推送线程

push subscriber 的回调也有超时。超过 subscriberCallbackTimeout 时,流通过 onError 暴露失败。把用户代码放在无限制回调中,会把「一个面板慢」扩散成整个本地更新路径的资源问题。

稳定做法是:回调只做快速转交;耗时 IO 进入独立有界执行器;同时记录 overflow 和 callback timeout 的低基数指标。

反例与故障注入

把容量设为 1,连续发布两个变化但不消费:

这个实验刻意不讨论哪一个「总是正确」。正确答案由消费者是否需要最新视图、顺序前缀还是显式失败决定。

实验

cd submodule/dsm
mvn -q -pl dsm-runtime -am \
  -Dtest=BufferedChangeStreamTest,InMemoryDsmRegisterTest \
  -Dsurefire.failIfNoSpecifiedTests=false test

然后完成 overflow-cases.json:为四种策略写出 retained event、producer outcome 和 required metric。

实验验收卡

字段 内容
运行命令 上述 Maven 聚焦测试;node --test tests/chapter-assets.test.mjs
输入或故障 容量 1、两条事件、慢消费者和超时回调
可观察结果 四种策略保留/拒绝行为不同;overflow 与 callback timeout 可见
证据等级 E2:集合和缓冲区行为测试
本实验未证明 ChangeStream 可靠历史投递、跨进程 exactly-once、生产容量足够

回顾

下一步

第 14 章把所有失败窗口叠在一起:网络分区时两个节点都可能返回局部成功,而 repair 不能撤销已经发生的业务承诺。

证据链接