{ "index": 260, "slug": "editorial-2020-10-mechanism-backup-recovery", "title": "Резервная копия PostgreSQL: как доказать, что её можно восстановить", "excerpt": "Файл с совпавшим SHA-256 ещё не является рабочей резервной копией. Разбираем scope, manifest и безопасный restore на учебном примере PostgreSQL.", "contentHtml": "

В хранилище лежит свежий файл catalog.dump. SHA-256 совпадает с записью в журнале. Во время сбоя оператор запускает восстановление, а нужной роли нет, таблица пуста или архив относится к другой схеме. Цена ошибки — потерянное время в самом дорогом окне и риск направить restore в источник, который ещё содержит единственную рабочую копию.

\n

Проблема не в одной команде. Слово «backup» смешивает четыре разных факта: данные прочитали, файл записали, файл можно разобрать, после restore получился нужный набор объектов. Checksum подтверждает только неизменность байтов. Код выхода 0 подтверждает только завершение конкретного процесса. Ни один из них не отвечает на вопрос, сможет ли приложение использовать восстановленную базу.

\n

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

\n

Сначала определите, что именно копируется

\n

pg_dump создаёт логический dump одной базы. Это не снимок всего кластера. Роли и другие cluster-wide объекты требуют отдельного решения и могут входить в область pg_dumpall. Tablespaces, файлы на диске, секреты и данные внешних систем также не появляются в обычном dump автоматически. Если они нужны приложению, их надо назвать отдельными артефактами или явно исключить из критерия.

\n

Выборочная копия схемы уменьшает размер, но увеличивает число предположений. Таблица может ссылаться на тип, функцию или другую схему, которую фильтр не включил. Поэтому строка scope.includes должна описывать не удобный путь команды, а минимальный набор, который нужен целевой базе. Если зависимость неизвестна, это пробел контракта, а не повод считать архив «почти полным».

\n
pg_dump --format=custom --file=training-catalog.dump training_catalog\npg_restore --list training-catalog.dump\nshasum -a 256 training-catalog.dump
\n

Команды выше — учебный пример. Имена базы и файла вымышлены. Они показывают форму последовательности, но не доказывают, что команда выполнялась и что архив пригоден для production-восстановления. Сначала зафиксируйте область, затем создавайте артефакт.

\n

Manifest связывает создание и восстановление

\n

Без manifest оператор выбирает файл по имени и дате. Такой выбор не описывает содержимое. Минимальная запись должна позволять другому человеку ответить на пять вопросов: какой это артефакт, в каком он формате, какие байты проверять, что входит в scope и каким наблюдением подтвердить результат.

\n
{\n  \"manifestVersion\": 1,\n  \"backupId\": \"training-catalog-2020-10-a\",\n  \"artifact\": {\"file\": \"training-catalog.dump\", \"format\": \"pg_dump custom (-Fc)\", \"sha256\": \"<hash>\"},\n  \"scope\": {\"includes\": [\"schema:catalog\", \"schema:reference\"], \"excludes\": [\"cluster roles\", \"tablespaces\", \"external files\"]},\n  \"restoreChecks\": {\"relations\": [\"catalog.items\", \"reference.codes\"], \"rows\": {\"catalog.items\": 3, \"reference.codes\": 2}},\n  \"safety\": \"только изолированный учебный кандидат\"\n}
\n

Поле artifact.sha256 относится к файлу до restore. Поля restoreChecks относятся к базе после restore. Их нельзя заменить одним флагом complete: true. Если байты изменились, маршрут останавливается до чтения архива. Если байты целы, но список объектов не совпадает, нужно разбирать scope или выбрать правильный артефакт.

\n
\"Путь
Restore проходит несколько границ. Каждая граница может остановить маршрут и сохранить конкретную причину отказа.
\n

Checksum проверяет файл, а не смысл

\n

SHA-256 полезен на границе хранения и передачи. Он обнаруживает изменённый, повреждённый или перепутанный файл. При несовпадении digest действие одно: остановить restore и получить доверенный артефакт заново. Нельзя «проверить дальше», потому что следующие результаты уже относятся к байтам, которым нельзя доверять.

\n

Совпавший digest не видит неверный scope. Два файла могут быть целыми и одинаково непригодными: оба могли содержать только одну из двух требуемых схем. Поэтому checksum — gate целостности, а не verdict восстановления. Оглавление архива и checks после restore отвечают на другие вопросы.

\n

Симптом → причина → проверка → действие

\n
Диагностика резервной копии и restore
СимптомПричинаПроверкаДействие
SHA-256 не совпалФайл изменился, повреждён или выбран не тот артефактПовторить digest и сверить путь с manifestОстановить restore, получить архив из доверенного источника
Архив читается, нужной relation нетScope слишком узкий или выбран другой dumpСравнить pg_restore --list с scope.includesИсправить путь создания или выпустить новую версию manifest
Роль отсутствует после restoreCluster-wide объект не входил в dump базыПроверить scope.excludes и список требуемых ролейВосстановить роли отдельным согласованным шагом или изменить контракт
Restore завершился, таблица пустаDump неполный, выбран не тот объект или check не соответствует даннымВыполнить безопасный row count и сверить его с manifestОстановить ввод данных, найти расхождение до переключения
Команда направлена в рабочую базуНе доказана изоляция целиПроверить адрес, имя и отдельные credentials кандидатаНе запускать restore; создать и явно подтвердить безопасную цель
\n

Таблица задаёт отрицательный путь. Любое расхождение переводит операцию в остановку. Нельзя продолжать до следующего шага только потому, что архив «свежий» или команда раньше уже работала. Причина должна попасть в запись проверки вместе с действием, которое вернёт маршрут к доверенному состоянию.

\n

Restore выполняйте в изолированном кандидате

\n

Цель должна быть отдельной базой или отдельным кластером с понятным именем, доступом и запретом на перезапись источника. Перед командой проверьте подключение и сохраните фактический endpoint. Учебный пример ниже не запускает PostgreSQL. Он показывает порядок и границу безопасности.

\n
# Учебная последовательность. Источник не перезаписывается.\ncreatedb training_restore_candidate\npg_restore --dbname=training_restore_candidate --exit-on-error training-catalog.dump\npsql training_restore_candidate --command=\"SELECT to_regclass('catalog.items');\"\npsql training_restore_candidate --command=\"SELECT count(*) FROM catalog.items;\"
\n

Флаг --exit-on-error не превращает restore в доказательство успеха. Он делает ранний отказ заметнее. После команды нужно отдельно проверить ожидаемые relations, версию схемы и безопасный набор синтетических строк. Проверка не должна менять источник, отправлять письмо, списывать деньги или обращаться к внешней системе.

\n

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

\n
  1. Сформулируйте scope одним предложением: какая база, схемы и зависимые объекты должны появиться после restore.
  2. Перечислите исключения: роли, tablespaces, внешние файлы, секреты и другие ресурсы, которые не входят в этот артефакт.
  3. Создайте архив выбранного формата и запишите имя, размер, версию инструмента и SHA-256 в manifest.
  4. Прочитайте список архива и сравните его с ожидаемыми объектами. Несовпадение останавливает проверку.
  5. Создайте изолированный кандидат. Проверьте адрес, credentials и отсутствие маршрута записи в источник.
  6. Выполните restore с сохранением stderr и кода выхода. Не трактуйте пустой stderr как полную проверку.
  7. Запустите structural checks: relations, владельцы и версия схемы. Затем запустите безопасные semantic checks из manifest.
  8. Запишите verdict и список непроверенных областей. Только после этого решайте, нужна ли отдельная проверка ролей, внешних файлов, retention или переключения.
\n

Ограничения и критерий готовности

\n

Этот учебный маршрут не измеряет RPO, RTO, скорость выгрузки, стоимость хранения или длительность restore. Он не проверяет шифрование, права доступа, репликацию и автоматическое расписание. Logical dump не заменяет физическое резервирование, continuous archiving или план восстановления всего кластера. Подход также не отвечает за данные, которые живут вне PostgreSQL.

\n

Не называйте пример production-результатом. Учебные имена, числа строк, checksum и результаты здесь не являются отчётом о реальной базе. В рабочей системе значения должен получить сам запуск на разрешённом стенде. Версию PostgreSQL и версию клиента нужно записать рядом с результатом: формат и поведение инструментов должны соответствовать поддерживаемой конфигурации.

\n

Критерий готовности проверяемый: для конкретного manifest другой оператор может найти нужный архив, подтвердить его digest, увидеть заявленные объекты, восстановить его только в изолированной цели и получить ожидаемые checks. При изменённом файле, неполном scope, неизвестной цели или расхождении результата маршрут останавливается с понятной причиной. Пока это не доказано повторяемым drill, в наличии есть файл, но нет подтверждённой резервной копии.

\n

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

\n" }