# Настройка непрерывной архивации

Архив будем хранить в каталоге /var/lib/postgresql/archive. Он должен быть доступен пользователю-владельцу PostgreSQL.

    postgres$ mkdir /var/lib/postgresql/archive

В реальной практике архив может размещаться на отдельном сервере или дисковой системе с доступом по сети.

Включим режим архивирования и установим команду копирования заполненных сегментов журнала.

Выполняем команды от имени суперпользовательской роли:

    student$ psql -U postgres -c "ALTER SYSTEM SET archive_mode = on"

    ALTER SYSTEM

    student$ psql -U postgres -c "ALTER SYSTEM SET archive_command = 'test ! -f /var/lib/postgresql/archive/%f && cp %p /var/lib/postgresql/archive/%f'"

    ALTER SYSTEM

В archive_command мы сначала проверяем наличие файла с указанным именем в архиве, и копируем его только в случае отсутствия.

В archive_command можно указать произвольную команду, лишь бы она завершалась со статусом 0 только в случае успеха. Например, можно организовать сжатие архивируемых сегментов:

    α=> -- SET archive_command = 'test ! -f /var/lib/postgresql/archive/%f && gzip <%p >/var/lib/postgresql/archive/%f'

В идеале команда архивирования должна выполнять sync, чтобы файл гарантированно попал в энергонезависимую память. Иначе при сбое он может пропасть из архива, а сервер может успеть стереть его из каталога pg_wal.

В общем случае удобно поместить всю необходимую логику архивирования в отдельный скрипт и вызываеть его:

    α=> -- SET archive_command = 'archive.sh "%f" "%p"'

Для того, чтобы настройки вступили в силу, потребуется рестарт сервера.

    α=> \q

    student$ sudo pg_ctlcluster 10 alpha restart

Проверим работу настройки. Создадим базу и таблицу.

    student$ psql

    α=> CREATE DATABASE backup_archive;

    CREATE DATABASE

    α=> \c backup_archive

    You are now connected to database "backup_archive" as user "student".

    α=> CREATE TABLE t(s text);

    CREATE TABLE

    α=> INSERT INTO t VALUES ('Привет, мир!');

    INSERT 0 1

Вот какой сегмент WAL используется сейчас:

    α=> SELECT pg_walfile_name(pg_current_wal_lsn());

         pg_walfile_name
    --------------------------
     000000010000000000000003
    (1 row)

Обратите внимание: первые восемь цифр в имени файла - номер текущей ветви времени.

Чтобы заполнить файл, пришлось бы выполнить большое количество операций, но в тестовых целях проще принудительно переключить сегмент:

    student$ psql -U postgres -c "SELECT pg_switch_wal()"

     pg_switch_wal
    ---------------
     0/3026220
    (1 row)

    α=> INSERT INTO t VALUES ('Доброе утро, страна!');

    INSERT 0 1

Сегмент сменился:

    α=> SELECT pg_walfile_name(pg_current_wal_lsn());

         pg_walfile_name
    --------------------------
     000000010000000000000004
    (1 row)

А предыдущий должен был попасть в архив. Проверим:

    postgres$ ls -l /var/lib/postgresql/archive

    total 16384
    -rw------- 1 postgres postgres 16777216 июн 13 19:58 000000010000000000000003

Таким образом, архивация насторена и работает.

