3 октября pg_dump базы DB2 оборвался через 29 секунд. Архив весил 95 043 271 байт вместо 1 120 951 158 байт. При этом gzip -t вернул код 0. Импорт через zcat | psql — тоже.

После восстановления в PostgreSQL не оказалось ни одного индекса. Этот случай описан в эксперименте «Копия базы 1С оборвалась, а gzip -t и psql вернули 0» на Habr. Он показал границу привычных проверок: исправный gzip-файл не доказывает, что pg_dump выгрузил базу целиком.

Если у вас каждую ночь появляется .sql.gz, самого файла мало. За несколько минут нужно подтвердить две вещи: дамп завершился штатно, а восстановленная база открывается через 1С.

Почему две проверки вернули код 0

Команда gzip -t проверяет структуру gzip-потока. Повреждённый архив она обнаружит. Но узнать, сколько данных успел передать pg_dump, не сможет.

В исследованном сбое pg_dump остановился, а gzip штатно дописал полученный поток. Для gzip архив был исправен. Для PostgreSQL — оборван.

Вторая ловушка скрыта в конвейере:

pg_dump DB2 | gzip > DB2.sql.gz

Без pipefail оболочка возвращает код последней команды. Если pg_dump упал, а gzip записал полученные данные и вернул 0, весь конвейер выглядит успешным.

Обычный запуск psql тоже не подходит для приёмки копии:

zcat DB2.sql.gz | psql -d DB2_test

Без ON_ERROR_STOP=1 клиент продолжает читать SQL после ошибок. Если нет общей транзакции, уже созданные объекты остаются в тестовой базе. В эксперименте импорт оборванного дампа вернул 0, хотя индексов в восстановленной базе не было.

ПроверкаЧто она подтверждаетЧего она не подтверждает
gzip -t copy.sql.gzgzip-поток читается без ошибкиpg_dump дошёл до конца базы
Обычный zcat | psqlpsql прочитал доступную часть SQLВсе команды выполнены, схема и данные полны
Поиск PostgreSQL database dump completeТекстовый pg_dump записал штатную финальную строкуВосстановленная база работает через 1С
psql -1 -v ON_ERROR_STOP=1Импорт остановится при первой ошибке и пройдёт одной транзакциейПрикладные объекты 1С открываются без ошибок

Код 0 говорит только об одной завершившейся команде. Полноту копии подтверждает цепочка проверок, а не один результат.

Ловите обрыв во время создания копии

Сначала включите pipefail. Тогда оболочка учтёт ошибку любой команды в конвейере, а не только последней.

В эксперименте конвейер без pipefail вернул 0 после принудительной остановки pg_dump. С pipefail тот же сценарий завершился кодом 137: процесс получил сигнал KILL.

Заготовка для текстового дампа:

#!/usr/bin/env bash

set -o pipefail

database="DB2"
backup="DB2.sql.gz"
temporary="${backup}.tmp"

if ! pg_dump -Fp "$database" | gzip > "$temporary"; then
 echo "pg_dump или gzip завершился с ошибкой" >&2
 exit 1
fi

if ! zcat "$temporary" \
 | tail -n 5 \
 | grep -q 'PostgreSQL database dump complete'; then
 echo "В дампе нет признака штатного завершения" >&2
 exit 1
fi

mv -- "$temporary" "$backup"
echo "Копия создана: $backup"

Скрипт пишет данные во временный файл. Рабочее имя появляется лишь после успешного конвейера и проверки финальной строки. Поэтому cron не примет обрывок за новую копию по одному факту существования файла.

Не записывайте сообщение об успехе раньше проверки $? или конструкции if. Иначе журнал подтвердит лишь запуск задания. Создание законченного дампа он не подтвердит.

Но pipefail закрывает только одну часть задачи. Он ловит ошибку работающего процесса, но ничего не говорит о старых файлах в хранилище.

Если выбираете схему защиты, а не отдельную команду, сравните варианты копирования PostgreSQL для базы 1С. pg_dump создаёт логический дамп и не возвращает базу к выбранной секунде. Для PITR нужна физическая копия от pg_basebackup и архив WAL.

Текстовый дамп должен заканчиваться служебной строкой

В описании проверки резервных копий PostgreSQL, опубликованном Byzoni, указан признак полного текстового дампа -Fp. В конце должна стоять строка:

PostgreSQL database dump complete

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

zcat copy.sql.gz \
 | tail -n 5 \
 | grep -c 'PostgreSQL database dump complete'

В эксперименте команда вывела 0 для оборванной копии и 1 для полной. Здесь результат grep -c означает количество найденных строк. Это не код завершения всей проверки.

В скрипте удобнее вызвать grep -q:

if zcat copy.sql.gz \
 | tail -n 5 \
 | grep -q 'PostgreSQL database dump complete'; then
 echo "Финальная строка найдена"
else
 echo "Дамп оборван или записан не в plain-формате" >&2
 exit 1
fi

Финальная строка подтверждает, что pg_dump дошёл до штатного конца. Прикладную целостность она не проверяет. Восстановление всё равно понадобится.

Для custom-формата -Fc эта проверка не подходит. В том же эксперименте pg_restore -l прочитал оглавление неполного архива и вернул 0. Полное чтение через pg_restore -f /dev/null обнаружило обрыв и вернуло 1.

