
МАСТЕР: НАСТРОЙКА ПОТОКОВОЙ РЕПЛИКАЦИИ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Настраиваем потоковую репликацию без использования архива сегментов WAL,
точно так же, как в предыдущей теме.
Внесем необходимые изменения в конфигурацию.

    psql -h localhost -p 5432 -U postgres

    => alter system set wal_level = hot_standby;
    ALTER SYSTEM

    => alter system set archive_mode = off;
    ALTER SYSTEM

    => alter system set max_standby_streaming_delay = '10s';
    ALTER SYSTEM

    => alter system set max_replication_slots = 5;
    ALTER SYSTEM

    => alter system set max_wal_senders = 5;
    ALTER SYSTEM

    => alter system set wal_log_hints = on;
    ALTER SYSTEM

Параметр wal_log_hints нужен для утилиты pg_rewind.

.......................................................................

Добавим еще несколько параметров, которые надо будет изменить
на реплике.

    => alter system set port = 5432;
    ALTER SYSTEM

    => alter system set hot_standby = off;
    ALTER SYSTEM

    => alter system set hot_standby_feedback = off;
    ALTER SYSTEM

    => alter system set wal_receiver_status_interval = 0;
    ALTER SYSTEM

.......................................................................

МАСТЕР: ПРОВЕРКА ПОДКЛЮЧЕНИЯ ДЛЯ РЕПЛИКАЦИИ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

В файле pg_hba.conf должна быть строка для базы данных replication:

postgres> sed -i s/#\(\w\+\s\+replication\)/\1/ /usr/local/pgsql/data/pg_hba.conf 



postgres> tail -n 5 /usr/local/pgsql/data/pg_hba.conf 
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     postgres                                trust
host    replication     postgres        127.0.0.1/32            trust
host    replication     postgres        ::1/128                 trust

Все в порядке.

.......................................................................

Перезапустим сервер, чтобы изменения вступили в силу.

    => \q

postgres> pg_ctl restart -w -l /home/postgres/logfile 
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started

.......................................................................

МАСТЕР: СЛОТ РЕПЛИКАЦИИ
~~~~~~~~~~~~~~~~~~~~~~~

Создадим слот репликации.

    psql -h localhost -p 5432 -U postgres

    => select pg_create_physical_replication_slot('slot_a');
     pg_create_physical_replication_slot 
    -------------------------------------
     (slot_a,)
    (1 row)
    

.......................................................................

СОЗДАНИЕ БАЗОВОЙ РЕЗЕРВНОЙ КОПИИ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Создадим базовую резервную копию.
Убедимся, что в кластере нет пользовательских табличных пространств, так как
их наличие усложнило бы процесс резервирования и восстановления.

    => \db
           List of tablespaces
        Name    |  Owner   | Location 
    ------------+----------+----------
     pg_default | postgres | 
     pg_global  | postgres | 
    (2 rows)
    

.......................................................................

Выполним команду pg_basebackup от имени postgres2, в качестве каталога
указываем PGDATA будущей реплики. Используем формат по умолчанию (plain),
Ключ -R создаст заготовку файла recovery.conf.

postgres2> bash -c "rm -rf /usr/local/pgsql2/data/*" 
postgres2> pg_basebackup -U postgres -p 5432 -D /usr/local/pgsql2/data -X stream -R 

.......................................................................

Проверим содержимое каталога:

postgres2> ls -l /usr/local/pgsql2/data 
total 132
-rw-------  1 postgres2 postgres2   208 дек.  14 19:00 backup_label
-rw-------  1 postgres2 postgres2   208 дек.  14 19:00 backup_label.old
drwx------ 29 postgres2 postgres2  4096 дек.  14 19:00 base
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 global
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_clog
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_commit_ts
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_dynshmem
-rw-------  1 postgres2 postgres2  4465 дек.  14 19:00 pg_hba.conf
-rw-------  1 postgres2 postgres2  1636 дек.  14 19:00 pg_ident.conf
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_log
drwx------  4 postgres2 postgres2  4096 дек.  14 19:00 pg_logical
drwx------  4 postgres2 postgres2  4096 дек.  14 19:00 pg_multixact
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_notify
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_replslot
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_serial
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_snapshots
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_stat
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_stat_tmp
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_subtrans
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_tblspc
drwx------  2 postgres2 postgres2  4096 дек.  14 19:00 pg_twophase
-rw-------  1 postgres2 postgres2     4 дек.  14 19:00 PG_VERSION
drwx------  3 postgres2 postgres2  4096 дек.  14 19:00 pg_xlog
-rw-------  1 postgres2 postgres2   336 дек.  14 19:00 postgresql.auto.conf
-rw-------  1 postgres2 postgres2 21672 дек.  14 19:00 postgresql.conf
-rw-r--r--  1 postgres2 postgres2    98 дек.  14 19:00 recovery.conf
-rw-r--r--  1 postgres2 postgres2   127 дек.  14 19:00 recovery.done

.......................................................................

РЕПЛИКА: НАСТРОЙКА
~~~~~~~~~~~~~~~~~~

