处理BUG的那些往事:难以复现的巧合
在多年的程序员生涯中,我处理过各式各样的软件问题。其中有些BUG虽已历史久远,但却印象深刻。我不想让这些经历无声无息的湮没在回忆中,因此决定把它们写下来。
虽然谈软件BUG不可避免的会讲技术,但我不想把文章写成纯技术讨论。就像《人件》书中说的:“我们是以团队和项目以及其它紧密结合的工作小组的形式来从事这项工作的,我们主要在从事人类交流的业务。我们的成功源自于良好的、与所有此项工作的参与者之间的人际交往,同样我们的失败原因也是由于糟糕的人际交往。”——所以我更想把这些经历写成鲜活、生动的故事。对于涉及到技术的部分,我会尽可能用通俗的写法,力争让没有专业基础的外行也能看懂。
始于春节的事故
某年的春节假期前两天,我的同事W在开车回老家的路上接到了公司打来的电话,告诉他海外某国的通信设备在升级时出现了业务中断,影响很严重,要求立即回公司处理。
虽然春节被叫回公司加班肯定是件糟心事,但W也知道海外一线若不是实在不得已也不想在这个时候得罪人。于是他调转车头,从回老家改成了回公司。
刚开始的时候,W还想着快速搞定问题然后回家过年。可他发现海外那边的设备每次升级时都会业务中断,而且中断后重启软件都恢复不了,只能用硬件重启的方法强行恢复。由于不知道问题会造成哪些影响,只好暂停升级操作,先拖延到春节结束。
实验室7*24小时攻关
春节结束后,开发团队和测试团队的员工一回到公司,就立刻开始着手分析问题。
一般来说,这种问题只要能在公司的实验室里复现出来,很快就能分析出原因。可诡异的是,实验室里搭建了与海外一模一样的组网,使用一模一样的配置,进行了多次升级,怎么也重现不出来那个故障。
眼看着几天过去了,海外设备每次升级必然会出现的问题,实验室里从来不出现。问题的分析毫无进展。各级领导高度重视这个问题,要求以最高优先级进行攻关。
于是,我们这些人开始7*24小时的连续攻关,每人工作24小时后休息几小时,把所有能做的尝试全都试一试。
谁都希望这种工作状态尽早结束。但是一方面,问题没法复现,就没有突破口;另一方面,我们尝试分析代码,但在几百万行的代码中大海捞针,也没找出任何能引发这种故障的可能性。
进展僵在了这种状态下。
空降的测试妹子
大领导眼看又是几天过去了,仍然没有进展,觉得该拉点其他部门的人过来帮忙。于是领导找其它研究所协调了一个测试人员,让她立刻坐飞机来我们这里协助。
这位测试人员是个女孩子,她在一天傍晚到达我们实验室。刚到实验室,领导就拉着我们几个过去,说你们赶紧交流一下信息。
于是我们几个睡眼惺忪的汉子来到妹子旁边,跟她说我们做过哪些分析。然而,在和该妹子交流时,我们很快发现她以前几乎没有用过这款出问题的设备,连配置方法都不怎么熟悉。聊着聊着,大家脸上开始出现苦笑。妹子又问了几个问题后,也从我们的表情中读出了异样,觉得有点不好意思。旁边的领导看出不对劲了,一摆手,说“行了行了,那就各自先开始干活吧。”
我们回到各自工位继续分析代码和尝试复现,心里想着就当这个测试妹子是来体现团结协助精神的吧。
不出意料的,那妹子不时的发消息过来,说在配置的时候碰到了问题。每当这时我们就会心一笑,互相递个眼神:“XX,你去教教她怎么用。”
又过了一阵子,妹子好像沉寂了,不再问问题了,我们估计她对于基本操作学得差不多了。
大约在晚上八、九点钟左右,我们接到了妹子的电话,她说弄出来了一个现象跟现网事故有点像。大家又是会心一笑,互相递个眼神,说:走,去看看这个妹子在捣腾些什么。
然而,我们在她的设备上看了一会儿之后,表情越来越严肃。因为无论从哪方面看,这一次真的跟现网的故障一模一样。我们开始迅速的对着代码进行深入分析。
测试妹子看到我们围在她的座位上聚精会神的研究,不好意思的笑了笑,弱弱的问了句:“还有什么需要我协助的么?”
我抬起头,梗住了半天不知道说什么,最后憋出一句话:“你现在可以扬名立万了。”
……
后来,不出意料的,这个做测试的女孩子当年拿到了最高绩效,而且在那之后多次得到嘉奖,职级一路晋升。
只是至今也没人可以解释:一群精通产品代码的开发人员想了好多天都无法复现的问题,是怎么被一个刚接触设备的女孩子在几个小时以内复现出来的。
真相大白
一旦找到问题复现方法,接下来的进展都是顺水推舟。当天晚上我们就知道了问题根因,那天晚上成为了这次攻关的最后一次通宵。尽管很疲惫,可大家开心极了。
为什么之前这个问题复现不出来,是因为它需要几个BUG以非常巧合的方式组合在一起,才会出现。
这款设备的软件有这么几层:业务配置模块接收用户的配置并发送给下层;芯片配置模块根据业务配置计算怎么配置芯片,并且用数据库表来记录芯片的配置状况;芯片操作模块直接操作硬件芯片实现需要的功能。

几个BUG分别是:
BUG1:升级时业务配置模块意外的触发了“配置同步”的流程,要求下层模块完整核对一遍配置。通常来说,在用户没有改配置的情况下,是不应该进入这个流程的。但如果只是多一次配置同步也不会导致什么问题。
BUG2:芯片配置模块在某些特定操作后,数据库里应当删除的表项没有被删除,使得存在表项数据残留。通常这不会导致什么问题,因为残留在数据库里的表项并不影响硬件芯片的功能,用户感受不到。而且,一般在配置同步时会清除残留的表项。
——BUG2是问题能否复现的关键点。之前一直复现不出问题就是因为没有构造出残留的表项,而那个测试妹子却通过一系列操作巧合的弄出来了。
BUG3:在配置同步过程中,芯片配置模块会根据新老配置的对比检查哪些表项需要删除,哪些表项需要修改。然而本来应该被删除的表项,却误入到了修改流程。通常这也不会导致什么问题,因为若用户删除了某个配置,这个配置就不再使用了,即使对应的表项被修改了也感知不到。
——但是,BUG3和BUG2组合在一起就出现问题了。这时“应当被删除”的表项并不是用户删除的配置,而是BUG2产生的残留表项,它指向的是正在使用的业务。BUG3使得本该被删除的表项触发了修改流程,在芯片里写入了一条全为0的配置,导致业务中断。
BUG4:数据库管理表项时无法区分“未使用的表项”和“值为全0的表项”。因此,一旦全0的表项被写入到芯片,以后的配置同步再也无法修正这个问题,即使软件重启也不能恢复。
——这是一个设计上的缺陷。由于软件工程师经常给未使用的属性赋值为0,时至今日这种设计缺陷也不罕见。
每个BUG单独出现时,都不会引发用户可感知的功能错误,仅仅只有软件内部状态的异常。可倒霉的是,在海外那些设备上,经过特定的操作步骤,4个BUG恰好同时被触发,产生了这次事故。
要说我从这次事故中得到的启示,那就是:架构设计的不严谨,是不可能靠集成测试的加强来弥补的。
如果非要加一句,那就是:不要低估新手的威力。
- 点赞
- 收藏
- 关注作者
评论(0)