Формат копииБыстрая проверкаДостаточный признак окончанияСледующий шаг
Plain SQL, pg_dump -FpПрочитать конец файлаЕсть строка PostgreSQL database dump completeИмпортировать через строгий psql
Custom, pg_dump -Fcpg_restore -l читает оглавлениеОдного чтения оглавления недостаточноВыполнить pg_restore -f /dev/null, затем тестовое восстановление
Физическая копия pg_basebackupНайти backup_manifestpg_verifybackup закончил проверку без ошибкиПоднять отдельный кластер и проверить базу
Физическая копия с WALПроверить базовую копию и доступность WALЕсть записи WAL до требуемой точкиВыполнить пробный PITR в отдельном контуре

У каждого формата своя проверка. Одной команды для .sql.gz, архива -Fc и каталога pg_basebackup нет.

pg_verifybackup работает только с физическими копиями

Документация PostgreSQL 13 описывает pg_verifybackup как утилиту для проверки копий от pg_basebackup. Она читает backup_manifest, сверяет список и размеры файлов, проверяет контрольные суммы и ищет нужные записи WAL через pg_waldump.

Для SQL-дампов эта команда не предназначена:

pg_verifybackup /backup/base

Документация PostgreSQL отдельно предупреждает: успешный результат pg_verifybackup не исключает всех проблем. После проверки нужно запустить тестовый кластер и проверить базу.

Следите и за версиями. Для проверки WAL документация предписывает брать pg_verifybackup и pg_waldump той же версии, которой создана копия.

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

Восстановление должно останавливаться при первой ошибке

Разворачивайте текстовый дамп в отдельную пустую базу. Не загружайте его поверх рабочего экземпляра. Иначе частичный импорт смешает старые объекты с восстановленными.

Для строгой проверки включите общую транзакцию и остановку на первой ошибке:

createdb DB2_restore_test

zcat DB2.sql.gz \
 | psql \
 -1 \
 -v ON_ERROR_STOP=1 \
 -d DB2_restore_test

Ключ -1 помещает импорт в одну транзакцию. Переменная ON_ERROR_STOP=1 останавливает psql после первой ошибки.

В эксперименте такой запуск завершился кодом 3. Транзакция откатилась, поэтому после теста не осталась внешне правдоподобная база с частью объектов.

Custom-архив восстанавливают через pg_restore:

createdb DB2_restore_test

pg_restore \
 --exit-on-error \
 --single-transaction \
 --dbname=DB2_restore_test \
 DB2.dump

Ключи должны соответствовать вашей версии PostgreSQL. Не смешивайте утилиты разных выпусков, пока не проверите их совместимость.

Ошибка schema already exists обычно указывает на импорт в непустую базу. Создайте новый тестовый экземпляр. Не пытайтесь вручную вычистить следы предыдущего восстановления.

Порядок действий для PostgreSQL, SQL Server и файловой базы есть в инструкции по развёртыванию копии 1С отдельно от рабочей базы. Здесь нужен изолированный экземпляр, куда не входят пользователи.

Успешный импорт ещё не возвращает рабочую 1С

После завершения psql или pg_restore подключите восстановленную информационную базу к тестовому контуру 1С. SQL-клиент проверяет исполнение дампа. О требованиях прикладной конфигурации он ничего не знает.

Минимальная приёмка состоит из трёх действий:

  1. Добавьте восстановленную информационную базу в список тестового кластера 1С.
  2. Откройте её через клиент 1С под отдельной учётной записью.
  3. Откройте прикладной объект, который читает данные и обращается к индексам.

Объект зависит от конфигурации. В одной базе подойдёт список документов, в другой — регистр или отчёт. Универсального числа документов нет: проверка должна задеть тот сценарий, ради которого вы восстанавливаете базу.

Наличие индексов проверьте отдельно. В описанном сбое импорт вернул 0, но PostgreSQL содержал ноль индексов. Принимать такой экземпляр по факту подключения к СУБД нельзя.

Как встроить проверку рядом с cron

Поднимать тестовую базу после каждого ночного дампа необязательно. Но создание файла и пробное восстановление лучше разделить на два задания. У каждого должен быть собственный результат.

Первое задание создаёт копию:

Второе задание принимает копию:

Не объединяйте эти результаты сообщением «скрипт отработал». В журнале нужны отдельные статусы: создание файла, проверка формата, восстановление и вход через 1С. Тогда место сбоя видно сразу.

Если из копий собирают контуры разработки, появляется ещё одна граница. Рабочие персональные данные нельзя передавать вместе с дампом. В отдельном материале показано, как pg_anon формирует обезличенную копию прямо во время выгрузки.

Правило приёмки резервной копии

Принимайте копию только после четырёх результатов:

Сегодня добавьте в скрипт pipefail и поиск финальной строки. Потом отдельно разверните последнюю копию. Ошибка на любом этапе означает одно: этот файл пока нельзя считать резервной копией.

gzip -t проверяет архив. psql исполняет доступный SQL. Пригодность копии подтверждает только вся цепочка — от кода pg_dump до входа в восстановленную базу через 1С.

pg-dump postgresql администрирование 1с восстановление базы резервное копирование