
Продолжаем тему эпических багов. В прошлый раз мы говорили про AT&T, положивших свою ультранадежную сеть одним «Break» в коде. Сегодня на очереди Knight Capital Group, решивших переиспользовать старый флаг в бинарном протоколе, затем там был мёртвый код, который забыли удалить и деплой, проверенный на семи серверах из восьми. Итог: уход в минус 450 миллионов долларов за 45 минут.
На Хабре этот инцидент упоминался несколько раз, но даже в самой большой статье (к слову, переводу, со всеми странными атрибутами инопрессы, вроде фраз «Атака зомби из „Кода убийцы“» и пространным вступлением) инцидент рассматривался скорее как финансовый. А нас же больше интересуют именно технические детали.
Набор высоты
К 2012 году Knight Capital Group считалась монстром, занимавшейся маркет-мейкингом, электронным исполнением сделок, а также институциональными продажами и торговлей. Благодаря своим алгоритмам высокочастотной торговли Knight была крупнейшим трейдером на американском фондовом рынке, занимая 17,3% рынка на NYSE и 16,9% на NASDAQ.

Работа на высококонкурентном рынке обязывает реагировать за доли секунды. Поэтому для команд серверам использовался не JSON или protobuf (которые надо обрабатывать посимвольно), а бинарный протокол, в котором ордера передавались как сериализованные структуры. Соответственно, внутри жестко описанной структуры существовало поле флагов, которое задействовало те или иные алгоритмы.
Хроника пикирующего б̶о̶м̶б̶а̶р̶д̶и̶р̶о̶в̶щ̶и̶к̶а̶ маркет-мейкера
Поскольку Knight Capital Group была той ещё финансовой акулой, да еще и маркет-мейкером (участником биржи, обеспечивающим ликвидность на рынке, то есть обеспечивающий возможность купить и продать акции на рынке быстро и по предсказуемой цене), то в ее арсенале была хитрая функциональность по снижению цены акций путем автоматической покупки дорого и продажи дешево. Называлась она Power Peg.
У нее был предохранитель — при исполнении заявки на покупку/продажу большая заявка делилась на множество мелких и исполнялась ровно до тех пор, пока не достигалась общая сумма продажи/покупки. Другими словами, на сервере стоял счетчик, который проверял — продолжаем ли торговлю или уже хватит. И он работал.
Функциональность Power Peg практически не использовали, и в 2003 году её и вовсе отключили. В 2005 году программисты Knight Capital проводили рефакторинг и перенесли проверку общего количества продаж/покупок выше по коду, вынеся ее за пределы Power Peg. Другими словами, предохранитель убрали. Тут дальше истории расходятся — либо этот баг просто не отловили при тестировании, либо, если верить инсайдерам, тесты стали падать, но поскольку это был старый, неиспользуемый код, тесты Power Peg просто отрубили.

Все прекрасно работало до августа 2012 года (7 лет). В июле этого года Нью‑Йоркская фондовая биржа анонсировала запуск Retail Liquidity Program (RLP) — программы, позволяющей розничным инвесторам получать улучшенные цены через брокеров вроде Knight Capital. Ордера RLP требовали специальной обработки, а значит — нового флага в протоколе, используемом «рыцарями».
Поскольку переделка бинарного протокола с жесткой структурой требовала заметной переделки всех компонентов обмена, полного цикла интеграционного тестирования, плюс координированного деплоя с зависимыми сервисами, решили обойтись малой кровью. Тем более, что и сроки поджимали — NYSE запускала RLP 1 августа, и Knight не могла не успеть к этому дедлайну. В общем, программисты решили использовать устаревший флаг от давно неиспользованной функции. А что у нас 7 лет как не использовалось? Пра‑а-а‑вильно, Power Peg! Ну а чтобы не активировать «покупку дорого и продажу дешево», реакцию на флаг переписали.
Крутое пике
Начиная с 27 июля DevOps'ы Knight начинают разворачивать новый код на серверах SMARS поэтапно, устанавливая его на ограниченное число серверов в течение нескольких дней. Процесс был ручным — инженер подключался по SSH к каждой машине и устанавливал через rsync обновление Чтобы не париться, он написал скрипт, который автоматизировал эту задачу. Но в скрипте была ма‑а-аленькая ошибка — при неудачной попытке открыть SSH‑соединение с машиной скрипт молча продолжал работу и рапортовал об успехе. Поскольку скрипт был самописный, его вообще официально не тестировали и какого‑либо контроля версий у него не существовало.
В результате 7 серверов получили обновленное ПО (давайте назовём его, скажем v3.8), а 8-й сервер, перезагружавшийся в момент деплоя, остался на старом v3.7). Серверы поработали 3 дня, их выборочно погоняли по тестам перед 1 августа (видимо, не попав в 8-й) и каких‑либо аномалий не отловили. Вердикт — можно работать.

