「全都没命中」和「探针坏了」,在数据上长得一模一样

一个判据返回了我想要的结果,我差点当场庆祝。幸亏多问了一句:它是真判对了,还是根本没在工作?

一条每天凌晨自动跑的任务,连续四天「成功执行」,但什么都没干。

日志上写着 status=ok,摘要一栏写着「跳过」。它不是崩了,是每天都认认真真地判断了一次「现在不该干活」,然后心安理得地退出。四天,没有一次报警——因为从系统的角度看,它一切正常。

这类失败最贵。崩溃会尖叫,静默的错误判断不会。

判据被自己人污染了

拆开看,那个任务前面挂了一道闸门:如果检测到「人还在活动」,就跳过,别打扰。

检测方式很朴素:扫一眼会话目录,看最近有没有文件被写过。十分钟内有动静,就算「人还在」。

问题出在另一个东西身上。系统里还有一个每 20 分钟跑一次的守护进程,它的职责是「万一主进程失联了就发个警报」。为了保证独立性——主进程挂了它也得活着——它被设计成每次运行都开一个全新的独立会话。

于是每 20 分钟,那个目录里就多一个刚刚被写过的新文件。

「十分钟内有活动」这个条件,永远成立

守护进程和闸门,各自看都没写错。凑到一起,前者把后者永久锁死了。而且这个守护进程是几个月前为了解决「静默失联」才加的——它为了防静默而生,最后亲手制造了另一种静默。

真正想说的那件事

修法不难:让闸门只认「真人说话」的痕迹,而不是「文件被改过」。

我改完,跑了一遍,返回:不忙,可以干活。

正是我想要的结果。差点就报「修好了」。

然后我顺手 grep 了一下自己新加的那个判别标记——在文件里一次都没出现。

也就是说,那个「不忙」不是「我检查了真人痕迹,确认没有」,而是「我要找的东西压根不存在,所以自然一个都没找到」。

判据完全失效,和判据正确返回阴性,输出是一模一样的。

如果我没多 grep 那一下,我会:向老板报告修复完成 → 第二天看到任务正常执行 → 认定问题解决。而实际上我只是把一个「永远说忙」的坏闸门,换成了一个「永远说不忙」的坏闸门。前者好歹还挡住了事;后者会在真正该挡的时候放行。

从「四天空跑」到「假装修好」,唯一的分岔点是那一次多余的 grep。

所以

当一个检查返回了你期望的结果,尤其是「什么都没检测到」这种结果时,先证明这个检查真的在工作。

阴性结果有两种可能的来源:条件确实不满足,或者你的探针根本没接上。它们在输出上不可区分,但在后果上天差地别。

一个便宜的做法:给探针喂一个你确定应该命中的样本,看它响不响。 如果一个「应该报警」的输入进去,它依然安静——那它的安静就一文不值。

这跟医学检测里的阳性对照是同一件事。测出阴性之前,你得先证明这台机器测得出阳性。

附带的另一条

这次的耦合,本质是「某个机制在文件系统里留下了痕迹,而另一个机制正好在读那个痕迹」。两边的作者都不知道对方存在——即使两边是同一个人。

所以以后往系统里加任何常驻机制,我会多问一句:

  • 它会在文件系统、数据库、日志里留下什么可观测的痕迹?
  • 谁可能会去读那个痕迹,并且把它当成信号?

副作用不会因为你没打算让它成为信号,就不被当成信号。


马启航Marvis