Резервное копирование и восстановление баз 1С на postgresql

1. alexey.karmanov 189 06.08.18 16:00 Сейчас в теме
Более опытные в вопросах postgresql коллеги, прошу вашей помощи - сам только осваиваю азы администрирования этой СУБД.

Сам я не администратор, а разработчик и эти основы нужные мне, чтобы добавить работу с postgresql в свою программу администрирования баз 1С.

Текущее окружение: Centos 7, PostgreSQL 9.6.9 (версия с патчами от 1С с сайта postgrespro.ru). Но версия ОС в рамках этой задачи не так важна, проблема повторяется и на Windows.

Сейчас передо мной стоят следующие задачи:

1. Написать скрипт выгрузки дампа базы в формате sql через pg_dump (архивация)
2. Написать скрипт загрузки этого дампа в эту же самую базы, выполнив этот скрипт через psql (восстановление базы из архива)

При этом вариант удаления и создания чистой базы перед восстановлением не подходит, так как эта операция потребовала бы остановки кластера 1с (dr op table не отрабатывает при соединениях с базой). И вообще сам вариант удаления и повторного создания базы для её восстановления кажется мне не совсем подходящим.

Соответственно скрипт дампа перед загрузкой архивной копии должен полностью очищать текущую базу, при этом всё это должно происходить до первой ошибки в рамках одной транзакции (должно быть восстановлено всё или ничего).

Скрипт архивации получился такой:
pg_dump.exe --quote-all-identifiers --format=plain --clean --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"BASE1С_NAME" --file="backup.sql"

Скрипт восстановления такой:
psql.exe --single-transaction --echo-errors --quiet --set="ON_ERROR_STOP=on" --file="backup.sql" --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"BASE1С_NAME"

Ключик --clean, который мы передали на вход pg_dump как раз и отвечает за генерацию SQL-кода в дампе, который выполняет полную очистку базы перед загрузкой в неё архивной копии.

И проблема возникает именно в этой части sql-дампа. Точная ошибка гласит: тип "public.mvarchar" не существует.

Знаю, что этот тип как раз и был добавлен патчами от 1С.

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

Судя по всему нужно делать постобработку (в части mvarchar) выгруженного дампа перед тем как отдавать его на выполнение pssql.
По теме из базы знаний
Вознаграждение за ответ
Показать полностью
Найденные решения
12. alexey.karmanov 189 07.08.18 07:12 Сейчас в теме
Так мне и не удалось научиться корректно очищать существующую базу для загрузки в неё архивной копии. Ничего не остаётся, кроме как загружать копию в новую базу.

Возможно это кому-то пригодится, получилось примерно так:

1. Делаем бэкап:
pg_dump.exe --quote-all-identifiers --format=plain --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"BASE1C" --file="BACKUP.sql"

2. Создаём новую базу с временным именем (пусть сейчас это будет temp)
createdb.exe --encoding=utf8 --template=template1 --username="USER" --host="HOST" --port=PORT temp

3. Загружаем в temp наш архив:
psql.exe --single-transaction --echo-errors --quiet --set="ON_ERROR_STOP=on" --file="BACKUP.sql" --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"temp"

4. Далее программно блокируем и выгоняем всех пользователей и фоновые. задания из базы через кластер 1с.

5. Далее программно через кластер очищаем свойство "база данных" у нашей базы. Это нужно, чтобы кластер отпустил базу в СУБД.

6. Далее переименовываем базу BASE1C в BASE1C_TEMP:

psql.exe --single-transaction --echo-errors --quiet --set="ON_ERROR_STOP=on" --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"template1" --command="ALT ER DATABASE BASE1C RENAME TO BASE1C_TEMP;"

7. Далее переименовываем нашу temp в BASE1C:

psql.exe --single-transaction --echo-errors --quiet --set="ON_ERROR_STOP=on" --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"template1" --command="ALT ER DATABASE temp RENAME TO BASE1C;"

8. Далее снова программно прописываем в свойства базы в кластере имя BASE1C

