Несколько потерянных страниц базы данных нарушили работу целой VPN-инфраструктуры. Специалисты Tailscale потратили полгода на поиски причины неожиданных сбоев и обнаружили редкую ошибку SQLite, остававшуюся незамеченной с 2010 года.
Tailscale рассказала о завершении расследования после серии повреждений баз данных, начавшейся в августе 2025 года. Для поиска неисправности разработчикам потребовалась помощь создателей SQLite и новый диагностический инструмент, подробно записывающий операции с файлами.
Сервис Tailscale создает частные одноранговые сети на базе протокола WireGuard и напрямую соединяет компьютеры, серверы, сетевые хранилища и другие устройства. Каждая частная сеть, которую компания называет tailnet, работает на одном из нескольких серверов. Информацию о подключениях хранит база SQLite, используемая Tailscale с 2022 года.
Система резервного копирования каждые несколько минут создавала полный снимок базы и загружала файл SQLite в хранилище S3. В августе 2025 года во время подготовки копий начали регулярно появляться сообщения о повреждении данных. Общего условия для возникновения сбоя найти не удавалось, а низкоуровневый код к тому моменту не менялся уже несколько месяцев.
Инженеры проверили все компоненты, обращавшиеся к SQLite, но первоначальный аудит не выявил причину. Команда последовательно исключила нарушение блокировок POSIX после вызовов close(), ошибки управления памятью и работу с базой из нескольких потоков при отключенной потокобезопасности. После каждого нового случая специалисты расширяли диагностику и собирали дополнительные сведения.
Подозрение постепенно перешло на механизм контрольных точек SQLite. Для ускорения работы база сначала записывает изменения в отдельный журнал упреждающей записи, известный как Write-Ahead Log или WAL. Во время создания контрольной точки накопленные страницы переносятся из журнала в основной файл базы данных.
При обычной работе SQLite самостоятельно выбирает время для запуска контрольной точки. Tailscale управляла процессом вручную, чтобы быстрее создавать согласованные резервные копии. Нестандартный и довольно интенсивный режим повысил вероятность крайне редкого совпадения операций.
При финансовой поддержке Tailscale разработчики SQLite создали специальную прослойку виртуальной файловой системы, которая подробно регистрировала работу контрольных точек. После очередного повреждения журнал помог обнаружить ошибку, получившую название WAL-Reset.
Причиной оказалась гонка между созданием контрольной точки и транзакцией записи. Если новая запись появлялась в строго определенный момент, SQLite ошибочно считала, что часть страниц уже перенесена из WAL в основной файл. Фактически страницы оставались неперенесенными, а затем безвозвратно терялись. Одновременно база сохраняла другие страницы со ссылками на утраченные данные, что приводило к повреждению всей структуры.
Ошибка возникает только при одновременном выполнении нескольких условий: активном режиме WAL, нескольких открытых подключениях к одному файлу и точном совпадении операций чтения и записи. В стандартных конфигурациях вероятность сбоя остается крайне низкой, однако ручное и частое создание контрольных точек в инфраструктуре Tailscale заметно повышало риск.
По оценке разработчиков SQLite, дефект присутствовал в коде начиная с версии 3.7.0, выпущенной в июле 2010 года. Исправление уже подготовлено, а пользователям рекомендовали перейти на обновленную версию. Вероятность столкновения с WAL-Reset невелика, но последствия включают необратимую потерю страниц и повреждение базы данных.