Требуется немного изменить postgresql.auto.conf. Изменяем его вручную,
а не командой alter system, поскольку экземпляр еще не запущен.


postgres2> sed -i "s/port = .*/port = 5433/" /usr/local/pgsql2/data/postgresql.auto.conf 
postgres2> sed -i "s/hot_standby = .*/hot_standby = on/" /usr/local/pgsql2/data/postgresql.auto.conf 
postgres2> sed -i "s/hot_standby_feedback = .*/hot_standby_feedback = on/" /usr/local/pgsql2/data/postgresql.auto.conf 
postgres2> sed -i "s/wal_receiver_status_interval = .*/wal_receiver_status_interval = 1s/" /usr/local/pgsql2/data/postgresql.auto.conf 

.......................................................................

Проверим, что получилось:

postgres2> cat /usr/local/pgsql2/data/postgresql.auto.conf 
# Do not edit this file manually!
# It will be overwritten by ALTER SYSTEM command.
wal_level = 'hot_standby'
archive_mode = 'off'
max_standby_streaming_delay = '10s'
max_replication_slots = '5'
max_wal_senders = '5'
wal_log_hints = 'on'
port = 5433
hot_standby = on
hot_standby_feedback = on
wal_receiver_status_interval = 1s

Параметр wal_receiver_status_interval уменьшен для целей демонстрации.

.......................................................................

Заготовка для recovery.conf была подготовлена утилитой pg_basebackup:

postgres2> cat /usr/local/pgsql2/data/recovery.conf 
standby_mode = 'on'
primary_conninfo = 'user=postgres port=5432 sslmode=disable sslcompression=1'

.......................................................................

Остается добавить указание использовать слот репликации.


postgres2> bash -c "echo primary_slot_name = \\'\'slot_a\\'\' >> /usr/local/pgsql2/data/recovery.conf" 
postgres2> cat /usr/local/pgsql2/data/recovery.conf 
standby_mode = 'on'
primary_conninfo = 'user=postgres port=5432 sslmode=disable sslcompression=1'
primary_slot_name = 'slot_a'

.......................................................................

РЕПЛИКА: СТАРТ СЕРВЕРА
~~~~~~~~~~~~~~~~~~~~~~

Можно стартовать сервер.

postgres2> pg_ctl start -w -l /home/postgres2/logfile 
waiting for server to start..... done
server started

.......................................................................

Слот репликации инициализировался и используется (поле active
и не пустое поле restart_lsn):

    => \x
    Expanded display is on.

    => select * from pg_replication_slots;
    -[ RECORD 1 ]+-----------
    slot_name    | slot_a
    plugin       | 
    slot_type    | physical
    datoid       | 
    database     | 
    active       | t
    active_pid   | 14896
    xmin         | 557789
    catalog_xmin | 
    restart_lsn  | 3/BF000000
    

.......................................................................

ПРОВЕРКА РЕПЛИКАЦИИ
~~~~~~~~~~~~~~~~~~~

Создадим базу данных и таблицу на мастере:

    => create database db12;
    CREATE DATABASE

    => \c db12
    You are now connected to database "db12" as user "postgres".

    => create table replica_test(t text);
    CREATE TABLE

    => insert into replica_test values ('Я - мастер.');
    INSERT 0 1

.......................................................................

Вот они на реплике:

    |    psql -h localhost -p 5433 -U postgres

    |    => \c db12
    |    You are now connected to database "db12" as user "postgres".

    |    => select * from replica_test;
    |          t      
    |    -------------
    |     Я - мастер.
    |    (1 row)
    |    

.......................................................................

ПЕРЕХОД НА РЕПЛИКУ
~~~~~~~~~~~~~~~~~~

Сейчас второй сервер является репликой (находится в режиме восстановления):

    |    => select pg_is_in_recovery();
    |     pg_is_in_recovery 
    |    -------------------
    |     t
    |    (1 row)
    |    

.......................................................................

Повышаем реплику:

postgres2> pg_ctl -w promote 
server promoting

.......................................................................

Теперь бывшая реплика стала полноценным экземпляром.

    |    => select pg_is_in_recovery();
    |     pg_is_in_recovery 
    |    -------------------
    |     f
    |    (1 row)
    |    

Мы можем изменять данные:

    |    => insert into replica_test values ('Я - бывшая реплика (новый мастер).');
    |    INSERT 0 1

.......................................................................

Между тем старый мастер еще не выключен и в принципе тоже может изменять
данные:

    => insert into replica_test values ('Die hard');
    INSERT 0 1

Остановим его (аккуратно, с выполнением контрольной точки).

    => \q

postgres> pg_ctl stop 
waiting for server to shut down.... done
server stopped

.......................................................................

Создадим на новом мастере слот для будущей реплики.

    |    => select pg_create_physical_replication_slot('slot_a');
    |     pg_create_physical_replication_slot 
    |    -------------------------------------
    |     (slot_a,)
    |    (1 row)
    |    

.......................................................................

БЫВШИЙ МАСТЕР -> НОВАЯ РЕПЛИКА
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

В ключах утилиты надо указать каталог PGDATA нашего сервера и способ обращения
к серверу-источнику: либо подключение от имени суперпользователя (если сервер
работает), либо местоположение его каталога PGDATA (если он выключен).

