Осенью 2020-го британская система отслеживания пандемии споткнулась о число, которое программисты знают наизусть, а чиновники в тот момент, вероятно, видели впервые. Эта история — о том, как почти шестнадцать тысяч заражённых людей на несколько дней выпали из поля зрения эпидемиологов, потому что таблица закончилась.
Конец сентября 2020 года в Англии — время, когда вторая волна COVID-19 набирала ход. Ежедневные цифры новых заражений ползли вверх, правительство обсуждало локальные локдауны, а коммерческие лаборатории, обрабатывавшие так называемый Pillar 2 — массовое тестирование населения вне больниц, — выдавали результаты всё более плотным потоком. Каждый положительный тест должен был пройти цепочку: лаборатория фиксирует результат, данные уходят в центральные системы Public Health England (PHE), оттуда — в дашборды для эпидемиологов и, что критически важно, в систему NHS Test and Trace, где контактные трекеры начинают обзванивать заболевших и выяснять, с кем те виделись за последние дни. Скорость этой цепочки — буквально часы — определяла, успеют ли предупредить людей до того, как они разнесут вирус дальше.
Пациенты в те дни получали свои результаты в штатном режиме. Человек, сдавший мазок 27 сентября, узнавал о положительном тесте и получал указание на самоизоляцию. Его личная траектория не ломалась. Но его данные — имя, контакты, дата теста — застревали на полпути к тем людям, чья работа состояла в том, чтобы позвонить его коллегам, соседям по пабу, родственникам и сказать: изолируйтесь, вы в зоне риска. И никто этого не замечал, потому что снаружи всё выглядело нормально: цифры в ежедневных отчётах росли, но росли они чуть медленнее, чем должны были, и эту разницу невозможно было разглядеть без сверки с первичными данными лабораторий.
Чтобы понять, что произошло, нужно на секунду вспомнить историю одного файлового формата. Расширение .xls — это бинарный формат Microsoft Excel, появившийся в 1987 году, когда персональный компьютер с мегабайтом оперативной памяти считался мощной машиной. Предел в 65 536 строк для одного листа был заложен архитектурно: строки адресовались шестнадцатибитным числом, 2 в шестнадцатой степени — вот вам и потолок. В 2007 году Microsoft выпустила формат .xlsx, где лимит вырос до 1 048 576 строк — больше миллиона. К 2020-му формату .xls исполнилось тридцать три года, и в мире разработки он воспринимался примерно как дисковый телефон: работает, но зачем?
Автоматизированный процесс в PHE принимал результаты из коммерческих лабораторий в виде CSV-файлов — простых текстовых таблиц без ограничения длины. Затем эти данные переносились в шаблоны формата .xls для дальнейшей загрузки в центральные системы. И вот здесь кроется деталь, от которой у меня каждый раз холодеет спина: каждый результат теста генерировал в таблице несколько строк — с контактными данными, датой забора образца, идентификаторами. Это означало, что потолок в 65 536 строк исчерпывался примерно после 1 400 кейсов. Тысяча четыреста. В стране с населением под шестьдесят миллионов, в разгар второй волны, пропускная способность одного шаблона составляла полторы тысячи человек — и всё, дальше данные просто срезались, как вода, переливающаяся через край ведра. Без ошибки, без предупреждения, без красного сигнала на экране. Excel не кричит, когда строки не влезают в старый формат. Он молча их выбрасывает.
Сбой обнаружили в ночь на пятницу, 2 октября 2020 года. Кто именно заметил расхождение и каким образом — публичные отчёты описывают скупо, но сам факт, что потребовалась неделя (данные терялись как минимум с 25 сентября), говорит о многом. Система мониторинга не имела встроенной проверки полноты: никто автоматически не сравнивал количество результатов, отправленных лабораториями, с количеством записей, дошедших до дашбордов. Это как если бы почтовое отделение считало только письма, разложенные по ящикам, но не сверяло их число с тем, что привёз почтальон.
Когда масштаб потери стал ясен, началась экстренная работа. К 1:00 ночи 3 октября все 15 841 пропущенный положительный результат были переданы в систему NHS Test and Trace. Более 75% из них — 11 968 случаев — относились к трём последним дням, с 30 сентября по 2 октября. Остальные были размазаны по предшествующей неделе. 3 октября эти данные разом добавили в официальную статистику, и дневной прирост случаев подскочил так, что графики выглядели как стена. Заголовки газет кричали о рекордах, комментаторы в соцсетях подозревали манипуляции, а эпидемиологи хватались за голову, потому что искажённые цифры за предыдущую неделю уже успели повлиять на модели и прогнозы.
Майкл Броди, исполнявший обязанности главы PHE, в официальном заявлении подтвердил цифру: технический сбой в процессе загрузки данных оставил за бортом отчётности все 15 841 случай. Сьюзан Хопкинс, медицинский советник NHS Test and Trace и PHE, объясняла, что результаты тестов не были потеряны — они существовали в системах лабораторий, пациенты были уведомлены. Но контактные трекеры, те самые люди, которые должны были обрывать цепочки заражения звонками и письмами, работали с неполными списками. Сколько контактов не были предупреждены вовремя — вопрос, на который точного публичного ответа так и не прозвучало.
Формула, которая мгновенно разлетелась по интернету, звучала неотразимо: «Excel сломал британское здравоохранение». Она красива, компактна и почти полностью неправильна. Excel сделал ровно то, что ему полагалось: открыл файл в формате .xls и вместил столько строк, сколько формат позволяет. Обвинять программу — всё равно что ругать кастрюлю за то, что суп выкипел: кастрюля не выбирала, сколько воды в неё нальют.
Настоящая проблема лежала на несколько уровней глубже. Во-первых, выбор формата. Кто-то когда-то — возможно, месяцами ранее, в спешке весенней волны — создал шаблон в .xls, и с тех пор никто не пересмотрел это решение, хотя объёмы тестирования выросли кратно. Во-вторых, архитектура пайплайна. Данные шли по маршруту CSV → .xls → центральная система, и промежуточный шаг с Excel был, по сути, бутылочным горлышком, вставленным в поток без какой-либо валидации. Ни один скрипт не проверял, все ли строки из исходного CSV оказались в итоговом файле. В-третьих, и это, пожалуй, самое болезненное: вся конструкция была временным решением, которое стало постоянным. Пандемия требовала скорости, и весной 2020-го инженеры PHE собирали процессы из того, что было под рукой. Таблицы Excel были под рукой всегда. Проблема временных решений в том, что они переживают своих создателей: человек, собравший пайплайн в марте, мог уйти к лету, а шаблон остался работать.
Мэтт Хэнкок, тогдашний министр здравоохранения, сказал в парламенте, что этого «не должно было случиться», и отчитался о быстрой реакции команды; к понедельнику, впрочем, контакт-трейсеры успели дойти лишь до 51% из застрявших пациентов. Джонатан Эшворт, теневой министр здравоохранения от лейбористов, назвал произошедшее «хаосом» и потребовал объяснений, почему критическая инфраструктура зависела от устаревшего программного обеспечения. Профессор Джон Кроуфт из Кембриджского университета указал на системную проблему: дело было не в конкретном баге, а в отсутствии инженерной культуры обращения с данными на уровне, который соответствовал бы масштабу задачи.
Легко сказать: ну, данные же восстановили, пациенты знали о своих результатах — в чём трагедия? Трагедия — в природе контактного отслеживания. Его эффективность измеряется часами. Если человек узнал о положительном тесте в понедельник, а его коллегу предупредили только в пятницу, эти четыре дня коллега ходил на работу, ездил в метро, встречался с друзьями. Каждый потерянный день — это ветвящееся дерево потенциальных заражений, и подрезать его задним числом уже поздно.
Контактные трекеры NHS Test and Trace и без того работали с задержками: система была перегружена, дозвониться удавалось не всегда, люди не брали трубку или не помнили, с кем виделись. Но одно дело — задержка в несколько часов из-за очереди звонков, и совсем другое — полное отсутствие данных в системе на протяжении дней. Для тех 15 841 случаев, которые неделю существовали только в файлах лабораторий и в личных уведомлениях пациентов, контактное отслеживание просто не начиналось. Меня поражает арифметика: если каждый заражённый имел в среднем хотя бы пять-шесть близких контактов, речь идёт о десятках тысяч людей, которые не получили предупреждения вовремя. Точную цифру PHE не публиковало, и, возможно, её невозможно посчитать, потому что мы никогда не узнаем, кто из непредупреждённых заразился и кого заразил дальше.
Отдельный слой проблемы — искажение эпидемиологической картины. Модели, на основе которых правительство принимало решения о локдаунах и ограничениях, питались ежедневными данными. Неделя заниженных цифр, а затем резкий всплеск — это шум, который мешает отличить реальный тренд от артефакта. Эпидемиологам пришлось задним числом пересчитывать кривые, восстанавливая распределение случаев по датам забора образцов, а не по датам отчётности. Работа кропотливая и неблагодарная, потому что к моменту, когда она завершилась, политические решения на основе неточных данных уже были приняты.
Этот эпизод — не про Excel. И даже не про PHE конкретно. Он про архитектуру систем, которые государства строят в условиях кризиса, и про то, что происходит, когда временное становится несущим. В любой крупной организации — от банка до министерства — существуют десятки «невидимых» таблиц, скриптов, макросов, которые кто-то однажды написал для себя, а потом они оказались встроены в критический процесс. Их никто не документирует, не тестирует на предельных нагрузках, не проверяет на соответствие текущим объёмам данных. Они работают — и этого достаточно, пока однажды не перестают.
В случае PHE совпали несколько факторов. Пандемия масштабировала тестирование быстрее, чем масштабировались системы обработки. Коммерческие лаборатории, подключённые к Pillar 2, отправляли данные в формате, который сам по себе не был проблемой (CSV не имеет ограничений на число строк). Проблема возникала на стыке — в момент, когда данные переливались из одного формата в другой, как вода из широкой трубы в узкую. И этот стык никто не мониторил, потому что он казался тривиальным: ну подумаешь, копирование данных из одного файла в другой, что тут может пойти не так?
Меня, признаться, больше всего цепляет именно это: не сам сбой, а неделя тишины. Семь дней система теряла данные, и ни один автоматический тест, ни одна проверка целостности не подняла тревогу. Это значит, что в пайплайне не было даже элементарного контроля — подсчёта строк на входе и на выходе. Любой студент-программист на втором курсе знает, что при миграции данных нужно проверять: сколько записей было, сколько стало, совпадает ли. Но PHE в тот момент не занималось классической разработкой ПО — оно латало трубы на ходу, под давлением политиков, прессы и растущих цифр заражения. И в этой спешке базовые принципы инженерии данных оказались роскошью, на которую не хватило времени, людей или внимания.
После инцидента быстрой заплаткой стало разбиение больших файлов на куски, чтобы каждый влезал в прежний шаблон, а PHE инициировало полную ревизию всех систем обработки — от лаборатории до дашборда. Позднее, в 2021 году, PHE было реорганизовано: его функции перешли к новому агентству — UK Health Security Agency (UKHSA), хотя причины реорганизации были шире одного инцидента с таблицами. Система NHS Test and Trace продолжала работать, но эпизод с потерянными данными стал одним из аргументов критиков, указывавших на системные проблемы в британском ответе на пандемию.
Этот случай теперь живёт в учебниках по управлению данными и в презентациях на конференциях по data engineering как хрестоматийный пример. Но он заслуживает чуть более неудобного вопроса, чем «почему не обновили формат файла». Вопрос вот в чём: почему система, от скорости которой зависело прерывание цепочек заражения — буквально жизни и смерти, — была собрана так, что единственной преградой между лабораторными данными и эпидемиологами оказался лимит строк, заданный инженерами Microsoft за три года до падения Берлинской стены? Ответ, вероятно, не в чьей-то злонамеренности и не в глупости. Он в том, как устроены большие бюрократические системы: они растут наслоением, каждый новый слой ложится поверх предыдущего, и никто не заглядывает вниз, пока пол не проваливается. В октябре 2020-го пол провалился на глубину ровно в 65 536 строк.