Hunting a Bug

· 873 words · 2 minute read

这两天在追踪一个非常不可思议的bug - 我们的系统里会定时收到报警邮件说: 数据表是空的。而且,这个报警的内容和条件都是我设定的。

我的算法逻辑很简单,根据数据表的内容做相应的事情,而数据表为空是个极端情况。系统分为生产环境和测试环境,我事先分别向两个环境中的数据表注入了数据。因此,收到这种警报意味着可能代码实现有错误。其中有个重要的细节: 生产环境只是开启的时候有少量报警,然后就没有了。而测试环境确实一直在报警,甚至连报警的间隔时间也和我在算法里面的设置非常一致。

重复看了多遍代码,结论只有一个:这绝对不可能发生。逻辑验证了很多遍,无论异常路径怎么走,理论上都不可能发生。而事实放在眼前,于是只能老僧入定般冥思苦想。竞争?死锁?某个API没有用对?

同时还要帮助PM进行一些客户支持。还好用git,本地专门有个分支。

中间发现、又填了一些坑。每发现一个就增加一份不自信,每减少一个坑却又增加了寻找一个可以解释这个现象的理由的难度。直到今天下午,为了防止远程环境干扰本地调试,我把测试环境里部署好的服务删掉了。然而,不可思议的是: 报警邮件还是以之前那个节奏发送。仿佛幽灵一般。天生异象,必有妖孽。

重新检查日志。发现了另一个至关重要的细节:定期报警中的一个ID和刚部署上去的ID不一致(为了方便诊断,我为不同节点上跑的对等的单例生成了一个ID)。这意味着,一直报警的线程并非新部署上去的服务导致。顺着这个思路抽丝剥茧, 终于发现,之前这个神秘的报警是另外一个端对端测试造成的。我们会在服务上线之前,专门在这个环境下做端对端测试。我没有意识到同事这几天为了上线, 做了端对端测试,而且端对端测试环境中的那张表,确实是空的。这也解释了为何生产环境后来没有报错,因为生产环境中我是部署后注入数据的。而测试环境中,其实一直没有报错。

真相大白。有点像央视《走进科学》那样无厘头。然而复杂环境下的调试真的需要丰富的专业知识和福尔摩斯般的推理能力。为了这个问题,自己常常念念有词think out of the box。中午和同事聊前东家的黑科技,聊非确定性芯片,聊 Mill CPU。看起来,奏效了。

comments powered by Disqus