В системах баз данных WAL (Write-Ahead Log) отвечает за восстановление данных и репликацию и считается одним из самых стабильных и надежных компонентов всей цепочки обработки данных.
Однако в ходе недавнего расследования инцидента в промышленной среде команда YMatrix столкнулась с крайне необычной проблемой: на узле Mirror постоянно возникали ошибки проверки CRC32C WAL, что приводило к блокировке синхронной репликации и тайм-аутам при фиксации транзакций. При дальнейшем анализе выяснилось, что локальный WAL на узле Primary был полностью корректен, сетевое соединение работало нормально, а проверки TCP checksum успешно проходили.
Ситуацию дополнительно усложняло то, что проблема не воспроизводилась постоянно. Подавляющее большинство WAL-файлов были полностью идентичны, и лишь очень небольшое число сегментов повреждалось в определённых позициях. Более того, поврежденные данные демонстрировали явные «структурированные признаки».
Для локализации проблемы команда YMatrix начала анализ с исходного кода потоковой репликации WAL PostgreSQL / GPDB и с помощью синхронного многоуровневого захвата данных через GDB, бинарного анализа WAL и аудита сетевого тракта Linux постепенно сузила область поиска, в конечном итоге обнаружив проблему в аппаратной цепочке передачи NIC DMA и PCIe. Данное расследование затронуло не только ядро базы данных, но и глубоко ушло в Linux network stack, механизмы DMA и поведение передачи данных на аппаратном уровне. В этой статье подробно описан весь процесс расследования данного сложного инцидента, а также инженерные практики и технические выводы YMatrix в области надежности низкоуровневых трактов передачи данных。
Проблема впервые проявилась в процессе потоковой репликации Primary-Mirror в кластере MatrixDB. Во время воспроизведения WAL-записей узел Mirror постоянно сообщал об ошибках CRC32C checksum:
LOG: incorrect resource manager data checksum in record at 0/4F551398
После этого узел Mirror начинал бесконечно повторять попытки воспроизведения WAL, цепочка синхронной репликации блокировалась, распределённые транзакции COMMIT постепенно начали завершаться по тайм-ауту, а весь кластер вошёл в нестабильное состояние.
На первый взгляд это выглядело как типичная проблема повреждения WAL-данных. Однако настоящая сложность заключалась в том, что проблема не воспроизводилась стабильно. Подавляющее большинство WAL-файлов были абсолютно корректны, и только крайне небольшое количество сегментов вызывало CRC-ошибки в фиксированных позициях. Это означало, что проблема не имела характерной для традиционных software bug стабильной закономерности, а скорее напоминала низковероятностное кратковременное событие повреждения данных.
Для систем баз данных такие спорадические проблемы зачастую являются наиболее сложными для диагностики. Их невозможно стабильно воспроизвести с помощью нагрузочного тестирования, а в логах обычно отсутствуют очевидные признаки неисправности. Более того, при последующем бинарном сравнении WAL-файлов команда обнаружила, что из 24 WAL-файлов 22 были полностью идентичны, и только один файл содержал реальное повреждение, причём между повреждёнными сегментами находились несколько полностью корректных WAL segment. Это наблюдение оказалось чрезвычайно важным. Оно означало, что проблема не являлась постоянной системной ошибкой, а скорее представляла собой редкое аномальное внедрение данных в одном из узлов цепочки передачи. В результате расследование постепенно сместилось от внутренних механизмов базы данных к более низкоуровневой инфраструктуре передачи данных.
Поскольку финальная ошибка проявлялась на этапе WAL replay на Mirror-узле, наиболее естественным предположением было повреждение WAL-файла в процессе записи на диск. Чтобы проверить эту гипотезу, команда сначала выполнила миграцию WAL-хранилища Mirror-узла, создав symbolic link WAL-каталога на другой диск, пытаясь исключить проблемы с устройством хранения.
Однако проблема сохранилась.
Это означало, что данные, вероятно, повреждались ещё до фактической записи на диск. Таким образом, расследование постепенно сместилось от «аномалий дисковой подсистемы» к «аномалиям тракта передачи данных». На этом этапе традиционных логов уже было недостаточно для дальнейшего сужения области поиска. Команда YMatrix решила отказаться от предположений и напрямую захватывать реальные данные в ключевых точках WAL transmission pipeline.
После этого команда разработала несколько серий синхронных экспериментов с GDB capture, чтобы пошагово проверить состояние WAL-данных на разных этапах передачи. Сначала данные WAL были захвачены в процессе walsender на Primary-узле непосредственно перед отправкой. Результаты показали, что проверка pg_waldump проходила успешно, CRC32C был корректен, а содержимое WAL record оставалось целостным. Это подтвердило, что локальные WAL-файлы на Primary-узле не содержали ошибок, а цепочка от чтения с диска до вывода walsender функционировала корректно.
Однако когда команда дополнительно захватила данные в walreceiver на Mirror-узле — сразу после recv() и до дальнейшей обработки — проблема впервые проявилась напрямую. Полученные walreceiver данные уже были повреждены и полностью совпадали с тем, что позже записывалось на диск. Это означало, что write() не являлся точкой возникновения повреждения — Mirror-узел лишь «добросовестно» записывал уже испорченные данные на диск.
На этом этапе проблема впервые была точно локализована: WAL, отправляемый с Primary-узла, был корректным, однако данные повреждались в процессе передачи.
Этот вывод имел принципиальное значение, поскольку проблема перестала ограничиваться внутренней логикой базы данных и переместилась в область взаимодействия между СУБД, операционной системой, сетевой подсистемой и аппаратным обеспечением.
Чтобы окончательно проверить возможность модификации WAL payload самим программным слоем базы данных, команда приступила к последовательному аудиту исходного кода потоковой репликации PostgreSQL / GPDB.
Теоретически WAL-запись на пути от Primary к Mirror проходит через несколько этапов:
чтение WAL-файла;
walsender;
libpq buffer;
kernel socket;
TCP stack;
NIC DMA;
сетевую передачу;
recv buffer на Mirror-узле.
Любой из этих этапов потенциально может стать источником аномалии. Однако после глубокого анализа исходного кода команда обнаружила, что весь WAL replication pipeline на самом деле чрезвычайно «тонкий». Большая часть цепочки по сути сводится к:
memcpy → send() → recv() → memcpy → write()
Во всём процессе отсутствуют компрессия, повторное кодирование протокола или преобразование данных. При отключённых SSL/GSS отсутствует и шифрование. Подавляющая часть цепочки представляет собой лишь стандартные операции копирования памяти и системные вызовы. Это означало, что программный слой базы данных практически не содержит пути, способного «активно переписывать WAL-данные». Иными словами, если sender отправляет корректный WAL, а receiver получает уже повреждённые данные, то источник проблемы, скорее всего, находится за пределами database software — в Linux network stack, DMA pipeline или даже на более низком уровне аппаратной передачи данных. Таким образом, расследование продолжило смещаться в сторону инфраструктурного уровня.
После этого команда выполнила полное бинарное сравнение WAL segment между sender и receiver.
Результат оказался весьма показательным: из 24 WAL-файлов 22 были полностью идентичны, и только один файл содержал реальное повреждение. Это означало, что проблема не являлась постоянной ошибкой transmission path, а скорее представляла собой кратковременное событие повреждения данных.
Однако настоящий перелом в расследовании произошёл не из-за того, что «повреждён был только один файл», а из-за содержимого самого повреждённого участка. Обычные WAL-данные, как правило, обладают высокой энтропией и напоминают случайный бинарный поток. Но аномальная область демонстрировала крайне выраженные структурированные признаки:
FF E1 EA DA 01 00 00 00
00 FF FF FF FF 00 00 00
Размер аномального блока составлял ровно 64 байта. Для разработчиков СУБД 64 байта могут показаться обычным числом. Однако в аппаратном мире это крайне характерная величина. 64 байта обычно соответствуют CPU cache line, DMA descriptor или типичному PCIe payload chunk. Более того, повторяющиеся FF FF FF FF очень напоминали типичные маркеры PCIe error poisoning или структуры управления DMA. Что ещё важнее — этот структурированный блок отсутствовал в исходном WAL sender-узла, но был полностью вставлен в WAL stream receiver-узла. Это означало, что он не являлся частью самого WAL, а представлял собой «внешний структурированный фрагмент данных». На этом этапе проблема явно вышла за пределы database software, и команда постепенно переключила подозрения на более низкоуровневые причины: NIC DMA descriptor leakage, PCIe TLP error poisoning и аномалии DMA transmission path.
Это один из самых легко неправильно интерпретируемых, но одновременно наиболее важных аспектов всего инцидента.
Многие по умолчанию считают:
«Если TCP checksum проходит проверку, значит данные корректны.»
На практике возможности TCP checksum значительно ограниченнее, чем принято думать. TCP гарантирует лишь то, что данные не изменились в процессе сетевой передачи. Но он не может гарантировать, что сами данные, участвующие в вычислении checksum, изначально были корректны.
Если повреждение возникает до вычисления checksum — например внутри NIC DMA, на этапе PCIe DMA memory write или во время hardware offload processing — тогда TCP checksum может вычисляться уже по повреждённым данным. В такой ситуации recv() будет работать абсолютно нормально, TCP connection не покажет никаких аномалий, но база данных всё равно получит повреждённый WAL.
Именно поэтому в экосистеме PostgreSQL помимо TCP validation используются WAL CRC32C, page checksum и механизмы end-to-end consistency.
База данных должна быть способна защищаться от сценариев, в которых: «TCP считает данные корректными, хотя сами данные уже повреждены.» По своей сути данный инцидент как раз относился к этой категории аномалий.
Если оглянуться назад, изначально проблема выглядела всего лишь как ошибка WAL replay. Однако по мере углубления расследования она в конечном итоге привела к Linux network stack, механизмам DMA и аппаратным трактам передачи PCIe. Главные особенности подобных проблем — крайне низкая вероятность возникновения, сложность воспроизведения и отсутствие признаков в логах. Традиционные системы мониторинга практически не способны их обнаруживать. Часто базы данных воспринимаются исключительно как программные системы. Однако в современных масштабных real-time data scenarios СУБД всё больше приближаются к аппаратным границам. NUMA, CPU cache, DMA, NIC offload, PCIe, Linux network stack — всё то, что раньше считалось частью «системного уровня», сегодня может напрямую влиять на стабильность базы данных. Для современной data infrastructure настоящая надёжность больше не определяется исключительно самой database software, а зависит от согласованности и проверяемости всей data path на каждом уровне системы.
Расследование данной WAL-аномалии в конечном итоге привело от ядра базы данных к Linux network stack, механизмам DMA и аппаратным PCIe transmission path. Сложность диагностики подобных проблем обусловлена не только многоуровневостью технологий, но и тем, что они находятся в своеобразной «пограничной зоне»: они не воспроизводятся как типичные software bug и не сопровождаются явными аппаратными ошибками, как классические hardware failure. Вместо этого они проявляются как крайне редкие, кратковременные аномалии практически без каких-либо следов в логах, из-за чего методы анализа, основанные на одном уровне системы, легко оказываются неэффективными. Продвижение в расследовании таких инцидентов уже невозможно строить исключительно на «экспертных предположениях» внутри одного слоя. Необходимо последовательно разбирать всю цепочку передачи данных — от внутренних механизмов базы данных до операционной системы, сетевой передачи и фактического поведения hardware DMA — проверяя, остаются ли данные достоверными на каждом этапе. В этом процессе становится заметно более фундаментальное изменение: сама database system постепенно эволюционирует из «software component» в полноценную data infrastructure, охватывающую операционную систему и аппаратные каналы передачи данных. Корректность данных больше не обеспечивается исключительно внутренней логикой СУБД, а зависит от согласованности всей transmission chain на множестве уровней. В дальнейшем YMatrix продолжит углублённые инженерные исследования и техническую практику в области distributed database kernel, streaming real-time architecture, data foundation for the Agent era и высоконадёжных data transmission infrastructure.
Глубокий анализ: В эпоху ИИ базы данных вступают в «эпоху унифицированного хранения» (Часть I)
Глубокий анализ: В эпоху ИИ базы данных вступают в «эпоху унифицированного хранения» (Часть II)
Глубокий анализ: В эпоху ИИ базы данных вступают в «эпоху унифицированного хранения» (Часть III)
Эпоха ИИ: параллельные запросы — от «быстрого сканирования» к «быстрому вычислению»
Модель потоковой обработки PostgreSQL
Новый фундамент баз данных в эпоху ИИ: исследование векторизованного выполнения в PostgreSQL