Второй день недели, а я просто в каком-то безумии нахожусь.



Предыстория: мы в MapReduce обрабатываем просто тучу данных и понятное дело, что не обходится без странных машин каждый день. Чтобы от этого хоть как-то спастись, в shuffle сессии мы пишем чексуммы при записи из Map, а при чтении из Reduce мы их читаем и сравниваем. Если какие-то чексуммы не совпадают, ну что поделать, машина была побитая, удалим машину и перезапустим шард для исполнения снова. Такие падения исчисляются единицами в день, но в целом забавно смотреть как им мозги отказывают.



Конечно же команда по Silent Data Corruption настроила мониторинг на это дело. И тут вот после нескольких месяцев тестирования решили выкатить новые процессоры :). Угадайте какая метрика равномерно для всех пользователей повысилась. Падения всё ещё редки, но достаточно повысились, чтобы бить тревогу.



Слышал байку, что мы тут в Google дизайним системы, чтобы они были устойчивы к багам в CPU. Конечно же, это неправда. В общем, даже процессоры не работают в этом мире, ничего, блин, не работает в этом мире. Уже два дня пытаюсь понять в чём дело, а ещё убедить себя, что компьютеры всё же были ошибкой.



Своими глазами я повидал:



1. Данные на диске были записаны правильно, кроме одной 4кб страницы -- были записаны нулями. Root cause: скорее всего при использовании DIRECT_IO кто-то решил почитать данные из того же файла до close(), воспроизводилось раз на 4 тысячи машин

2. Команда побайтового сравнения вернула, что какие-то байты разные, по факту записаны были одни и те же байты. Root cause: непонятен, но спустя 2 недели у машины совсем отказали мозги, и она умерла

3. Битфлипы всех видов, в том числе детерминированные: машина работала корректно, но при сжатии посылаемых данных один бит был другим, чем на реплике. Root cause: не ясно

4. Cамокорректирующиеся AES -- decode(encode(x)) на машине давал x, а на другой decode от закодированной на первой выдавал ерунду. Root cause: одна инструкция ошибалась "правильно", процессор отправился на свалку

5. Падения инструкций, которые обещают не читать кое-какие данные, но всё же их читают. Root cause: баг в процессоре, был починен вендором



Мои коллеги из Google [1] также рассказывали о:



1. Битфлип, влияющий на сборку мусора в Java, где крутились холодные пользовательские данные. Результат: потеря данных из-за того, что система не обращалась к данным.

2. Повреждения таблицы страниц в ядре, в результате чего процесс и ядро были несогласованы и ломались в очень непредсказуемых местах

3. Два треда зашли в критическую секцию, нарушилась базовая гарантия Mutex, результат -- поломанные пользовательские данные



Что помогало, и что я привнёс(у):



1. Проверять чексуммы с битфлипами, если обнаружилось несовпадение, посвапать биты один за другим и пересчитать чексуммы. Честно украл идею, когда работал в поиске, много ловит и отсекает "простые" случаи

2. Как ни странно, алгоритмы сжатия типа LZ4, Snappy и даже ZSTD тоже хорошо находят битфлипы. В том плане, что если записана чексумма разжатых данных, и битфлип произошёл в сжатом формате, иногда битфлипы сохраняются и в разжатом виде. Особенно для не очень хорошо сжимаемых данных. Это очень нетривиальный факт, но я умею доказывать какие-то свойства про него.

3. Использовать алгоритмы сжатия, которые умеют в диагностику, в каком бите формат данных поломался. В это умеют LZ4 и ZSTD. Snappy и zlib не умеют.

4. Искать большую последовательность нулей, особенно большую степень двойки+-1. Тоже отсекает проценты понятных поломок.

5. Если данные важны в каждом бите, делайте кворумные джобы, запускайте 3 реплики и пока 2 не договорятся, никакого удаления/пересчёта денег не происходит.



Если 1-4 ничего не нашли, на это стоит смотреть глазами. 5 работает примерно всегда.



Возьму пару дней погулять. Ощущение, что ничего не работает после таких дебагов. Но зато они интересные, весь стек надо понимать.



[1] Cores that don't count. Lessons from silent data corruption at Google

[2] ClickHouse тоже писал про "Checksum does not match"

[3] Silent data corruptions at Facebook