postgres> pg_rewind -D /usr/local/pgsql/data "--source-server=user=postgres port=5433" -P 
connected to server
servers diverged at WAL position 3/BF021FE0 on timeline 17
rewinding from last common checkpoint at 3/BF002448 on timeline 17
reading source file list
reading target file list
reading WAL in target
need to copy 82 MB (total source directory size is 905 MB)
    0/84425 kB (0%) copied
78561/84425 kB (93%) copied
84425/84425 kB (100%) copied
creating backup label and updating control file
syncing target data directory
Done!

.......................................................................

В результате работы утилита pg_rewind "откатывает" файлы данных на ближайшую
контрольную точку до того момента, как пути серверов разошлись, а также создает
файл backup_label, который обеспечивает применение нужных журналов для завершения
восстановления.

Заглянем в backuplabel:

postgres> cat /usr/local/pgsql/data/backup_label 
START WAL LOCATION: 3/BF002410 (file 0000001100000003000000BF)
CHECKPOINT LOCATION: 3/BF002448
BACKUP METHOD: pg_rewind
BACKUP FROM: standby
START TIME: 2017-12-14 19:00:34 MSK

.......................................................................

Поскольку postgresql.auto.conf был скопирован с нового мастера, его нужно
немного поправить.

postgres> sed -i "s/port = .*/port = 5432/" /usr/local/pgsql/data/postgresql.auto.conf 
postgres> sed -i "s/hot_standby = .*/hot_standby = on/" /usr/local/pgsql/data/postgresql.auto.conf 
postgres> sed -i "s/hot_standby_feedback = .*/hot_standby_feedback = on/" /usr/local/pgsql/data/postgresql.auto.conf 

Проверим, что получилось:

postgres> cat /usr/local/pgsql/data/postgresql.auto.conf 
# Do not edit this file manually!
# It will be overwritten by ALTER SYSTEM command.
wal_level = 'hot_standby'
archive_mode = 'off'
max_standby_streaming_delay = '10s'
max_replication_slots = '5'
max_wal_senders = '5'
wal_log_hints = 'on'
port = 5432
hot_standby = on
hot_standby_feedback = on
wal_receiver_status_interval = 1s

.......................................................................

Теперь бывший мастер готов к восстановлению. Но мы хотим, чтобы он стал
новой репликой. Поэтому нам еще требуется файл recovery.conf.

postgres> bash -c "echo standby_mode = \\'\'on\\'\' >> /usr/local/pgsql/data/recovery.conf" 
postgres> bash -c "echo primary_conninfo = \\'\'user=postgres port=5433 sslmode=disable sslcompression=1\\'\' >> /usr/local/pgsql/data/recovery.conf" 
postgres> bash -c "echo primary_slot_name = \\'\'slot_a\\'\' >> /usr/local/pgsql/data/recovery.conf" 

.......................................................................

Проверим:

postgres> cat /usr/local/pgsql/data/recovery.conf 
standby_mode = 'on'
primary_conninfo = 'user=postgres port=5433 sslmode=disable sslcompression=1'
primary_slot_name = 'slot_a'

.......................................................................

НОВАЯ РЕПЛИКА: СТАРТ СЕРВЕРА
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Можно стартовать новую реплику.

postgres> pg_ctl start -w -l /home/postgres/logfile 
waiting for server to start..... done
server started

.......................................................................

Слот репликации инициализировался и используется:

    |    => \x
    |    Expanded display is on.

    |    => select * from pg_replication_slots;
    |    -[ RECORD 1 ]+-----------
    |    slot_name    | slot_a
    |    plugin       | 
    |    slot_type    | physical
    |    datoid       | 
    |    database     | 
    |    active       | t
    |    active_pid   | 15031
    |    xmin         | 557795
    |    catalog_xmin | 
    |    restart_lsn  | 3/BF046EF4
    |    

.......................................................................

Данные, измененные на новом мастере, получены:

    psql -h localhost -p 5432 -U postgres

    => \c db12
    You are now connected to database "db12" as user "postgres".

    => select * from replica_test;
                     t                  
    ------------------------------------
     Я - мастер.
     Я - бывшая реплика (новый мастер).
    (2 rows)
    

.......................................................................

Проверим еще:

    |    => insert into replica_test values ('Еще строка с нового мастера.');
    |    INSERT 0 1

    => select * from replica_test;
                     t                  
    ------------------------------------
     Я - мастер.
     Я - бывшая реплика (новый мастер).
     Еще строка с нового мастера.
    (3 rows)
    

.......................................................................

Таким образом, два сервера поменялись ролями.

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

.......................................................................

    |    => \q

postgres2> pg_ctl stop -D /usr/local/pgsql2/data 
waiting for server to shut down.... done
server stopped
postgres> pg_ctl -w promote 
server promoting

    => select pg_drop_replication_slot('slot_a');
     pg_drop_replication_slot 
    --------------------------
     
    (1 row)
    

    => alter system reset all;
    ALTER SYSTEM

    => \q

postgres> pg_ctl restart -w -l /home/postgres/logfile 
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started