Итак, наступает 1 августа. Сервер 8, как и все остальные, получает команду на активацию флага Power Peg, но только он начинает работать с ней по старой логике. Поскольку Power Peg, это все‑таки страшная штука (а в Knight Capital Group все‑таки не зря считались крутыми финансистами) в системе существовала функция отправки предупредительных писем. Всего система сгенерировала 97 с сообщением, что Power Peg таки включен. Но представьте любую большую компанию с текучестью кадров: кто‑то давно уволился, кто‑то поменял должность, кто‑то и вовсе забыл что это такое. В общем, на письма никто не обратил внимание. А если и обратили, то это были не те, кто мог эту штуку выключить.
В 9:30 начинается торговля и сервер 8 начинает массово понижать цену, покупая акции по рыночной цене и тут же перевыставляя их дешевле. Поскольку предохранитель у него отрубили на рефакторинге, он начинает это делать бесконечно. И тут начинается самое интересное. Видя аномальную бурю в «стакане» и понимая, что творится что‑то странное, менеджмент решает откатить обновление на предыдущую версию ПО, на которой все работало нормально.
Почему не выключить все? Потому что они крупный маркет‑мейкер и не могут просто так прекратить работу. Да и убыток пока еще был не самый большой. Но какая же у нас старая, хорошо протестированная версия серверного ПО? Правильно, v3.7, где логика работы с Power Peg такая же на 8 сервере. И именно ее спешно раскатывают на остальные серверы. После чего деньги начинают уходить со скоростью в 7 раз быстрее. Как показали поздние подсчеты, серверы «рыцарей» отправили более 4 млн ордеров.
Пр̶осадка
Итог ужасен. К 10:15 утра Knight Capital потеряла 450 миллионов долларов (при том что на балансе компании было всего 365 миллионов). В результате компания, успешно работавшая 17 лет, стала технически банкротом менее чем за час. Кроме того, компания получила штраф от финансового регулятора SEC на 12 миллионов долларов за нарушение правил рыночного доступа. Хотя, казалось бы, куда еще то убытки.
Разбирая ситуацию, сложно сказать, кто именно тут накосячил больше всего. Как говорилось в старом фильме «День Радио»:

…и программисты, отрефакторившие код и убравшие предохранитель в логике наверх, и тестеры, проморгавшие изменения в Power Peg (к слову, переиспользование кода выглядело как раз логичным решением) и DevOps'ы, сначала использовавшие косячный самописный скрипт для заливки обновления, а потом и вовсе обновившие все сервера на ПО со старой логикой.
Самое интересное, что в итоге Knight Capital выжила. Ребята получили экстренное финансирование в размере 400 миллионов долларов от группы инвесторов, но потеряли независимость — в декабре 2012 года компанию приобрела Getco LLC.
Размещайте облачную инфраструктуру и масштабируйте сервисы с надежным облачным провайдером Beget.
Эксклюзивно для читателей Хабра мы даем бонус 10% при первом пополнении.

