Настройка репликации без архива

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

Создаем слот:

α=> SELECT pg_create_physical_replication_slot('replica');
 pg_create_physical_replication_slot 
-------------------------------------
 (replica,)
(1 row)


Вначале слот не инициализирован (restart_lsn пустой):

α=> SELECT * FROM pg_replication_slots \gx
-[ RECORD 1 ]-------+---------
slot_name           | replica
plugin              | 
slot_type           | physical
datoid              | 
database            | 
temporary           | f
active              | f
active_pid          | 
xmin                | 
catalog_xmin        | 
restart_lsn         | 
confirmed_flush_lsn | 


Как мы помним из модуля "Резервное копирование", в версии 10+ все необходимые настройки есть по умолчанию:

Создадим автономную резервную копию, используя созданный слот. Копию расположим в каталоге PGDATA будущей реплики.

postgres$ rm -rf /var/lib/postgresql/10/beta/*
postgres$ pg_basebackup -U student --pgdata=/var/lib/postgresql/10/beta -R --slot=replica

Возможность использовать слот появилась у pg_basebackup, начиная с версии 9.6. В более ранних версиях следовало бы заранее инициализировать слот, подключившись к нему утилитой pg_receive_xlog.


После выполнения резервной копии слот инициализировался, и мастер хранит все файлы журнала с начала копирования (restart_lsn).

α=> SELECT * FROM pg_replication_slots \gx
-[ RECORD 1 ]-------+----------
slot_name           | replica
plugin              | 
slot_type           | physical
datoid              | 
database            | 
temporary           | f
active              | f
active_pid          | 
xmin                | 
catalog_xmin        | 
restart_lsn         | 0/8000000
confirmed_flush_lsn | 


Мы собираемся запустить реплику одновременно с основным сервером на одной машине, поэтому в настройках необходимо поменять порт:

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

Чтобы реплика выполняла запросы (была "горячей"), необходимо выставить параметр hot_standby. Если этого не сделать, реплика будет "теплой" и не будет принимать подключения.

postgres$ echo 'hot_standby = on' >> /var/lib/postgresql/10/beta/postgresql.auto.conf

Файл recovery.conf был подготовлен утилитой pg_basebackup, и уже включает указание режима непрерывного восстановления (standby_mode) и слота репликации (primary_slot_name):

postgres$ cat /var/lib/postgresql/10/beta/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=student passfile=''/var/lib/postgresql/.pgpass'' host=''/var/run/postgresql'' port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any'
primary_slot_name = 'replica'

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

Журнальные записи, необходимые для восстановления согласованности, реплика получит от мастера по протоколу репликации. Далее она войдет в режим непрерывного восстановления, как указано в recovery.conf, и будет продолжать получать и проигрывать поток записей.

student$ sudo pg_ctlcluster 10 beta start

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

postgres$ ps -o pid,command --ppid `head -n 1 /var/lib/postgresql/10/beta/postmaster.pid`
  PID COMMAND
 1427 postgres: 10/beta: startup process   waiting for 000000010000000000000009
 1428 postgres: 10/beta: checkpointer process   
 1429 postgres: 10/beta: writer process   
 1430 postgres: 10/beta: stats collector process   
 1431 postgres: 10/beta: wal receiver process   

Процесс wal receiver принимает поток журнальных записей, процесс startup применяет изменения.

Процессы wal writer и autovacuum launcher отсутствуют.


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

postgres$ ps -o pid,command --ppid `head -n 1 /var/lib/postgresql/10/alpha/postmaster.pid`
  PID COMMAND
 1035 postgres: 10/alpha: checkpointer process   
 1036 postgres: 10/alpha: writer process   
 1037 postgres: 10/alpha: wal writer process   
 1038 postgres: 10/alpha: autovacuum launcher process   
 1039 postgres: 10/alpha: stats collector process   
 1040 postgres: 10/alpha: bgworker: logical replication launcher   
 1090 postgres: 10/alpha: student student [local] idle
 1432 postgres: 10/alpha: wal sender process student [local] idle

Здесь добавился процесс wal sender, обслуживающий подключение по протоколу репликации.


Проверка репликации

Выполним несколько команд на мастере:

α=> CREATE DATABASE replica_physical;
CREATE DATABASE
α=> \c replica_physical;
You are now connected to database "replica_physical" as user "student".
α=> CREATE TABLE test(s text);
CREATE TABLE
α=> INSERT INTO test VALUES ('Привет, мир!');
INSERT 0 1

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

student$ psql -p 5433 -d replica_physical
β=> SELECT * FROM test;
      s       
--------------
 Привет, мир!
(1 row)


При этом изменения на реплике не допускаются:

β=> INSERT INTO test VALUES ('Replica');
ERROR:  cannot execute INSERT in a read-only transaction

Вообще реплику от мастера можно отличить с помощью функции:

β=> SELECT pg_is_in_recovery();
 pg_is_in_recovery 
-------------------
 t
(1 row)


Мониторинг репликации

Состояние репликации можно смотреть в специальном представлении на мастере. Чтобы пользователь получил доступ к этой информации, ему должна быть выдана роль pg_read_all_stats (или он должен быть суперпользователем).

α=> \c - postgres
You are now connected to database "replica_physical" as user "postgres".
α=> GRANT pg_read_all_stats TO student;
GRANT ROLE
α=> \c - student
You are now connected to database "replica_physical" as user "student".

α=> SELECT * FROM pg_stat_replication \gx
-[ RECORD 1 ]----+------------------------------
pid              | 1432
usesysid         | 16384
usename          | student
application_name | walreceiver
client_addr      | 
client_hostname  | 
client_port      | -1
backend_start    | 2018-06-13 19:59:13.044052+03
backend_xmin     | 
state            | streaming
sent_lsn         | 0/9025620
write_lsn        | 0/9025620
flush_lsn        | 0/9025620
replay_lsn       | 0/9025620
write_lag        | 00:00:00.000199
flush_lag        | 00:00:00.001131
replay_lag       | 00:00:00.001184
sync_priority    | 0
sync_state       | async

Обратите внимание на поля *_lsn (и *_lag) - они показывают отставание реплики на разных этапах. Сейчас все позиции совпадают, отставание нулевое.


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

α=> INSERT INTO test SELECT 'Just a line' FROM generate_series(1,1000000);
INSERT 0 1000000
α=> SELECT *, pg_current_wal_lsn() from pg_stat_replication \gx
-[ RECORD 1 ]------+------------------------------
pid                | 1432
usesysid           | 16384
usename            | student
application_name   | walreceiver
client_addr        | 
client_hostname    | 
client_port        | -1
backend_start      | 2018-06-13 19:59:13.044052+03
backend_xmin       | 
state              | streaming
sent_lsn           | 0/D1299E0
write_lsn          | 0/D1299E0
flush_lsn          | 0/D1299E0
replay_lsn         | 0/C47A7FC
write_lag          | 00:00:00.135193
flush_lag          | 00:00:00.143305
replay_lag         | 00:00:00.743007
sync_priority      | 0
sync_state         | async
pg_current_wal_lsn | 0/D1299E0

Видно, что возникла небольшая задержка. Однако не следует рассчитывать на точность выше чем полсекунды, так как статистика обновляется примерно с такой частотой.


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

β=> SELECT count(*) FROM test;
  count  
---------
 1000001
(1 row)

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


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

α=> SELECT *, pg_current_wal_lsn() from pg_stat_replication \gx
-[ RECORD 1 ]------+------------------------------
pid                | 1432
usesysid           | 16384
usename            | student
application_name   | walreceiver
client_addr        | 
client_hostname    | 
client_port        | -1
backend_start      | 2018-06-13 19:59:13.044052+03
backend_xmin       | 
state              | streaming
sent_lsn           | 0/D1299E0
write_lsn          | 0/D1299E0
flush_lsn          | 0/D1299E0
replay_lsn         | 0/D1299E0
write_lag          | 00:00:00.135193
flush_lag          | 00:00:00.143305
replay_lag         | 00:00:00.54367
sync_priority      | 0
sync_state         | async
pg_current_wal_lsn | 0/D1299E0

Все позиции выровнялись.


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