9. Наконец, удаляем базу BASE1C_TEMP:
dropdb.exe --username="USER" --host="HOST" --port=PORT BASE1C_TEMP

10. Пускаем всех обратно в базу.

Описанный алгоритм позволяет выполнить загрузку архивной копии в уже существующую базу максимально корректно и безопасно, насколько это возможно в условиях задачи.
MarchTomCat; Fox-trot; KAV2; oldcopy; +4 Ответить
Остальные ответы
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
9. Fox-trot 156 06.08.18 22:50 Сейчас в теме +5 $m
(1)
И вообще сам вариант удаления и повторного создания базы для её восстановления кажется мне не совсем подходящим.
а другого варианта и нет
11. alexey.karmanov 189 07.08.18 01:52 Сейчас в теме
(9) я пока надеюсь, что другой вариант всё же найдётся и вернусь к идее с удалением и повторным созданием базы уже в крайнем случае.

Дело в том, что вариант с очисткой и загрузкой можно делать в одной транзакции, в отличие от удаление и пересоздания базы.
2. herfis 498 06.08.18 16:05 Сейчас в теме
Рабочие варианты команд, которые я успешно юзал на базах 1С:
pg_dump -h localhost -U postgres -Fc -Z9 -c -f /backups/WorkBase.bak WorkBase
pg_restore -h localhost -U postgres -c -d WorkBase -v /backups/WorkBase.bak
4. alexey.karmanov 189 06.08.18 17:10 Сейчас в теме
(2) У меня этот вариант также отрабатывает с предупреждениями о mvarchar. То есть база восстанавливается (вроде бы даже рабочая), но наличие этих ошибок при восстановлении не позволяет принять этот вариант за рабочий (иначе как программно контролировать другие типы ошибок, которые могут возникнуть в будущем).

Кроме того хочется бэкап именно в формате sql, а не архивный вариант. Это более гибкий вариант с точки зрения преодоления возможных проблем при восстановлении созданного бэкапа.
6. herfis 498 06.08.18 17:20 Сейчас в теме
(4) У постгри, насколько я знаю, до последнего времени и не было бинарных бэкапов. Т.е. это тоже вроде в формате sql бэкап.
А с ошибкой твоей дело в том, что ключик очистки для pg_dump не выполняет волшебную команду чистки всей базы. Он просто генерит скрипт по предварительному удалению тех объектов, которые он затем создавать будет. Соответственно, если восстановление выполняется в непустую базу, то будет ругаться на попытку удаление всех объектов, которых в этой базе нет. По идее, для твоих целей чистить надо не ключами в pg_dump, а ключами в pg_restore, но я так ни разу не пробовал.
Хм... Курю мануалы, там хитрее. Погоди чуток..
3. Timur.V 78 06.08.18 16:59 Сейчас в теме
5. alexey.karmanov 189 06.08.18 17:11 Сейчас в теме
(3) Всё видел, но проблема специфична именно для баз 1С. В статье также нет подходящего способа применительно к нашему случаю.
7. herfis 498 06.08.18 17:30 Сейчас в теме
Покурил мануалы по pg_dump и pg_restore.
И вроде как там нет опции по предварительной полной очистке целевой базы :)
Опции очистки и там и там просто добавляют команды удаления того объекта, который восстанавливается из бэкапа перед его созданием. И соответственно всегда будет "левая" ошибка, если в целевой базе не было этого объекта перед восстановлением (это явно говорено в мануале).
Хоть сам объекты дропай :)
8. herfis 498 06.08.18 17:48 Сейчас в теме +5 $m
Погуглил. Оказывается что удалить все объекты базы не удаляя саму базу - задача нетривиальная.
Самое простое из предлагаемого - дропнуть схему (большинство объектов принадлежит схеме и тоже дропнутся). Но потом придется ее создать и дефолтные гранты восстановить.
Так что в принципе можно попробовать убрать опцию очистки базы из pg_dump и перед восстановлением бэкапа удалять/восстанавливать схему.
10. alexey.karmanov 189 07.08.18 01:50 Сейчас в теме
(8)
Предварительная очистка базы поддерживается. Ключик --clean для pg_dump генерирует как раз этот код для очистки. Который затем выполняется через psql. Это если мы делаем дамп в plain формате. Как раз в этом коде очистки базы и возникает конфликт с mvarchar (сегодня буду разбираться подробнее).

