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

Настраиваем потоковую репликацию без использования архива сегментов 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 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)
    

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

    => \x
    Expanded display is on.

    => select * from pg_replication_slots;
    -[ RECORD 1 ]+---------
    slot_name    | slot_a
    plugin       | 
    slot_type    | physical
    datoid       | 
    database     | 
    active       | f
    active_pid   | 
    xmin         | 
    catalog_xmin | 
    restart_lsn  | 
    

Обратите внимание на столбец restart_lsn: он пустой, так как слот создается
неинициализированным. Для абсолютной надежности слот надо инициализировать
и использовать его не только для подключения реплики, но и в утилите
pg_basebackup.
Но полноценные средства для этого будут доступны только в версии 9.6:
- второй параметр у функции pg_create_physical_replication_slot,
- ключ у pg_basebackup.

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

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

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

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

    => \x
    Expanded display is off.

    => \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 18:59 backup_label
-rw-------  1 postgres2 postgres2   208 дек.  14 18:59 backup_label.old
drwx------ 29 postgres2 postgres2  4096 дек.  14 18:59 base
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 global
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_clog
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_commit_ts
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_dynshmem
-rw-------  1 postgres2 postgres2  4465 дек.  14 18:59 pg_hba.conf
-rw-------  1 postgres2 postgres2  1636 дек.  14 18:59 pg_ident.conf
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_log
drwx------  4 postgres2 postgres2  4096 дек.  14 18:59 pg_logical
drwx------  4 postgres2 postgres2  4096 дек.  14 18:59 pg_multixact
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_notify
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_replslot
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_serial
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_snapshots
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_stat
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_stat_tmp
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_subtrans
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_tblspc
drwx------  2 postgres2 postgres2  4096 дек.  14 18:59 pg_twophase
-rw-------  1 postgres2 postgres2     4 дек.  14 18:59 PG_VERSION
drwx------  3 postgres2 postgres2  4096 дек.  14 18:59 pg_xlog
-rw-------  1 postgres2 postgres2   315 дек.  14 18:59 postgresql.auto.conf
-rw-------  1 postgres2 postgres2 21672 дек.  14 18:59 postgresql.conf
-rw-r--r--  1 postgres2 postgres2    98 дек.  14 18:59 recovery.conf
-rw-r--r--  1 postgres2 postgres2   127 дек.  14 18:59 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'
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

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

Посмотрим на процессы реплики.

postgres2> ps -o pid,command --ppid `sudo head -n 1 /usr/local/pgsql2/data/postmaster.pid`
  PID COMMAND
14460 postgres: startup process   waiting for 0000001100000003000000BA
14462 postgres: checkpointer process  
14463 postgres: writer process      
14464 postgres: stats collector process  
14465 postgres: wal receiver process  

Добавился процесс wal receiver.

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

И сравним с процессами мастера.

postgres> ps -o pid,command --ppid `sudo head -n 1 /usr/local/pgsql/data/postmaster.pid`
  PID COMMAND
14388 postgres: checkpointer process  
14389 postgres: writer process     
14390 postgres: wal writer process  
14391 postgres: autovacuum launcher process  
14392 postgres: stats collector process  
14401 postgres: postgres postgres 127.0.0.1(50282) idle
14466 postgres: wal sender process postgres [local] idle

Здесь добавился процесс wal sender.

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

