一個(gè)Bug導(dǎo)致每秒鐘虧172,222美元,持續(xù)了45分鐘
這大概是我讀過(guò)的最慘痛的一份bug報(bào)告。它描述了一個(gè)去年下半年爆發(fā)的軟件bug是怎樣一步步使得騎士資本(Knight Capital)在交易中損失了4.65億美金,并且直接導(dǎo)致了公司的破產(chǎn)。
這個(gè)故事,有著一個(gè)大型、無(wú)維護(hù)、腐爛(代碼本身長(zhǎng)達(dá) 8 年沒(méi)用過(guò)了)代碼庫(kù)的技術(shù)債務(wù)的所有特點(diǎn),真是一個(gè)極為低劣且無(wú)職業(yè)素養(yǎng)的Devops故事。
重點(diǎn)摘要:
為了允許客戶參與紐約股票交易所的“零售流動(dòng)性計(jì)劃”(RLP),騎士資本對(duì)它的指 令處理流程相關(guān)的系統(tǒng)和軟件代碼進(jìn)行了幾次修改,其中有5個(gè)被安排在從2012年8月1號(hào)開始。這些修改包括在SMARS上開發(fā)和部署新的軟件代碼。 SMARS是一個(gè)自動(dòng)化高速算法路由器,可以把指令發(fā)送到市場(chǎng)上執(zhí)行。SMARS的一個(gè)核心功能是用于接受從騎士交易系統(tǒng)的其他組件發(fā)送過(guò)來(lái)的指令 (“父”指令),然后根據(jù)可用流動(dòng)性的需求,向外部市場(chǎng)發(fā)送一個(gè)或多個(gè)代表指令(或者“子”指令)以便執(zhí)行。
13. 在部署時(shí),SMARS上的新RLP代碼本來(lái)是用于取代指令路由器上與之相關(guān) 但并未使用的代碼。那些未被使用的代碼之前被用于一個(gè)叫做“Power Peg”的功能,但是騎士多年前就已經(jīng)不再使用了。雖然很久不用了,但是在部署 RLP代碼時(shí),Power Peg功能仍然存在而且可以被調(diào)用。并且,新的RLP代碼重用了一個(gè)原本用于激活Power Peg的標(biāo)記。騎士原本預(yù)計(jì)刪除 Power Peg代碼,這樣當(dāng)標(biāo)記設(shè)置為“是”的時(shí)候,參與工作的將會(huì)是新的RLP代碼,而不是Power Peg代碼。
14. 之前騎士使用Power Peg代碼時(shí),當(dāng)子指令被執(zhí)行時(shí),有一個(gè)累積數(shù)量 功能會(huì)計(jì)算父指令中被執(zhí)行的股票數(shù)。這個(gè)功能會(huì)指明讓代碼在父指令已經(jīng)全部被執(zhí)行后不再發(fā)送子指令。2003年,騎士停止使用Power Peg功能。 2005年,騎士把Power Peg代碼中這段追蹤累積股票數(shù)的功能挪到了SMARS代碼順序中更前面一點(diǎn)的地方。挪完之后,騎士并沒(méi)有重新測(cè)試 Power Peg代碼,已確定在被調(diào)用時(shí)Power Peg是否仍然能夠正確運(yùn)作。
15. 從2012年7月27號(hào)開始,騎士分批將新的RLP代碼部署到SMARS 上,連續(xù)幾天把它放置到數(shù)目有限的SMARS服務(wù)器上。然而,在部署新代碼的過(guò)程中,騎士的某個(gè)技術(shù)人員并沒(méi)有把新的代碼復(fù)制到八臺(tái)SMARS服務(wù)器的其 中一臺(tái)上。騎士沒(méi)有讓第二位技術(shù)人員復(fù)查這次部署,也沒(méi)有人意識(shí)到第八臺(tái)服務(wù)器其實(shí)并沒(méi)有刪除Power Peg代碼,也沒(méi)有安裝新的RLP代碼。騎士沒(méi) 有任何書面流程要求這樣的復(fù)查。
16. 8月1號(hào),騎士從經(jīng)紀(jì)自營(yíng)商那里收到了有權(quán)參與RLP的客戶的指令。那七臺(tái) 安裝了新代碼的服務(wù)器正確處理了那些指令。但是,使用了重用標(biāo)記的指令發(fā)送到第八臺(tái)服務(wù)器,觸發(fā)了那臺(tái)服務(wù)器上殘留的有缺陷的Power Peg代碼。因 此,那臺(tái)服務(wù)器開始向特定交易中心發(fā)送子指令以便執(zhí)行。
19. 8月1號(hào),騎士同樣收到了有權(quán)參與RLP并且特別指定在開市前交易的指令。 有6臺(tái)SMARS處理了那些指令,從東部時(shí)間大概早上8:01開始,騎士的一個(gè)內(nèi)部系統(tǒng)根據(jù)SMARS自動(dòng)發(fā)出郵件信息(叫做”BNET指令拒絕”),發(fā) 現(xiàn)了一個(gè)叫做“Power Peg已被禁止”的錯(cuò)誤。騎士系統(tǒng)在上午9:30開市之前向騎士的某組員工發(fā)送了97封這樣的郵件。騎士沒(méi)有把這類信息設(shè)計(jì)為 系統(tǒng)報(bào)警,騎士員工在收到時(shí)也基本上不會(huì)檢查。
更慘的是:
27. 8月1號(hào),騎士并沒(méi)有關(guān)于事故反應(yīng)的監(jiān)控流程。說(shuō)得更具體一點(diǎn),騎士沒(méi)有監(jiān) 控流程在重大問(wèn)題發(fā)生時(shí)可以指導(dǎo)它的相關(guān)員工。在8月1號(hào),騎士基本上依賴于它的技術(shù)團(tuán)隊(duì)在實(shí)時(shí)交易的環(huán)境下,試著發(fā)現(xiàn)和解決SMARS問(wèn)題。當(dāng)騎士的員 工在試圖查找問(wèn)題源頭的同時(shí),它的系統(tǒng)仍然在持續(xù)發(fā)出上百萬(wàn)條子指令。在一次試圖解決問(wèn)題時(shí),騎士把新的RLP代碼從正確安裝的七臺(tái)服務(wù)器上卸載了。這加 劇了這個(gè)問(wèn)題,因?yàn)樗鼘?dǎo)致額外新進(jìn)來(lái)的父指令激活了那些服務(wù)器上殘留的Power Peg代碼,就像第八臺(tái)服務(wù)器上已經(jīng)發(fā)生的一樣。
這篇文檔的其他部分絕對(duì)值得一讀,但是重要的是推薦了新的為避免類似災(zāi)難的人為過(guò)程。導(dǎo)致這個(gè)bug的運(yùn)營(yíng)錯(cuò)誤沒(méi)有什么是和人為因素有關(guān)的,反而更 象是因?yàn)楹軤€的部署腳本和很不幸的產(chǎn)品監(jiān)控。什么樣業(yè)余的系統(tǒng)才會(huì)連確保服務(wù)器集群運(yùn)行統(tǒng)一軟件發(fā)布的監(jiān)控都沒(méi)有!?更不用說(shuō)可以檢查返回值的部署腳 本……
我們只能希望那些未使用代碼的“書面測(cè)試過(guò)程”指的是有系統(tǒng)的測(cè)試, 就象參照一個(gè)十年前的wiki頁(yè)面所說(shuō)的那樣。
***的部分是關(guān)于罰金:1200萬(wàn)美金,雖然最終審計(jì)也披露系統(tǒng)有條理地發(fā)送了無(wú)擔(dān)保賣空指令。
原文鏈接:http://pythonsweetness.tumblr.com/post/64740079543/how-to-lose-172-222-a-second-for-45-minutes