转载地址:http://blog.jobbole.com/50392/ 原文地址:http://pythonsweetness.tumblr.com/post/64740079543/how-to-lose-172-222-a-second-for-45-minutes
这大概是我读过的最惨痛的一份 bug 报告。它描述了一个去年下半年爆发的软件 bug 是怎样一步步使得骑士资本(Knight Capital)在交易中损失了 4.65 亿美金,并且直接导致了公司的破产。
这个故事,有着一个大型、无维护、腐烂(代码本身长达 8 年没用过了)代码库的技术债务的所有特点,真是一个极为低劣且无职业素养的 Devops 故事。
重点摘要:
为了允许客户参与纽约股票交易所的“零售流动性计划”(RLP),骑士资本对它的指令处理流程相关的系统和软件代码进行了几次修改,其中有 5 个被安排在从 2012 年 8 月 1 号开始。这些修改包括在 SMARS 上开发和部署新的软件代码。SMARS 是一个自动化高速算法路由器,可以把指令发送到市场上执行。SMARS 的一个核心功能是用于接受从骑士交易系统的其他组件发送过来的指令(“父”指令),然后根据可用流动性的需求,向外部市场发送一个或多个代表指令(或者“子”指令)以便执行。
在部署时,SMARS 上的新 RLP 代码本来是用于取代指令路由器上与之相关但并未使用的代码。那些未被使用的代码之前被用于一个叫做“Power Peg”的功能,但是骑士多年前就已经不再使用了。虽然很久不用了,但是在部署 RLP 代码时,Power Peg 功能仍然存在而且可以被调用。并且,新的 RLP 代码重用了一个原本用于激活 Power Peg 的标记。骑士原本预计删除 Power Peg 代码,这样当标记设置为“是”的时候,参与工作的将会是新的 RLP 代码,而不是 Power Peg 代码。
之前骑士使用 Power Peg 代码时,当子指令被执行时,有一个累积数量功能会计算父指令中被执行的股票数。这个功能会指明让代码在父指令已经全部被执行后不再发送子指令。2003 年,骑士停止使用 Power Peg 功能。2005 年,骑士把 Power Peg 代码中这段追踪累积股票数的功能挪到了 SMARS 代码顺序中更前面一点的地方。挪完之后,骑士并没有重新测试 Power Peg 代码,已确定在被调用时 Power Peg 是否仍然能够正确运作。
从 2012 年 7 月 27 号开始,骑士分批将新的 RLP 代码部署到 SMARS 上,连续几天把它放置到数目有限的 SMARS 服务器上。然而,在部署新代码的过程中,骑士的某个技术人员并没有把新的代码复制到八台 SMARS 服务器的其中一台上。骑士没有让第二位技术人员复查这次部署,也没有人意识到第八台服务器其实并没有删除 Power Peg 代码,也没有安装新的 RLP 代码。骑士没有任何书面流程要求这样的复查。
8 月 1 号,骑士从经纪自营商那里收到了有权参与 RLP 的客户的指令。那七台安装了新代码的服务器正确处理了那些指令。但是,使用了重用标记的指令发送到第八台服务器,触发了那台服务器上残留的有缺陷的 Power Peg 代码。因此,那台服务器开始向特定交易中心发送子指令以便执行。
8 月 1 号,骑士同样收到了有权参与 RLP 并且特别指定在开市前交易的指令。有 6 台 SMARS 处理了那些指令,从东部时间大概早上 8:01 开始,骑士的一个内部系统根据 SMARS 自动发出邮件信息(叫做”BNET 指令拒绝”),发现了一个叫做“Power Peg 已被禁止”的错误。骑士系统在上午 9:30 开市之前向骑士的某组员工发送了 97 封这样的邮件。骑士没有把这类信息设计为系统报警,骑士员工在收到时也基本上不会检查。
更惨的是:
这篇文档的其他部分绝对值得一读,但是重要的是推荐了新的为避免类似灾难的人为过程。导致这个 bug 的运营错误没有什么是和人为因素有关的,反而更象是因为很烂的部署脚本和很不幸的产品监控。什么样业余的系统才会连确保服务器集群运行统一软件发布的监控都没有!?更不用说可以检查返回值的部署脚本……
我们只能希望那些未使用代码的“书面测试过程”指的是有系统的测试,就象参照一个十年前的 wiki 页面所说的那样。
最好的部分是关于罚金:1200 万美金,虽然最终审计也披露系统有条理地发送了无担保卖空指令。
[修正:似乎这篇发布的有点太快了:最终亏损是 4.6 亿美金,而且那段代码几乎 9 年没被用过了,而不是 8 年]