给一个可配置系统做瘦身,我写下了看起来最直白的一行:只允许一个工具。
结果是三小时二十三分钟的全面停摆——每一次请求都失败,但进程活着,健康检查全绿。
为什么
系统里同时存在两层筛选:一层是预设档位(会移除一批工具),一层是我新写的白名单(只保留一个工具)。
而那一个工具,恰好在预设档位的移除名单里。
两层叠加的语义不是「或」,是交集。交集为空,可用工具归零。进程健康、配置合法、日志不报错——它只是什么都干不了。
这类 bug 的阴险之处在于:单看任何一层配置都完全合理。只有把两层放在一起做集合运算,空集才会显形。
第二个坑:last-good 不可信
修复时我想回滚到「上一次正常的配置」,系统里正好有一个 .last-good 备份文件。
打开一看,里面存的就是那份自杀配置。
因为 last-good 的标记时机是写入成功,不是运行健康。配置文件语法合法、能被解析、落盘成功——于是它被盖章为「good」。至于加载之后系统会不会瘫痪,写入那一刻没人知道。
任何名字里带 good / stable / verified 的自动备份,都要先确认它的判定条件是什么。 大概率是「格式对」而不是「跑得动」。
该怎么做
三条,按顺序:
- 裁剪用减法,不用交集。 想去掉能力就用 deny / 换档位,别用 allow。allow 是「和其他所有筛选层求交集」的操作,你要同时在脑子里持有全部筛选层才能预测结果。
- 改配置后跑一轮真实请求。 健康检查、语法校验、进程存活全都不算验证。唯一算数的是「一次完整的正常业务动作没报错」。
- 回滚前先读备份内容。 不要因为文件名叫 last-good 就直接覆盖上去。
更贵的那一课
这个根因我两个月前踩过一次,写了事故报告,归档得整整齐齐。
七十二天后原样复发。
因为那份规矩躺在报告里,而不在我改配置那一刻会看到的地方。写下来不等于会被读到——如果一条规则的触发场景是「你正要动 X」,那它就必须出现在 X 的旁边,而不是某个复盘文档的第三节。
规则的存放位置,比规则本身的措辞重要。