Если же custom (тот же sql, но пожатый), то восстанавливать надо через pg_restore и там тоже есть ключик --clean. Но проблема та же. Так как pg_restore распаковывает custom в тот же самый plain sql и так же запускает его на выполнение (не знаю только сам или через psql).

Спасибо за помощь, по результатам (если таковые будут) обязательно здесь отпишусь.
12. alexey.karmanov 189 07.08.18 07:12 Сейчас в теме
Так мне и не удалось научиться корректно очищать существующую базу для загрузки в неё архивной копии. Ничего не остаётся, кроме как загружать копию в новую базу.

Возможно это кому-то пригодится, получилось примерно так:

1. Делаем бэкап:
pg_dump.exe --quote-all-identifiers --format=plain --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"BASE1C" --file="BACKUP.sql"

2. Создаём новую базу с временным именем (пусть сейчас это будет temp)
createdb.exe --encoding=utf8 --template=template1 --username="USER" --host="HOST" --port=PORT temp

3. Загружаем в temp наш архив:
psql.exe --single-transaction --echo-errors --quiet --set="ON_ERROR_STOP=on" --file="BACKUP.sql" --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"temp"

4. Далее программно блокируем и выгоняем всех пользователей и фоновые. задания из базы через кластер 1с.

5. Далее программно через кластер очищаем свойство "база данных" у нашей базы. Это нужно, чтобы кластер отпустил базу в СУБД.

6. Далее переименовываем базу BASE1C в BASE1C_TEMP:

psql.exe --single-transaction --echo-errors --quiet --set="ON_ERROR_STOP=on" --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"template1" --command="ALT ER DATABASE BASE1C RENAME TO BASE1C_TEMP;"

7. Далее переименовываем нашу temp в BASE1C:

psql.exe --single-transaction --echo-errors --quiet --set="ON_ERROR_STOP=on" --dbname=postgresql://"USER":"PASSWORD"@"HOST:PORT"/"template1" --command="ALT ER DATABASE temp RENAME TO BASE1C;"

8. Далее снова программно прописываем в свойства базы в кластере имя BASE1C

9. Наконец, удаляем базу BASE1C_TEMP:
dropdb.exe --username="USER" --host="HOST" --port=PORT BASE1C_TEMP

10. Пускаем всех обратно в базу.

Описанный алгоритм позволяет выполнить загрузку архивной копии в уже существующую базу максимально корректно и безопасно, насколько это возможно в условиях задачи.
MarchTomCat; Fox-trot; KAV2; oldcopy; +4 Ответить
13. KAV2 156 08.08.18 07:02 Сейчас в теме
(12) А почему просто не удалить старую базу, перед восстановлением из бэкапа?

То есть:

1) Удаляем базу
dropdb dbname

2) Создаем новую пустую со старым имененм
createdb dbname

3) восстанавливаем в нее бэкап.
gunzip -c filename.gz | psql dbname
14. alexey.karmanov 189 08.08.18 12:27 Сейчас в теме
(13) ну как же...

Базу мы удалим, а затем выяснится что у нас или прав нет на создание новой базы или восстановить бэкап из-за ошибок в чистую базу не получилось... а базу мы уже благополучно удалили и оставили пользователя ни с чем.

Столько промежуточных шагов исключительно для надежности и отказоустойчивости операции в целом.
MarchTomCat; v.l.; Fox-trot; +3 Ответить
15. KAV2 156 08.08.18 15:06 Сейчас в теме
(14) Понял. Ну вы изначально вовсе хотели перезаписать базу, допустим была бы такая специальная команда - перезаписать базу данных при восстановлении бэкапа, тогда в процессе при возникновении ошибки можно было аналогичным образом потерять данные.
17. alexey.karmanov 189 09.08.18 02:25 Сейчас в теме
(15) "тогда в процессе при возникновении ошибки можно было аналогичным образом потерять данные"

