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

Выборочная копия схемы уменьшает размер, но увеличивает число предположений. При --schema pg_dump не пытается включить объекты из других схем, от которых выбранные объекты зависят. В такой копии по умолчанию не окажутся и large objects. Поэтому scope.includes должен описывать минимальный набор, нужный целевой базе, а scope.excludes — всё, что проверка сознательно оставляет за пределами.

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

Команды выше — учебный пример для PostgreSQL 12. Имена базы и файла вымышлены. Ключи --schema делают команду согласованной с примером scope, а --strict-names останавливает dump, если указанная схема не найдена. Это всё ещё не доказывает наличие зависимого типа или функции из другой схемы: их нужно добавить в scope либо доказать отдельной проверкой.

\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  \"toolVersion\": {\"client\": \"<pg_dump version>\", \"server\": \"<PostgreSQL version>\"},\n  \"scope\": {\"includes\": [\"schema:catalog\", \"schema:reference\"], \"excludes\": [\"cluster roles\", \"tablespaces\", \"large objects\", \"external files\"]},\n  \"restoreChecks\": {\"relations\": [\"catalog.items\", \"reference.codes\"], \"rows\": {\"catalog.items\": 3, \"reference.codes\": 2}},\n  \"safety\": \"только изолированный учебный кандидат\"\n}
\n

Числа строк и значение <hash> здесь учебные: их нельзя переносить в рабочий manifest. После dump оператор записывает фактический digest, размер и версии клиента и сервера. Manifest не должен содержать пароль или рабочую строку подключения; достаточно идентификатора разрешённой цели, если он позволяет независимо проверить endpoint и владельца.

\n

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

\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 завершает отправку SQL-команд при первой ошибке, но не превращает restore в доказательство успеха. После команды нужно отдельно проверить ожидаемые relations, версию схемы и безопасный набор синтетических строк. Проверка не должна менять источник, отправлять письмо, списывать деньги или обращаться к внешней системе.

\n

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

\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

Выборочный dump схем не гарантирует восстановление зависимостей из других схем, а dump одной базы не содержит cluster-wide роли и tablespaces. Если приложению нужны large objects, расширения, секреты или внешние файлы, их надо включить в отдельный контракт и пройти отдельным drill. Версия клиента и версия сервера должны соответствовать поддерживаемой конфигурации; ссылка на документацию PostgreSQL 12 ниже фиксирует контекст примера, а не заменяет проверку вашей среды.

\n

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

\n

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

\n

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

\n" }