DarkRiDDeR14 мин

Разбор учебного restore drill: от архива к доказательству

ДанныеНадёжностьРазбор

Симптом в разборе простой: команда видит свежий archive, но не может ответить, что произойдёт после команды restore. Неизвестны scope, ожидаемые объекты, допустимая цель и проверка результата. Цена не в том, что «не хватает документации»; в реальном сбое такой пробел превращает восстановление в эксперимент над источником. Оператор выбирает файл по дате, запускает знакомую команду и получает либо неполный набор, либо ещё одну неизвестную переменную вместо доказательства.

Ниже не production-инцидент и не отчёт о достигнутом RPO/RTO. Это синтетический разбор учебной пробы: две логические схемы, один custom archive, manifest с SHA-256 и изолированный кандидат. Цель — показать, как runbook превращает расплывчатый вопрос «backup есть?» в последовательность «симптом → причина → проверка → действие». После такой пробы можно честно перечислить непроверенные границы и назначить следующую проверку, не притворяясь, что уже проведён disaster recovery.

Собираем пакет доказательств до первой команды

Для drill нужны не только archive и пароль к базе — пароль в этом материале намеренно отсутствует. Нужны четыре безопасных артефакта: manifest, сам archive, checksum и лист ожидаемых checks. Manifest связывает файл с scope; checksum связывает manifest с конкретными bytes; список checks связывает restore с наблюдаемым результатом. Если начать с команды без этих связей, любая ошибка будет выглядеть одинаково: «не получилось». Тогда нельзя отличить повреждённый файл от неверной цели или разумно спорить, была ли нужная таблица вообще включена.

Учебный manifest не хранит реальных имён окружений и не выдаёт секреты. Он использует вымышленные labels catalog и reference, а excludes прямо называют cluster roles, tablespaces и external files. Это важнее красивого JSON: исключённая область должна быть видна до drill, чтобы позже никто не искал её в archive. PostgreSQL 12 разделяет dump одной базы и cluster-wide объекты; если роли нужны будущей цели, это отдельная часть плана, а не скрытый побочный эффект pg_dump.

Вертикальная схема диагностики учебного restore drill: для каждого симптома от отсутствующего scope до несовпавшего checksum и пустой relation показаны причина, точная проверка и безопасное действие; путь не содержит операций над источником
Схема помогает не перескакивать к restore. Сначала проверяем contract и байты, затем безопасность цели, затем структуру и синтетические данные результата.

Учебная шкала не равна измеренному RPO или RTO

В runbook полезно записывать последовательность событий, но нельзя подменять их историей о измеренном времени. Отметка restore_started нужна, чтобы в следующем разрешённом drill измерить интервал до semantic_checks_passed. До этого она не становится RTO. Дата archive помогает задать вопрос о максимально допустимой потере данных, но не становится RPO без требований к данным, частоты копий и понимания журналов изменений. В таблице ниже слова «вопрос» намеренны: они удерживают автора от ложной цифры.

Что учебный drill фиксирует, а что оставляет вопросом
НаблюдениеЧто можно записать сейчасКакой вопрос остаётсяСледующее действие
Дата и ID archiveкакой учебный артефакт участвовал в пробекакая потеря данных допустима между копиямисогласовать требования данных до следующего drill
Начало и конец restoreдве отметки учебного маршрутасколько времени допустимо для конкретного сервисаизмерить на разрешённой цели, не называть интервал RTO заранее
Checksumbytes совпали или нетдостаточен ли scope для прикладного восстановлениясверить includes/excludes и checks
Structural checksдве relations и синтетические счётчикиготово ли всё приложение и внешние зависимостирасширить contract или зафиксировать исключение
Isolated targetисточник не был частью restoreподходит ли цель для будущего сценарияописать требования к отдельному стенду

Такая таблица делает разбор полезнее отчёта «успешно». Она показывает, что было фактически наблюдаемо, и не превращает план в факт. Если будущая команда измерит длительность, ей всё равно придётся указать условия: размер данных, версия инструмента, параллелизм, доступность цели, набор checks. Без условий одно число не переносится на следующий restore. Здесь автор 2020 года формирует дисциплину фиксации, а не заявляет опыт управления программой надёжности.

Один synthetic trace вместо легенды о катастрофе

Вместо реального лога используется короткий учебный trace. Он показывает порядок решений: сначала читают scope, затем сравнивают digest, потом смотрят list archive, восстанавливают только в отдельного кандидата и только после этого запускают semantic checks. У каждой строки есть значение для диагностики. Если trace заканчивается после hash, это не «почти успешный restore»: это означает, что evidence о результате ещё не получен.

{"step":"scope_read","result":"catalog + reference; cluster roles excluded","kind":"training"}
{"step":"artifact_hash","result":"matches manifest","kind":"training"}
{"step":"archive_list","result":"expected relations listed","kind":"training"}
{"step":"restore_candidate","result":"completed without source overwrite","kind":"training"}
{"step":"semantic_check","result":"2 relations and expected row counts","kind":"training"}
{"step":"verdict","result":"evidence recorded; next drill still required","kind":"training"}

Этот пример намеренно не содержит host, username, credentials, реальных timestamps или описания пользовательских данных. В настоящий runbook можно добавить минимальный correlation ID и место хранения evidence, но не следует логировать сам archive, содержимое dump или секретные connection strings. Для учебного случая хватает backup ID, версии manifest, результата gate и причины остановки. Это делает заметки сравнимыми между drills и не превращает журнал в ещё одну копию чувствительных данных.

Диагностика начинается с вопроса о scope

