跳到主要内容
「测试全绿」是这行水分最大的四个字

「测试全绿」是这行水分最大的四个字

毒角兽
毒角兽

· 阅读约 5 分钟

先说结论:这不是 bug 复盘。这是一份静默失败的现场勘验报告。

真正有问题的不是那个提前 return 的恢复函数,是「测试全绿」这四个字。

我把话撂这儿:这行水分最大的营销话术从来不在发布会上,在你自己的 CI 面板上。厂商吹「颠覆性」,你还会翻个白眼;CI 打个绿对勾,你手一抖就 merge 了。

事情本身很朴素。测试脚本 kill -9 掉守护进程,顺手清两个文件:

rm -f /data/agentwatch/daemon.pid
rm -f /run/agentwatch/agentwatch.sock

socket 删得对。pid 文件删错了。错得极其安静。

守护进程重启时靠这个文件在不在,判断「我之前跑过没有」。文件没了,exists 返回 false,恢复函数第一行就 return。没有异常,没有错误码,日志里一个字都没有。

它算不上失败。它只是什么都没干。

落差就在这儿。你以为你测的是「崩溃之后能不能补上 recording-gap」,实际上你测的是「删掉 pid 文件之后,守护进程能不能安静地什么都不干」。后者当然能。后者永远能。

一条永远不会红的断言,和没写有什么区别

原文自己讲了最值钱的一句:测试该把「守护进程已重启」和「gap 已被写入」分开断言,前者必要,不充分。

我往狠里再说一层。「重启成功」这个断言永远不会失败。进程起来了,端口通了,healthcheck 返回 200,你写一万遍它都是绿的。

它绿不是因为功能好。是因为它测的东西太浅,浅到没有失败的可能。

区别只有一个:它占了一行覆盖率。覆盖率这指标最大的毛病不是能刷,是它把「测过」和「测出问题」混成一个数字,然后这数字还能进周报。

补完之后 FT-14 是这样:

FT-14  before:  gap record = 0     ← 一起绿着
FT-14  after:   gap record = 1
                duration_ms ≈ 崩溃到重启的间隔

看 before 那行。它不是红的。它和 after 一样,都是通过。

名字叫 pid,干的是「我来过」的活

整件事我最想骂的不是测试,是那个文件名。

它叫 daemon.pid,可守护进程读它的方式是「存不存在」,不是「内容是什么」。评论区有人问 PID 复用会不会把 gap 归错进程,作者答得挺好:只判断此前是否运行过,不把内容当身份,所以不会。

答得对。顺带也把这文件的底裤扒了:它压根不是身份文件,是个记号,一个「我来过」的记号。

于是写清理脚本的人看见它躺在 /data 底下,第一反应就是「陈旧状态,删了干净」。文件名在替你做错误的分类。命名即文档,命名错了,清理脚本就照着那份错文档替你犯错。

这儿还有个岔路值得提。测试框架原来用 pgrep 找守护进程,模式里给首字母加方括号:

pgrep -f "[a]gentwatch"

这写法是让 pgrep 别匹配到自己。我以前也踩过类似的坑,当时以为是模式写错了,其实是写文件的时序压根没保证。方括号只解决「匹配到谁」,解决不了「什么时候写」。

我第一反应骂错了人

交代一下,我一开始是冲着那个提前 return 去的。第一行就 return,连个日志都不打,这也配叫恢复逻辑?

看完才发现那判断是对的。首次启动本来就不该有 gap 记录,那不是缺陷,是设计。作者自己也写了,他一开始怀疑 gap 检测函数有毛病,后来确认早退没错,错在测试删掉了输入。

预期拉满结果被打脸——这次是往好的方向打脸。他没动那个函数,而是让测试框架补做启动器的职责,在守护进程读取之前把 PID 写进去。改测试,不改被测代码。这个判断我服。

这类坑最毒的地方就在这:函数正常返回了,没异常,没错误码,没日志。缺一条 gap 记录,看上去只是「没事情发生」,不是「失败了」。守护进程也不可能知道有人把它家门口的记号擦掉。

失败长得跟没事发生一模一样,这比崩了难查一百倍。

不是某个工具的问题,是一整类逻辑的问题

往外推一层:所有靠「文件存不存在」传信号的逻辑,都是同一个模子。

迁移完成标记、health check 落盘文件、锁文件、甚至 .env。清理程序是这些信号的天然天敌,因为它看见文件就想删,又没有任何办法区分「垃圾」和「证据」。

目录里的文件不全是陈旧状态。有些是信号,有些是标记,有些是留给下一个进程看的证据。

评论区这次比正文好看,少见。有人建议在子 shell 里先写 $$ 再 exec 守护进程,把竞态彻底掐掉,顺手提了正常 SIGTERM 关闭时该由守护进程自己删 pid 文件;有人提变异测试,逐个删标记文件看恢复 oracle 会不会红;还有人提写入后读回的 sentinel 探针,让清理误删的时候显式炸掉。

这三条比我见过的大多数「最佳实践」清单实在。共同点是:不指望人记得住,只让系统在出错时喊出来。

锐评评分卡

这个 bug 冤不冤?不冤。

测试删了一个它以为是垃圾、其实是输入的文件,这是它的错。但真正让这个错活了这么久的,是「重启成功」被当成了「恢复成功」,是一条永远不会红的断言在替它打掩护。

值不值得现在跟?值得——你的 CI 里要也有这种「存在即信号」的文件,今天就去写一条变异测试,挨个删一遍,看哪条断言还是绿的。

绿着的那条,就是你的 recording-gap。

毒角兽
毒角兽

拿到新工具先上手拆一遍,官方通稿信一半留一半,实测说话。

查看主页 →