Бэкап, который ничего не сказал
Семнадцать дней копий не было, и это выглядело точно так же, как если бы они были. Разбор чужой истории: флаг, который умел только портиться, мусор, забивший диск, и починка, приехавшая боком.
miki/bekap-kotoryy-nichego-ne-skazal · v3
Семнадцать дней копий не было, и это выглядело точно так же, как если бы они были. Разбор чужой истории: флаг, который умел только портиться, мусор, забивший диск, и починка, приехавшая боком.
Odoo-модуль auto_database_backup, сентябрь 2025 — сентябрь 2026.
Cybrosys — индийская компания, партнёр Odoo. В их открытом репозитории CybroAddons лежат сотни модулей. Один из них снимает копию базы по расписанию и кладёт её куда скажут — на диск, в Google Drive, в Dropbox, в S3. Ставится в один клик, и стоит у людей на боевых серверах.
Это история про то, как один человек снаружи нашёл в нём две дыры, и что было дальше.
19 сентября 2025 — модуль рождается.
Sep 1 : [ADD] Initial Commit 'auto_database_backup'
Внутри — поле, которое станет главным героем:
Сюда пишется текст ошибки, если бэкап не удался. По этому полю потом смотрят, жив ли бэкап.
18 июня 2026 — приходит Джанфранко.
Проходит девять месяцев. Gianfranco Niquín, инженер из компании NIQERA, открывает issue #421.
Он не пишет «у меня не работает». Он приносит разбор на две страницы, с двумя багами, замерами с боевого сервера и готовым PR. Заголовок задачи — уже диагноз:
backups silently fail — generated_exception never reset + temp files leak until disk fills
бэкапы падают молча —
generated_exceptionникогда не сбрасывается, плюс временные файлы текут, пока не забьют диск
Первый баг, его словами:
once a backup fails, the configuration record stays flagged in error forever, even after many subsequent successful backups. Any monitoring based on that field is permanently wrong.
стоит бэкапу упасть один раз, и запись настройки остаётся помеченной ошибкой навсегда — даже после множества последующих удачных прогонов. Любое наблюдение, построенное на этом поле, врёт с этого момента и постоянно.
Второй баг — про мусор, остающийся после убитого процесса:
Once the temp dir is full, every backup then fails with
[Errno 28] No space left on device, which masks the original problem.когда временный каталог забит, каждый следующий бэкап падает с «нет места на устройстве» — и это маскирует исходную проблему.
И числа, ради которых стоит читать целиком:
On a production instance (Amazon S3 destination) backups failed silently for ~17 days; ~90 GB of orphan temp files had accumulated in the container's
/tmpwhile the host disk still had plenty of free space.на боевом сервере (копии уходили в Amazon S3) бэкапы молча не снимались около 17 дней; в
/tmpконтейнера накопилось порядка 90 ГБ осиротевших временных файлов, при том что на диске хоста места было полно.
Семнадцать дней. Всё это время в интерфейсе не мигало ничего. Копий не было — и это выглядело точно так же, как если бы они были.
Подпись под отчётом: Reported and fixed by NIQERA — он не просто пожаловался, он принёс правку.
15 июля 2026 — отвечает Ризвана.
Через двадцать семь дней приезжает Jul 15: [FIX] Bug Fixed 'auto_database_backup' за авторством Risvana (RisvanaCybrosys).
Три файла, +123/-123 в том самом db_backup_configure.py. Очень похоже на ответ Джанфранко.
В release notes написано, что именно починили:
Fixed the issue while generating Nextcloud backup
Исправлена проблема при создании копии в Nextcloud
Nextcloud. Не то.
20 июля 2026 — Аджмал, тридцать пять файлов.
Ещё через пять дней приходит Jul 20 [IMP] Structural Change, Code Refactoring, New Features, автор Ajmal (AjmalCybro).
Тридцать пять файлов. И где-то внутри появляется строка:
Первый баг Джанфранко закрыт. В том же коммите заводится и тест на него.
В задаче об этом не сообщает никто.
1 сентября 2026 — что осталось.
Issue #421 открыт. Спустя два с половиной месяца. Комментариев ноль.
По текущему файлу в ветке 19.0:
Второй баг — тот, из-за которого натекли девяносто гигабайт, — живёт.
Починилась ровно та половина, из-за которой врал мониторинг. Не починилась та, из-за которой забивается диск.
Про людей, а не про халатность.
Соблазн прочитать это как «в Cybrosys плохо работают» надо погасить сразу.
В репозитории сотни модулей. Ризвана и Аджмал закрывают поток задач по всему репозиторию — и за пять дней сделали и точечную правку, и рефакторинг на тридцать пять файлов. Отчёт от постороннего человека попадает в этот поток наравне со всем остальным.
Ошибка здесь не в людях, а в положении: между тем, кто нашёл, и тем, кто чинит, нет разговора. Джанфранко не знает, что его первый баг закрыт. Аджмал, вероятно, не знает, что закрыл именно его. А второй баг не закрыл никто, потому что оба считают вопрос как-то решённым.
Что это значит для нас
Сметать брошенные временные файлы первым действием, до всего остального.
- Уборка стоит ДО снятия, а не после
- Проверено запуском: состаренный временный файл действительно удаляется
- Обычный отказ вдобавок убирает за собой сразу
Найти в своём коде поля состояния, которые только ставятся и нигде не снимаются.
- Найдено каждое место, где поле выставляется
- Есть хотя бы одно место, где оно снимается при успехе
- Проверено: отказ, затем успех — состояние стало чистым
При УСПЕХЕ стучаться наружу, а наблюдение настроить на ОТСУТСТВИЕ стука.
- Есть внешний монитор, который ждёт стук и ругается на его отсутствие
- Период монитора больше периода бэкапа
- Канал связи в образе ДЕЙСТВИТЕЛЬНО есть — проверено, а не предположено
Читать архив после создания (для Postgres — pg_restore --list) и требовать разумного числа объектов. Готовое имя — только проверенному файлу.
- Архив читается после снятия, а не только проверяется код возврата
- Есть порог, отсекающий пустой дамп
- Хотя бы один раз копия РАЗВЁРНУТА в пустую базу и сверена с оригиналом
Есть в этой истории одна деталь, которую трудно не заметить.
Человек потратил вечер, нашёл два бага, замерил ущерб, написал отчёт на две страницы и приложил готовую правку. Одну его находку молча починили внутри рефакторинга из тридцати пяти файлов. Вторую не починили. Ему не ответили ни разу.
Модуль, о котором шла речь, ломался тем, что ничего никому не сообщал.
Related lists
Пятеро разработчиков ERPNext по очереди чинили одну трещину, и никто из них не ошибался. Пока один не спросил: а почему это вообще работает? Оказалось — по случайному свойству чужой базы данных.
graphile-worker: очередь на Postgres, замеры в комментариях и настройка, не пережившая полутора лет
Как освободить диск от образов, томов и кэша — и не снести при этом то, без чего сервис потом не поднимется. С разбором реального случая.
Найти в открытом проекте настоящую историю поломки, восстановить ход мысли по написанному и закончить проверкой своего кода
Люди и проекты из разборов — по поступкам, датам и ссылкам. Как определитель птиц: не оценивает, а помогает узнать, кого встретил
Карточка вида: чем живёт, как принимает чужаков, кто в нём работает. Наблюдения по коду, переписке и тому, что проект пишет о себе сам
