{ "index": 259, "slug": "editorial-2020-10-field-backup-recovery", "title": "Резервная копия не равна восстановлению: как проверить backup без риска для источника", "excerpt": "Архив можно увидеть и скачать, но это ещё не доказательство восстановления. Разбираем manifest, checksum, область данных, изолированную цель и проверку результата на учебном PostgreSQL-сценарии.", "contentHtml": "

Симптом знакомый: в хранилище лежит свежий архив, команда видит его размер и дату, но никто не может уверенно сказать, что произойдёт после restore. Неясно, какие схемы попали в файл, совпадают ли его байты с теми, что указаны в журнале, куда пойдёт восстановление и чем считать результат успешным.

\n

Цена такой ошибки появляется в аварии. Оператор выбирает архив по имени, запускает привычную команду и обнаруживает неполный набор таблиц. Или восстанавливает данные поверх источника. В этот момент резервная копия не помогает: она превращается в ещё один объект, которому нужно доверять без проверки.

\n

Тезис статьи простой: backup готов к применению только тогда, когда команда может проверить его scope, целостность, безопасность цели и смысл результата. Наличие файла доказывает наличие файла. Оно не доказывает, что приложение сможет продолжить работу.

\n

Что именно нужно доказать

\n

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

\n

Эти вопросы связывает короткий manifest. В нём хранят идентификатор архива, формат, область данных, исключения, SHA-256 и проверки результата. Manifest не заменяет сам архив и не делает восстановление автоматическим. Он задаёт договор, с которым можно сравнить фактический файл и фактический кандидат.

\n
{\n  "backupId": "training-catalog-2020-10-a",\n  "format": "pg_dump custom",\n  "artifact": {\n    "file": "training-catalog.dump",\n    "sha256": "..."\n  },\n  "scope": {\n    "includes": ["schema:catalog", "schema:reference"],\n    "excludes": ["cluster roles", "tablespaces", "external files"]\n  },\n  "checks": {\n    "relations": ["catalog.items", "reference.codes"],\n    "rows": { "catalog.items": 3, "reference.codes": 2 }\n  },\n  "target": "isolated training candidate only"\n}
\n

Пример учебный. Имена, числа и значение хеша вымышлены. Поле excludes здесь не декоративно: логический dump одной базы не следует выдавать за копию всего кластера. Роли, tablespaces и внешние файлы требуют отдельного решения и отдельной проверки.

\n
Схема проверки резервного архива: scope, checksum, безопасная цель и проверки восстановленных данных
Сначала проверяется договор архива и его байты, затем цель восстановления, затем структура и данные кандидата.
\n

Почему checksum не заменяет restore

\n

SHA-256 помогает ответить на узкий вопрос: совпадают ли байты фактического файла с ожидаемым digest. Если значение изменилось, файл нельзя считать тем же артефактом. Причиной может быть неполная передача, повреждение, подмена или выбор другого файла под похожим именем.

\n

Но одинаковый digest не доказывает правильный scope. Можно безошибочно сохранить неполный dump и получить идеальное совпадение checksum. Поэтому проверка идёт в два слоя: сначала bytes, потом содержимое архива и результат восстановления.

\n

Симптомы и безопасные действия

\n
Первая классификация проблемы до повторного restore
СимптомПричинаПроверкаДействие
Архив есть, но scope не описанФайл создавали без явного договораСверить список ожидаемых схем и исключений с командой созданияОстановить drill и уточнить scope
SHA-256 не совпадаетВыбран другой или повреждённый файлЗаново вычислить digest фактического файлаНе читать и не восстанавливать файл; получить доверенный артефакт
В списке нет нужной relationОна не попала в dump или фильтр сузил областьСравнить pg_restore --list с manifestИсправить создание архива или оформить зависимость отдельно
Цель совпадает с источникомRunbook не закрепил изоляциюПроверить имя базы и запрет перезаписи до командыНе запускать; создать отдельного кандидата
Restore завершился, но таблица пустаНеполный scope, неверная версия или слабый checkВыполнить read-only запросы к кандидату и сравнить expected rowsЗафиксировать отрицательный verdict и исправить договор
\n

Конкретный маршрут для PostgreSQL

\n

Для учебного custom archive можно сначала получить список объектов, не меняя базу. Команда pg_restore --list показывает, что инструмент видит внутри non-plain archive. Этот список нужно сравнить с manifest, а не с памятью оператора. Если ожидалась reference.codes, а её нет, повторный запуск restore не добавит объект.

\n
# Учебные имена. Команды не выполнялись этой статьёй.\nsha256sum training-catalog.dump\npg_restore --list training-catalog.dump\n\n# Только после проверок — отдельный кандидат.\ncreatedb training_restore_candidate\npg_restore --dbname=training_restore_candidate training-catalog.dump
\n

Перед последней командой нужно подтвердить, что training_restore_candidate не является источником и не содержит нужных рабочих данных. Если безопасной цели нет, правильный результат — «restore не проверен». Нельзя превращать отсутствие стенда в разрешение на риск.

\n

После восстановления проверяют не только код возврата процесса. Нужны конкретные read-only проверки: существуют ли ожидаемые схемы, совпадает ли набор relations, имеют ли таблицы ожидаемый тип и не пусты ли обязательные справочники. Количество строк — учебный критерий, а не обещание для настоящей базы. В production оно меняется, поэтому ожидаемое значение должно зависеть от снимка и бизнес-условия.

\n

Порядок действий

\n
  1. Назвать backup ID и прочитать manifest целиком: формат, файл, scope, исключения, digest, цель и checks.
  2. Сверить область восстановления с вопросом. Если нужная схема не включена, назвать это пробелом дизайна, а не ошибкой pg_restore.
  3. Вычислить SHA-256 фактического файла. При несовпадении остановить маршрут до просмотра содержимого.
  4. Получить список архива через pg_restore --list и сравнить его с ожидаемыми relations.
  5. Подтвердить изолированную базу-кандидат и запретить перезапись источника. Проверку цели выполнить до команды restore.
  6. Восстановить архив только в кандидата. Сохранить время начала, время окончания, версию инструмента и итог команды.
  7. Выполнить структурные и смысловые read-only checks. Сравнить фактический результат с manifest, не меняя expected values после факта.
  8. Записать непокрытые области: роли, tablespaces, внешние файлы, журналы изменений, требования к потере данных и допустимое время восстановления.
\n

Отрицательный путь важнее зелёного

\n

Хорошая проверка должна остановиться на плохом входе. Изменённый файл должен отклоняться по checksum. Суженный scope должен отклоняться по сравнению manifest и списка объектов. Источник должен быть недопустимой целью. Пустая relation должна приводить к отрицательному verdict, если приложение требует данные.

\n

Нельзя исправлять отрицательный результат подменой evidence. Не следует менять хеш, чтобы пройти gate, добавлять строки вручную после восстановления или переписывать expected count после обнаружения расхождения. Эти действия скрывают причину и делают следующий запуск менее надёжным.

\n

Ограничения метода

\n

Логический dump PostgreSQL покрывает не все способы восстановления. Копирование файлов кластера, continuous archiving и point-in-time recovery имеют другие предпосылки. Команда, которая проверила custom archive одной базы, не доказала восстановление всего кластера и не измерила готовность приложения.

\n

Учебный сценарий не проверяет сеть, права, секреты, размер production-данных, скорость передачи, репликацию, WAL, внешние object storage и работу зависимых сервисов. Он также не даёт RPO или RTO. Время учебной команды становится RTO только после измерения на разрешённой цели с описанными условиями. Дата архива сама по себе не является RPO.

\n

Если drill не может затронуть реальное окружение, это не недостаток статьи. Нужно отделить проверенный учебный контракт от непроверенной инфраструктуры и назначить следующий безопасный эксперимент. Такой итог точнее, чем заявление о полной готовности к аварии.

\n

Критерий готовности

\n

Минимальный проверяемый критерий выглядит так: команда предъявляет manifest и фактический archive; SHA-256 совпадает; список объектов покрывает заявленный scope; restore выполнен только в изолированной цели; источник не изменился; структурные и смысловые checks прошли; время и версия инструмента записаны; непокрытые области явно перечислены.

\n

Если хотя бы один пункт неизвестен, verdict должен быть ограниченным: «архив найден», «байты совпали» или «учебная база восстановилась». Нельзя сокращать его до «резервное копирование работает». Копия становится рабочим механизмом только там, где команда может повторить путь, увидеть отрицательный результат и безопасно остановиться.

\n

Проверяемые источники

\n" }