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