# Базовая резервная копия

    postgres$ rm -rf /var/lib/postgresql/10/beta/*

Поскольку мы используем архив, попросим pg_basebackup не добавлять файлы журнала к резервной копии:

    postgres$ pg_basebackup -U student --wal-method=none --pgdata=/var/lib/postgresql/10/beta

    NOTICE:  pg_stop_backup complete, all required WAL segments have been archived

Каталог pg_wal резервной копии пуст:

    postgres$ ls -l /var/lib/postgresql/10/beta/pg_wal/

    total 4
    drwx------ 2 postgres postgres 4096 июн 13 19:59 archive_status

А в архиве прибавилось файлов:

    postgres$ ls -l /var/lib/postgresql/archive

    total 49156
    -rw------- 1 postgres postgres 16777216 июн 13 19:58 000000010000000000000003
    -rw------- 1 postgres postgres 16777216 июн 13 19:58 000000010000000000000004
    -rw------- 1 postgres postgres 16777216 июн 13 19:59 000000010000000000000005
    -rw------- 1 postgres postgres      302 июн 13 19:59 000000010000000000000005.00000024.backup

Заглянем в сгенерированный файл метки:

    postgres$ cat /var/lib/postgresql/10/beta/backup_label

    START WAL LOCATION: 0/5000024 (file 000000010000000000000005)
    CHECKPOINT LOCATION: 0/5000058
    BACKUP METHOD: streamed
    BACKUP FROM: master
    START TIME: 2018-06-13 19:58:59 MSK
    LABEL: pg_basebackup base backup

Главная информация в этом файле - указание начальной точки для восстановления (строка START WAL LOCATION). Теоретически восстановление можно начать и с более ранней (но не более поздней) позиции, но это потребует больше времени.

Эту же строку надо принимать во внимание, определяя, какие файлы из архива еще нужны, а какие можно удалить.

# Восстановление из базовой резервной копии

Изменим порт:

    postgres$ echo 'port = 5433' >> /var/lib/postgresql/10/beta/postgresql.auto.conf

Перенаправим непрерывное архивирование в другой каталог, чтобы не возникало коллизий с работающим первым сервером:

    postgres$ mkdir /var/lib/postgresql/archive_beta

    postgres$ sed -i 's/\/archive/\/archive_beta/g' /var/lib/postgresql/10/beta/postgresql.auto.conf

Вот что получилось:

    postgres$ cat /var/lib/postgresql/10/beta/postgresql.auto.conf

    # Do not edit this file manually!
    # It will be overwritten by the ALTER SYSTEM command.
    archive_mode = 'on'
    archive_command = 'test ! -f /var/lib/postgresql/archive_beta/%f && cp %p /var/lib/postgresql/archive_beta/%f'
    port = 5433

Теперь надо создать файл recovery.conf. В простейшем случае в него достаточно поместить команду восстановления, которая будет копировать указанный сегмент WAL обратно из архива по указанному пути:

    postgres$ echo "restore_command = 'cp /var/lib/postgresql/archive/%f %p'" >/var/lib/postgresql/10/beta/recovery.conf

Если в recovery.conf не указана целевая точка восстановления (один из параметров recovery_target*), то к базовой резервной копии будут применены записи WAL из всех файлов в архиве.

Следует иметь в виду, что самые последние изменения не попадут в архив, пока очередной сегмент WAL не будет заполнен (или пока не пройдет время, указанное в параметре archive_timeout).

    α=> INSERT INTO t VALUES ('Еще не в архиве');

    INSERT 0 1

Запускаем сервер.

    student$ sudo pg_ctlcluster 10 beta start

Файл recovery.conf переименовался в recovery.done:

    postgres$ ls -l /var/lib/postgresql/10/beta/recovery.*

    -rw-r--r-- 1 postgres postgres 57 июн 13 19:59 /var/lib/postgresql/10/beta/recovery.done

Проверим, что восстановлено в таблице:

    student$ psql -p 5433 -d backup_archive

    β=> SELECT * FROM t;

              s
    ----------------------
     Привет, мир!
     Доброе утро, страна!
    (2 rows)

Как и ожидалось, последняя строка не попала в архив и не восстановилась.

Что стало с ветвью времени после восстановления?

    β=> SELECT pg_walfile_name(pg_current_wal_lsn());

         pg_walfile_name
    --------------------------
     000000020000000000000006
    (1 row)

Номер увеличился на единицу.

В каталоге pg_wal появился файл истории, соответствующий этой ветви:

    postgres$ ls -l /var/lib/postgresql/10/beta/pg_wal/00000002.history

    -rw------- 1 postgres postgres 41 июн 13 19:59 /var/lib/postgresql/10/beta/pg_wal/00000002.history

В нем есть информация о "точках ветвления", через которые мы пришли в данную ветвь времени:

    postgres$ cat /var/lib/postgresql/10/beta/pg_wal/00000002.history

    1	0/6000000	no recovery target specified

Эти файлы PostgreSQL использует, когда мы указываем ветвь времени в recovery.conf. Поэтому файл истории подлежит архивации вместе с сегментами WAL, и удалять из архива их не надо:

    postgres$ ls -l /var/lib/postgresql/archive_beta

    total 4
    -rw------- 1 postgres postgres 41 июн 13 19:59 00000002.history

Конец демонстрации.