Commit 3e62c5f0 by luoqi

docs(plan): 更正事故注释 —— 昨晚那条 OOM 日志是 6 天前的,叙事全错

09-05 用 journalctl -b -1 复盘,推翻了 09-04 当晚写进代码的三条事实:

① **当晚内核一条 OOM 都没打。** VNC 上那条
   `Killed process 945388 (node) anon-rss:6321668kB` 是 **2026-08-29 07:26:17** 的 ——
   pid/anon-rss/pgtables 与内核日志逐字段相同,内核 cmdline 带 console=tty0,
   它在 framebuffer 上留了 6 天没被刷掉。/var/log/messages 独立日志槽交叉验证同样只此 1 条。
② **这台机没有 swap**(Swap: 0B / vm.swappiness=0 / /proc/swaps 空),「swap 抖死」不成立。
③ **没有崩溃循环**:内核 `eth0: renamed from` 按天计数 Sep 03=0 / Sep 04=0,
   当晚一次容器启动都没有;journalctl -u docker 事故时段无条目。

真正形态是**无 swap 下的 page-cache refault 活锁**:匿名页吃满后只能回收文件页,
程序正文被刷掉、下一条指令又缺页读回(监控上 286MB/s 满速读、写≈0、CPU~15%)。
因为始终有文件页可回收,out_of_memory() 从未被调用 —— 没人被杀,也就没有任何自愈,
一路挂到人工硬重启,共约 11.8 小时(不是原注释写的 2.5 小时)。
对照 08-29:同机同水位同样吃到 6GB,那次走 OOM kill、10 分钟量级自愈。
**失败形态比内存本身更决定后果。**

死点(生产库写入痕迹重建,应用日志已随部署 --force-recreate 丢失):
  场景+预取 4,110s → 写入段仅 43s(550,011 行落库)→ 第 3 步 stale-close 最后一次提交
  20:05:49(全库最后一次写)→ 收尾 backfillMissing 一行没写、阶段耗时从未打出。

代码行为不改(分批本身经测试机实测有效:45 批 heapUsed 单调下降 462→107MB、零累积;
属性测试证明结果与批大小无关)。改的是**它的定位**:
从「本次事故的直接对策」改成「降低峰值 → 降低再次触发活锁的概率」,
并明确写出**不治**的三项:hits 交接的双份拷贝(≈2.1GB,现存最大未处理项)、
第 3 步整池 findMany、以及真正的兜底缺口(容器 MemLimit=0 + 无 swap ⇒
cgroup OOM 与内核 OOM 两道兜底全被绕过)。

顺带删掉 treatment-initiation-recall.scenario.ts 里一句自 2026-08-30 起就失真的注释:
「场景查询是串行的,同一时刻只有一条在跑,峰值可控」—— 那天起生产设了 conc=4,
RDS 上同时有 4 个 SET LOCAL work_mem='256MB' 的事务,DB 侧峰值不再可控。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent ce2d605a
Pipeline #3676 failed in 0 seconds