Нет, нельзя было бы. Ведь это делалось в рамках одной транзакции (ключик --single-transaction).
19. KAV2 156 09.08.18 05:02 Сейчас в теме
16. oldcopy 173 08.08.18 22:50 Сейчас в теме
(12) В общем и целом - это единственно верное решение. Только вот переименовывать базу SQL туда-сюда-обратно я смысла не вижу. Создали вместо базы Base1, базу Base2, залили в нее дамп и меняем в настройках кластера в свойствах ИБ Base1 на Base2.

В итоге получится 1-5, 8, 10. Пункт 9, удаление базы я бы тоже убрал. Есть она не просит, пусть будет, мало ли что. Тогда можно отказаться от 1, зачем делать бекап, если мы не трогаем исходную базу. При этом нормально так выиграем по времени.
MarchTomCat; +1 Ответить
18. alexey.karmanov 189 09.08.18 02:32 Сейчас в теме
(16) Чего-то я не понял несколько... :)

Речь же о том, что мы добавляем эту возможность в универсальный инструмент, позволяющий делать архивные копии (пункт 1) и при необходимости восстанавливать их в базу (остальные пункты). Причём восстанавливать как в ручную, так и автоматически (например, при неудачном обновлении).

При этом в результате этих операций (бэкап и/или восстановление) в СУБД не должны появляться и оставаться новые базы, как и не должно меняться имя самой базы.
20. oldcopy 173 09.08.18 11:45 Сейчас в теме
(18)
При этом в результате этих операций (бэкап и/или восстановление) в СУБД не должны появляться и оставаться новые базы, как и не должно меняться имя самой базы.


Довольно спорный момент. Если что-то пойдет не так, можно остаться без базы вообще. История знает много случаев, когда заливали не тот бекап не в ту базу и т.д. и т.п. Полная автоматизация процесса восстановления - тоже опасный инструмент. Ситуации бывают разные, чаще всего восстанавливать лучше руками, чтобы был контроль за ситуацией.

Если говорить о скрипте для автоматического развертывания бекапов, то на мой взгляд достаточно создать новую базу и перепрописать ее в свойствах ИБ 1С. Если же не хотите менять имя, то лучше отключить старую базу, переименовать, создать новую с тем же именем и залить туда дамп. В этом случае у вас гарантированно останется рабочая база на момент перед восстановлением. Если не нужна - можно всегда удалить потом.
21. alexey.karmanov 189 09.08.18 13:11 Сейчас в теме
Я думаю, что вариант "не удаления старой базы после восстановления" будет опциональным. Это хорошая идея.
23. oldcopy 173 09.08.18 13:21 Сейчас в теме
(21) Подкину еще идею, иногда старую базу (до восстановления) нужно иметь рядом, скажем для "разбора полетов", также опционально ее можно сразу добавлять в кластер 1С.
24. alexey.karmanov 189 09.08.18 14:31 Сейчас в теме
(23) спасибо, записал в список идей :)
22. alexey.karmanov 189 09.08.18 13:13 Сейчас в теме
А по поводу "восстановил не в ту базу". В моём инструменте администрирования при восстановлении из резервной копии (если этот процесс запущен руками) всегда предлагается в начале создать резервную копию текущей базы. Поэтому база до восстановления в любом случае останется.

Если же это происходит при автоматическом обновлении баз (или тестировании), то там тоже перед началом операции создаётся резервная копия базы. Поэтому тоже мы ничего не потеряем даже при самом плохом сценарии.
Оставьте свое сообщение
Вакансии
1С аналитик
Москва
зарплата от 210 000 руб.
Полный день

Руководитель направления 1С
Москва
зарплата от 350 000 руб.
Полный день

1С Программист
Москва
зарплата от 180 000 руб.
Полный день

Программист 1С
Москва
зарплата от 180 000 руб. до 220 000 руб.
Полный день

Аналитик 1С / Бизнес-аналитик
Нижний Новгород
зарплата от 100 000 руб. до 250 000 руб.
Временный (на проект)