Слот репликации инициализировался и используется (поле 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   | 14466
    xmin         | 557781
    catalog_xmin | 
    restart_lsn  | 3/BA000000
    

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

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

На реплике нет базы данных db11:

    |    psql -h localhost -p 5433 -U postgres

    |    => select count(*) from pg_database where datname = 'db11';
    |     count 
    |    -------
    |         0
    |    (1 row)
    |    

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

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

    => create database db11;
    CREATE DATABASE

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

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

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

И вот они на реплике:

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

    |    => select * from replica_test;
    |     t 
    |    ---
    |    (0 rows)
    |    

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

Теперь вставим в таблицу большое количество строк, чтобы увидеть репликацию
в процессе работы.

    => insert into replica_test select 'a line' from generate_series(1,1000000);
    INSERT 0 1000000

    => select *, pg_current_xlog_location() from pg_stat_replication;
    -[ RECORD 1 ]------------+------------------------------
    pid                      | 14466
    usesysid                 | 10
    usename                  | postgres
    application_name         | walreceiver
    client_addr              | 
    client_hostname          | 
    client_port              | -1
    backend_start            | 2017-12-14 19:00:01.478036+03
    backend_xmin             | 
    state                    | streaming
    sent_location            | 3/BD040000
    write_location           | 3/BD000000
    flush_location           | 3/BD000000
    replay_location          | 3/BA69D8BC
    sync_priority            | 0
    sync_state               | async
    pg_current_xlog_location | 3/BDD4D5B0
    

Обратите внимание на поля *location - они показывают запаздывание
на разных этапах.

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

Смотрим на реплику:

    |    => select count(*) from replica_test;
    |     count 
    |    -------
    |         0
    |    (1 row)
    |    

Все строки успешно доехали.

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

И еще раз проверим состояние репликации:

    => select *, pg_current_xlog_location() from pg_stat_replication;
    -[ RECORD 1 ]------------+------------------------------
    pid                      | 14466
    usesysid                 | 10
    usename                  | postgres
    application_name         | walreceiver
    client_addr              | 
    client_hostname          | 
    client_port              | -1
    backend_start            | 2017-12-14 19:00:01.478036+03
    backend_xmin             | 
    state                    | streaming
    sent_location            | 3/BDD4D5B0
    write_location           | 3/BDD4D5B0
    flush_location           | 3/BDD4D5B0
    replay_location          | 3/BAF2D89C
    sync_priority            | 0
    sync_state               | async
    pg_current_xlog_location | 3/BDD4D5B0
    

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

КОНФЛИКТ С ОЧИСТКОЙ СТРОК
~~~~~~~~~~~~~~~~~~~~~~~~~

Теперь смоделируем конфликт с очисткой строк.
На мастере создадим таблицу с одной строкой...

    => create table replica_test2(t varchar(10));
    CREATE TABLE

    => insert into replica_test2 values ('one');
    INSERT 0 1

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

На реплике начинаем транзакцию с уровнем repeatable read и читаем строку...

    |    => begin;
    |    BEGIN

    |    => set transaction isolation level repeatable read;
    |    SET

    |    => select * from replica_test2;
    |      t  
    |    -----
    |     one
    |    (1 row)
    |    

...после чего изменяем строку на мастере.

    => update replica_test2 set t = 'two';
    UPDATE 1

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

Снимок на реплике "видит" первую версию строки, а на мастере видна вторая.

    |    => select *, xmin, xmax from replica_test2;
    |      t  |  xmin  |  xmax  
    |    -----+--------+--------
    |     one | 557785 | 557786
    |    (1 row)
    |    

    => \x
    Expanded display is off.

    => select *, xmin, xmax from replica_test2;
      t  |  xmin  | xmax 
    -----+--------+------
     two | 557786 |    0
    (1 row)
    

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

Причем мастер знает, какая версия нужна реплике (поле xmin):

    => \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   | 14466
    xmin         | 557786
    catalog_xmin | 
    restart_lsn  | 3/BDD50370
    

Если бы слот не использовался, то информация обратной связи отражалась бы
в представлении pg_stat_replication (поле backend_xmin).

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

Теперь мастер может "уйти вперед", но будет помнить, какая транзакция
нужна реплике:

    => select txid_current();
    -[ RECORD 1 ]+-------
    txid_current | 557787
    

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

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

Если теперь выполнить очистку строк, она просто ничего не сделает
(1 dead row versions cannot be removed yet):

    => vacuum verbose replica_test2;
    INFO:  vacuuming "public.replica_test2"
    INFO:  "replica_test2": found 0 removable, 2 nonremovable row versions in 1 out of 1 pages
    DETAIL:  1 dead row versions cannot be removed yet.
    There were 0 unused item pointers.
    Skipped 0 pages due to buffer pins.
    0 pages are entirely empty.
    CPU 0.00s/0.00u sec elapsed 0.00 sec.
    VACUUM

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

И только после того, как транзакция на реплике завершится, очистка
сможет отработать.

    |    => commit;
    |    COMMIT

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

    => vacuum verbose replica_test2;
    INFO:  vacuuming "public.replica_test2"
    INFO:  "replica_test2": found 1 removable, 1 nonremovable row versions in 1 out of 1 pages
    DETAIL:  0 dead row versions cannot be removed yet.
    There were 0 unused item pointers.
    Skipped 0 pages due to buffer pins.
    0 pages are entirely empty.
    CPU 0.00s/0.00u sec elapsed 0.00 sec.
    VACUUM

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

Теперь оба сервера снова видят одни и те же данные.

    |    => select *, xmin, xmax from replica_test2;
    |      t  |  xmin  | xmax 
    |    -----+--------+------
    |     two | 557786 |    0
    |    (1 row)
    |    

    => \x
    Expanded display is off.

    => select *, xmin, xmax from replica_test2;
      t  |  xmin  | xmax 
    -----+--------+------
     two | 557786 |    0
    (1 row)
    

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

А номер транзакции xmin увеличился до текущего:

    => \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   | 14466
    xmin         | 557788
    catalog_xmin | 
    restart_lsn  | 3/BDD52594
    

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


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

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

    |    => \q

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

    => select pg_drop_replication_slot('slot_a');
    -[ RECORD 1 ]------------+-
    pg_drop_replication_slot | 
    

    => 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
