今天发现了一个多线程引起的bug。然后进一步体会到,这东西太容易出问题了!
首先要说明的是,出问题的代码可不是一般人写的,是由一个叫EPAM systems的世界知名外包公司的人写的。
这些java程序员个个经验丰富,心高气傲。貌似base在乌克兰,工资比起我们这些中国民工可要高不少的。
然而事实证明,写多线程代码,再牛的人,心思稍微不缜密就会出错。
简单介绍一下代码内容:
1,工程是由多个project组成,会被定时运行。每个project都有记录自己状态的一个公共对象。
2, 其中一个incremental project一旦调用就不会停止。每天早上凌晨有一个maintenance project会被运行做些archive,housekeeping等工作。这两个project互斥。因此,maintenance project启动时,会先将incremental project停止。maintenance project运行完毕后,会再次启动incremental project。
3,incremental project 有失败的风险,因此有retry的功能。失败后会尝试5次,如果全部失败,则整个程序会停下来。但是一般5次之内可以成功。
4,incremental project与其他project不是互斥的。即同时可以有多个project同时运行。因此,每个project都在自己的线程中运行。
现象:
CI最近一直失败。查看log后发现retry的功能似乎失效了。incremental一次失败后程序就停止了。
一直忙今天终于有点时间查查怎么回事儿了,问题出在多线程上。
首先在CI的环境中,为了把每个project都测试一遍,maintenance project并不是每天凌晨跑一次,而是5分钟就运行一次。
而最近由于incremental project遇到一些特殊因素,很容易失败。
于是bug就凸显出来了:
maintenance project 试图启动,它首先将incremental project的状态设置为“正在停止”,然后等待这个project停下来。
此时很不幸的,在另外一个线程里incremental project遇到特殊问题,失败了。于是retry机制开始生效,但这里有一个条件:
只有当incremental project上次运行失败时,才开始retry (这个条件合情合理)。
具体为:当这个project当前状态为“失败”时,才会retry。
正常情况下,incremental project第一次失败时,会将状态记为“失败”。而此时,很不幸的被maintenance 改成“正在停止”了。
于是retry机制就失效了。
在正常情况下,maintenance 的调度一天才一次,一般不会遇到incremental正好也失败。
但在CI环境,这个错误却几乎每次都出现,结果就是程序总是运行一段时间后就停止了。
可以说EPAM这些人在多线程交互,防止线程间干扰方面做的很好。代码方面没有任何问题。
但是没有人想到这万分之一的可能性。
目前这个bug,还没想好怎么解决。
代码框架已经完成了,这种问题很棘手,因为牵一发而动全身。