Представим первый симптом: archive существует, но restore list не содержит reference.codes. Самая вероятная причина не в checksum: hash может идеально совпадать с теми байтами, которые изначально были сохранены неполно. Проверка — сравнить list archive с scope.includes и restoreChecks.relations из manifest. Действие — остановить restore, открыть creation command и решить, должен ли scope расшириться либо relation была внешней зависимостью, которую нужно описать отдельно. Повторный запуск restore в надежде, что relation «появится», не добавляет доказательств.

Второй симптом: list выглядит правильно, но checksum не совпал. Причина находится между записью manifest и текущим файлом: другой archive под тем же именем, неполная передача, повреждение или ошибка выбора. Проверка однозначна — повторно вычислить SHA-256 над фактически используемыми bytes. Действие тоже однозначно — не восстанавливать этот файл. Нужно получить доверенный artifact или создать новую учебную копию, а затем обновить manifest только вместе с ним. Подмена hash в JSON «чтобы пройти gate» уничтожает сам смысл контроля.

После checksum всё ещё нельзя трогать источник

Третий симптом: bytes и list прошли, но оператор собирается восстановить «туда, где видно данные». Причина — runbook не делает безопасную цель явным входом. Проверка должна быть предельно скучной: имя кандидата и запрет перезаписи источника зафиксированы до команды. Действие — создать отдельного учебного кандидата и повторить проверку цели. Даже маленький drill не оправдывает запуск с destructive flags ради удобства. Если под рукой нет безопасной цели, честный verdict — «restore не проверен», а не «backup успешен».

Четвёртый симптом: restore завершился, но relation пуста или schema version не та. Причина может быть в scope, версии инструмента, неполном содержимом или в слишком слабом критерии. Проверка состоит из заранее записанных read-only запросов против кандидата и сопоставления с manifest. Действие — сохранить фактический verdict, не маскировать его ручной вставкой строк и не менять expected count после факта. Если критерий был неверным, обновляют contract, создают новый артефакт и повторяют пробу как новую версию, а не переписывают историю старой.

Фикстура проверяет контракт, а не инфраструктуру

В revision-модуле есть небольшая in-memory fixture. Она строит synthetic manifest для двух relations, вычисляет SHA-256 фиксированных bytes и проверяет восемь условий: digest совпал, scope полон, safety запрещает overwrite источника, relations и rows совпали, изменение bytes отклонено, а суженный scope отклонён. У fixture нет файла, сети, PostgreSQL, cron, облачного хранилища или настоящего restore. Поэтому её можно запускать как проверку логики статьи, но нельзя прикладывать вместо реального drill.

# Запуск учебной fixture из revision-модуля:
node web/scripts/upgrade-2020-10.mjs --verify-fixture

# Ожидаем не скорость, а логические результаты:
# checksumMatches=true, scopeMatches=true, expectedRowsPresent=true
# alteredBytesRejected=true, narrowedScopeRejected=true, sourceWasUntouched=true

Нумерованный маршрут разбора

  1. Назвать backup ID и прочитать manifest целиком: format, includes, excludes, checksum и checks. Не выбирать archive только по дате в имени.
  2. Сверить scope с вопросом восстановления. Если нужная область не заявлена, зафиксировать это как design gap, а не как сбой pg_restore.
  3. Вычислить checksum фактического archive и сравнить с manifest. При несовпадении остановить маршрут до попытки чтения или restore.
  4. Получить list archive и сравнить его с ожидаемыми relations. Расхождение отделяет проблему creation path от проблемы target.
  5. Подтвердить isolated candidate и запрет действий над источником. Отсутствие безопасной цели — корректная причина не запускать drill.
  6. Восстановить archive в кандидате, выполнить read-only structural and semantic checks, сохранить результат и список непокрытых областей.
  7. Сделать из найденного пробела конкретную правку manifest, команды или checks и назначить новый учебный drill. Не превращать один запуск в заявление о готовности к любой аварии.

Как выглядит честный итог

Честный verdict после учебного drill может быть положительным и всё равно ограниченным: «вымышленный custom archive с двумя схемами совпал с manifest, развернулся в isolated candidate, прошёл два структурных checks; роли, tablespaces, внешние файлы, реальные требования к потере данных и сроки не проверялись». Такой текст полезнее громкого «резервное копирование настроено». Он сохраняет путь, по которому другой инженер сможет повторить проверку, и оставляет список того, что ещё нужно обсудить до любого production recovery.

Документация PostgreSQL помогает не размывать эту границу: logical dump одной базы, archive format и восстановление через pg_restore — конкретные механизмы, а не общая метафора «бэкапа». NIST-определённый SHA-256 даёт проверку bytes, но не заменяет semantic check. Соединяя их в manifest и drill, мы не обещаем невозможного; мы получаем один контролируемый путь, который можно сделать лучше после следующего измерения.

Имена, данные, timestamps, размеры, команды и результаты ниже учебные. Здесь нет реальной базы, access key, production-окружения или отчёта о disaster recovery.

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

  • PostgreSQL 12: Backup and Restore — разделяет SQL dump, копирование на уровне файловой системы и continuous archiving; у способов разные предпосылки и границы восстановления
  • PostgreSQL 12: pg_dump — документирует согласованный логический dump, форматы custom и directory, а также ограничение выборки схемы или таблицы без зависимостей
  • PostgreSQL 12: pg_restore — описывает восстановление non-plain archive, просмотр содержания и параметры, которые нужно сверять с версией выбранного инструмента
  • PostgreSQL 12: pg_dumpall — показывает отдельную область cluster-wide объектов, включая роли; один dump базы не следует выдавать за копию всего кластера
  • NIST FIPS 180-4: Secure Hash Standard — задаёт семейство SHA-2; hash сверяет байты артефакта, но сам по себе не доказывает пригодность результата к восстановлению