2.2 案例分析(第 1 天)
某天早晨你去公司上班,觉得这是一个郁闷的早晨,总想着“好讨厌啊”。为什么会觉得厌烦?因为你所参加的项目陷入了岌岌可危的状况,俗称“死亡行军”。
“但仔细想来,不是死亡行军的项目至今为止还没有遇到过”,你如此思考着,“可哪里出问题了呢?”你边想边去上班。
2.2.1 问题 1 :重要的邮件太多,无法确定处理的优先顺序
你想着想着就到了公司,打开邮件客户端,便看到下面这样小题大作的邮件主题。
【重要】系统报错导致工作无法进行,请立即着手处理!
【加急】正式环境发生重大故障!麻烦立即处理。
【需要处理】客户环境发生重大退化。请尽快处理。
【正式环境故障】【重要】【加急】【立即处理】收到用户投诉。请立即处理。
看到这些五花八门的邮件主题,虽然你已经感到烦躁无比,但还是要着手确认邮件的内容。但是所有的邮件主题都标有【重要】或【加急】等字样,完全不知道应该从哪里着手,所以只能依次阅读收到的邮件并着手处理。
2.2.2 问题 2 :没有能用于验证的环境
为了确认邮件中反映的问题,首先不得不构建和发生故障的正式环境相同的环境。因为这个项目一开始就没有准备用于验证的环境。
再现正式环境中发生的问题,必须首先再现与正式环境相同的环境。“要是有一直和正式环境保持同步状态 2 的验证用的环境,故障的再现就能简单多了”你一边想着,一边无可奈何地开始在本地机器上搭建和正式环境一样的环境。
2 实际上要再现的环境包括服务器台数、网络构成以及数据库事务数据,但要将全部内容都做得和正式环境完全一致是非常困难的。本书中指的是应用程序层面上的再现,也就是指程序的代码、数据库模式(schema)、中间件的配置等。
2.2.3 问题 3 :用别名目录管理分支
由于到昨天为止一直在开发新功能,自己本地机器上的代码和正式环境的代码已经相差甚远,所以只好将本地机器上的正在开发的代码所在的目录重命名为“XXX_new”,再从版本管理系统下载最新的代码。不这样的话,版本管理系统上的最新代码就会和本地的代码混淆(图 2.1)。
图 2.1 将目录重命名
其实公司从数年前就开始使用版本管理系统了,但由于开发人员中没有人精通版本管理系统的使用方法,所以并没有对其加以有效利用。
版本管理系统上经常只有最新的代码。那是因为没有有效地利用分支功能,为了区分本地机器上的代码和版本管理系统上的最新代码,只能用给目录重命名这种方式。合理地利用版本管理系统就应该能解决这些问题,但又不知道该如何操作。加上因为不知道标签(tag)的使用方法,发布时的系统快照也没能保存下来。而且版本管理系统上的最新代码还有可能已经和正式环境的代码有了差异,毕竟向正式环境进行发布已经是两周前的事情了。
因为没有使用标签,所以正式环境发布的版本对应版本管理系统中哪个版本 3 已经无从知晓。没有办法,只能麻烦负责运维的人将正式环境的代码取了下来。
3 这里使用了“版本”这个词,在 Subversion 中指的是“版本号”(revision),在 Git 中指的是“提交哈希”(commit hash)。各版本管理系统的叫法不相同,所以这里统称为“版本”。
自己试着比较了最新的代码和正式环境中的代码,的确有所差异 4 。在最新代码的环境中,故障似乎已经无法再现(图 2.2)。
4 这个例子假定的是 PHP 等脚本语言。Java 或 C# 的情况下,除了 Play Framework 等一部分情况例外,通常部署的都是编译后的二进制文件,无法简单地进行比较。
图 2.2 正式环境和开发环境产生差异
无法再现故障的代码就没有意义,所以你决定继续使用从正式环境获取的代码。用这份代码在本地机器上构建环境可不是一件容易的事情。还必须保证数据库模式和正式环境完全相同,而重新制作数据库也是一件非常麻烦的工作。
2.2.4 问题 4 :重新制作数据库比较困难
为了使数据库和正式环境完全相同,需要下载版本管理系统的代码库中的 SQL,并在自己的开发环境上构建数据库。刚才还在使用的用于新功能开发的数据库的模式已经和正式环境的数据库模式不一样了。仔细看了下下载下来的 SQL,总觉得有一些 SQL 并不适用于开发环境。
不知道该执行哪些 SQL 才能构建和正式环境完全相同的数据库。是否有必要记录下执行了哪些 SQL 呢?没办法,姑且只能根据推测执行 SQL,想办法试着构建环境了。也不知道是否能正确地再现正式环 境,不过也只能这样了。
数据库构建完后,为了再现和正式环境相同的状态,还需要导入数据。可是在将正式环境的数据 5 导入刚才构建的数据库时,不知为何部分数据的导入失败了。应该是数据库模式不同的原因吧。总觉得正式环境的模式和刚才在本地构建的模式有所差异。虽然不清楚是为什么,但鉴于时间也所剩无几了,只能自己用眼睛来寻找差异,并修改 SQL,然后重新构建数据库了。重新导入数据,这次终于成功了。
5 这里的数据是指正式环境的主数据(master data)。关联数据(transaction data)因为数据量过大,应避免直接导入本地环境。
代码和数据库都准备好后就可以试着运行程序了。赶紧依照报告中的描述操作一遍,却发现程序无法正常运行,并且和报告中描述的现象也不一样。
为了再现故障而试着搭建环境,但因为出现了其他问题,结果以失败告终。恐怕还是因为数据库模式和正式环境不一样吧。以为仔细点就能避免上述问题的,结果还是不行。没办法,只能将正式环境的数据库原封不动地复制到开发环境了 6 。
6 这个例子中,正式环境的数据量并不是太大,所以可以复制。如果是大规模服务的话,复制是不现实的。为了避免上述问题,需要考虑数据库的管理。
终于搭建好了和正式环境完全相同的环境,突然发现已经是中午了。已经过去了两个多小时,可是连邮件中反映的问题是否能再现都还没确认。
当天下午和同事们分头把早晨 4 封邮件中的问题全部再现了,用了 1 天的时间修正这 4 件 bug,并将其提交到版本管理系统。做完这些已经快要到末班车的时间了。今天一整天都在忙着处理这些问题,本来应该进行的新功能开发也完全没有着手。算了,明天再干吧,回家了。
