惨烈:1 个 Bug, 45 分钟损失 4 亿多美元
2012年8月1日,一个 bug 一步步让骑士资本在交易中损失了 4.65 亿美金,并且直接导致破产。这个故事涉及的代码库,是一个大型、无人维护、腐烂的代码库,代码本身将近 9 年没用过了,真是一次集合了技术债务所有特点的惨案。
2014 年我参加了一场会议并就 DevOps、代码配置和持续交付等话题进行发言,讲到 DevOps 和持续交付的时候我说了下面这个故事,来阐述自动化、可重复部署的重要性。那次会议之后,有好多人让我在博客上分享这个故事。这是一个真实的故事,下面根 据我了解的来进行分享(我并没有亲身经历)。
下面讲的是一个资产 4.6 亿美元的公司因由于部署问题在 45 分钟内破产的故事。
背景
Knight Capital Group(骑士资本集团)是美国一家全球性的金融服务公司,主要业务有 做市、电子交易系统和机构交易。2012 年,骑士资本是美国股票市场最大的经纪商,分别占有纽交所和纳斯达克 17% 的市场份额。骑士资本的电子贸易部门管理的平均日交易量超过 33 亿股,交易额高达 210 亿美元。这可不是开玩笑!
到 2012 年 7 月 31 日,骑士资本拥有高达 3 亿 6500 万美元的现金及现金等价物。
2012 年 8 月 1 日,纽交所计划启动零售流动性项目(该 项目旨在通过类似骑士资本这样的经济商向普通投资者提供更优惠的股票交易价格),为此,骑士资本对他们自动化且高速的算法程序 SMARS 进行了更新,该程序向交易所发送执行命令。SMARS 程序的核心功能之一是从骑士资本交易平台的其他部分接收订单(“父”订单),然后发出一个或多个“子”订单。换句话说,SMARS 程序从交易平台接收大订单,然后根据买家或卖家的股票交易数量把大订单拆分成合适的小订单。父订单越大,子订单越多。
这次更新主要是为了把过时或没用的功能替换掉,比如说“Power Peg”。这个功能已经 8 年没有用到过了( 8 年都没用到的功能代码仍然存在,这的确很稀奇,但这不是重点)。更新后的代码对以前用来激活 Power Peg 功能的标识符进行了更改。这次更新经全面测试后证明安全可靠,那问题究竟出在哪?
还有哪里可能出问题么?的确是!
从 2012 年 7 月 27 日至 31 日,骑士资本把软件手动部署到公司为数不多的服务器上——总共就 8 台。以下是美国证交会关于这次人工部署过程的档案描述(顺便一提,如果你的操作被记录到证交会档案里可就大事不妙了)。
“在部署过程中,相关技术人员忘记把新代码拷贝到这八台服务器其中的一台上。骑士资本也没有安排另外的技术人员对部署过程 进行复查,所以没有人意识到第八台机器上的 Power Peg 代码并没有被删除,新的 RLP 代码也没有被添加。对于复查,骑士资本并没有相关的书面流程。 —— 美国证监会文件 | 发布编号 70694 | 2013-10-16”
东部时间 2012 年 8 月 1 日早上 9:30 股市开盘,骑士资本开始处理来自 RLP 新项目的交易商的订单。其中七台正确部署的服务器开始正确地处理订单。但是发向第八台服务器的订单触发了被更改的标识符,执行的是无效的 Power Peg 代码。
来自「僵尸」代码的攻击
我们得弄清楚这段「僵尸」代码是用来干什么的。这个功能以前是用来对比买/卖股票的父订单和子订单的数额的。父订单数额一旦达标,Power Peg 就会向系统反馈停止订单拆分。总的来说,Power Peg 功能可以持续追踪子订单,而父订单一旦完成,该功能会立即终止子订单活动。然而 2005 年,骑士资本把跟踪计数这一功能转移到稍微靠前一些的代码里(因此 Power Peg 不再具备跟踪计数功能了)。
当第八台机器上的 Power Peg 功能被启动后,由于它无法跟踪对比父订单的股票数,所以子订单不断产生并执行,这就成了无法终止的死循环。
灾难性的 45分钟
你可以设想一下,当一个失去追踪计数功能的系统无限制地、高速地向市场发出订单时情况会有多糟糕。
早上 9:30 开盘后立即有人意识到出了问题。一分钟后,华尔街大部分人都感觉到大事不妙了,股票市场中某些个股涌现出大量不符合常规交易量的订单。又过了一分钟,人们 发现交易仍然没有停止——就高速交易系统而言,交易根本停不下来。为什么没人尝试停止出问题的系统呢?事后发现,这个系统根本没有切断开关。在 45 分钟之内,骑士资本执行了超过日均交易额 50% 的订单,导致部分股票市值上升超过 10%,带来的连锁反应是其他股票价格暴跌。
更糟的是,早在上午的 8:01(这时 SMARS 在进行开市前交易),骑士资本的系统就自动发送了有关问题的邮件。这些标记为 SMARS 的邮件提及 Power Peg 功能出现了问题。从 8:01 到 9:30,97 个个人邮箱都收到了这封邮件。估计这些邮件并没有系统警报的作用,所以并没有人马上查看。天呐。
在这灾难性的 45 分钟里,骑士资本想出几个对策来终止错误的交易。由于这个系统没有切断开关(也没有相关情况的文档说明),他们只能在每分钟交易 800 万股的线上环境中诊断问题起因。然而他们没能发现系统问题出在哪里,只能卸载已经部署到几台服务器上的新代码。换句话说,他们把有用的代码删掉反而留下了 问题代码。情况恶化了,除了第八台未被正确部署的服务器,另外七台服务器中的父订单也触发了 Power Peg 功能。最后,他们终于想办法终止了交易系统,然而已经过去了 45 分钟。
在开市后的 45 分钟内,骑士资本接收并处理了 212 个父订单,SMARS 发出数百万个子订单,累计对 154 支股票进行了 400 万次交易,交易量超过 3 亿 9700 万股。在内行人看来,骑士资本建立了 80 支个股 35 亿美元的净多头仓位和 74 支个股 31 亿 5000 万美元的净空头仓位。对外行人来说,骑士资本在 45 分钟内亏损了 4 亿 6000 万美元,而上文提到,骑士资本仅有 3 亿 6500 万美元。仅仅 45 分钟,骑士资本从美国股市最大的交易商和纽交所以及纳斯达克的大庄家变得一钱不值。破产后,骑士资本有 48 小时的时间筹集资金弥补损失(骑士资本还有大约六个投资者投资的 4 亿美元)。骑士资本最终被 Getco LLC 收购(2012 年 12 月),合并后新公司更名为 KCG Holdings。
吸取教训
所有运维团队都应该从骑士资本惨案中吸取教训。不仅要开发优秀的软件并进行全面测试,还需要把软件正确地交付给交易所,这样客户才能获得正确的结果 (才能避免公司破产)。这个事件中,我们不能把矛头全部对准部署 SMARS 的技术人员,骑士资本的业务流程根本不足以应付他们所面对的问题。此外,这种流程(或缺陷)本来就很容易出错。是人都会犯错,不论何时,只要你的部署过程 依赖于人工指令操作,就有可能出现问题。指令本身、对指令的解读以及指令的执行过程都可能存在隐患。
部署应该是自动化且可重复的,这个过程应该尽量排除人为因素的干扰。假如骑士资本采用的是自动部署系统——配置、部署和测试完全自动化,这场悲剧可能就不会发生了。
持续交付的某些原则也同样适用(即使你执行的不是一个完整的持续交付过程):
- 软件发布过程应该是可靠且可重复的。
- 合理的情况下,尽可能实现自动化。
本文文字及图片出自 伯乐在线
共有 1 条讨论