α=> CREATE DATABASE replica_physical;
CREATE DATABASE
Создаем и запускаем реплику точно так же, как в демонстрации.
Слот репликации:
α=> SELECT pg_create_physical_replication_slot('replica');
pg_create_physical_replication_slot ------------------------------------- (replica,) (1 row)
Резервная копия:
postgres$ rm -rf /var/lib/postgresql/10/beta/*
postgres$ pg_basebackup -U student --pgdata=/var/lib/postgresql/10/beta -R --slot=replica
Меняем порт и устанавливаем режим горячего резерва:
postgres$ echo 'port = 5433' >> /var/lib/postgresql/10/beta/postgresql.auto.conf
postgres$ echo 'hot_standby = on' >> /var/lib/postgresql/10/beta/postgresql.auto.conf
В файл recovery.conf добавляем указание application_name:
postgres$ sed -i 's/^\(primary_conninfo = '\''\)/\1application_name=replica /' /var/lib/postgresql/10/beta/recovery.conf
postgres$ cat /var/lib/postgresql/10/beta/recovery.conf
standby_mode = 'on' primary_conninfo = 'application_name=replica 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'
Запускаем:
student$ sudo pg_ctlcluster 10 beta start
Теперь включаем на мастере режим синхронной репликации. Сразу переключимся на суперпользовательскую роль.
student$ psql -d replica_physical -U postgres
α=> ALTER SYSTEM SET synchronous_commit = on;
ALTER SYSTEM
α=> ALTER SYSTEM SET synchronous_standby_names = 'replica';
ALTER SYSTEM
student$ sudo pg_ctlcluster 10 alpha reload
α=> SELECT * FROM pg_stat_replication \gx
-[ RECORD 1 ]----+------------------------------ pid | 7519 usesysid | 16384 usename | student application_name | replica client_addr | client_hostname | client_port | -1 backend_start | 2018-06-13 20:00:49.943947+03 backend_xmin | state | streaming sent_lsn | 0/1A00100C write_lsn | 0/1A00100C flush_lsn | 0/1A00100C replay_lsn | 0/1A00100C write_lag | 00:00:00.077777 flush_lag | 00:00:00.078191 replay_lag | 00:00:00.078232 sync_priority | 1 sync_state | sync
sync_state: sync говорит о том, что репликация работает в синхронном режиме.
Остановим реплику.
student$ sudo pg_ctlcluster 10 beta stop
α=> \c - student
You are now connected to database "replica_physical" as user "student".
α=> BEGIN;
BEGIN
α=> CREATE TABLE test(id integer);
CREATE TABLE
α=> COMMIT;
Фиксация ждет появления синхронной реплики...
student$ sudo pg_ctlcluster 10 beta start
После старта реплики фиксация завершается.
COMMIT
α=> INSERT INTO test VALUES (1);
INSERT 0 1
student$ psql -p 5433 -d replica_physical
β=> SHOW max_standby_streaming_delay;
max_standby_streaming_delay ----------------------------- 30s (1 row)
Начинаем транзакцию с уровнем изоляции repeatable read: первый оператор создаст снимок данных, который будет использоваться всеми последующими операторами этой транзакции.
β=> BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
β=> SELECT * FROM test;
id ---- 1 (1 row)
На мастере изменяем единственную строку и выполняем очистку. При этом первая версия строки будет удалена.
α=> UPDATE test SET id = 2;
UPDATE 1
α=> VACUUM test;
VACUUM
Через 20 секунд запрос на реплике еще сработает - реплика задерживает применение конфликтующей журнальной записи:
β=> SELECT * FROM test;
id ---- 1 (1 row)
Через 30 секунд такой же запрос уже будет аварийно прерван, поскольку версия строки, вхоящая в снимок, больше не существует.
β=> SELECT * FROM test;
FATAL: terminating connection due to conflict with recovery DETAIL: User query might have needed to see row versions that must be removed. HINT: In a moment you should be able to reconnect to the database and repeat your command. server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. connection to server was lost
Выставим задержку применения конфликтующих изменений в ноль.
student$ psql -p 5433 -d replica_physical -U postgres
β=> ALTER SYSTEM SET max_standby_streaming_delay = 0;
ALTER SYSTEM
β=> \c - student
You are now connected to database "replica_physical" as user "student".
student$ sudo pg_ctlcluster 10 beta reload
Снова начинаем транзакцию...
β=> BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
β=> SELECT * FROM test;
id ---- 2 (1 row)
На мастере изменяем строку и выполняем очистку...
α=> UPDATE test SET id = 3;
UPDATE 1
α=> VACUUM test;
VACUUM
Запрос на реплике прерывается тут же:
β=> SELECT * FROM test;
FATAL: terminating connection due to conflict with recovery DETAIL: User query might have needed to see row versions that must be removed. HINT: In a moment you should be able to reconnect to the database and repeat your command. server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. connection to server was lost
Установим обратную связь и, чтобы не ждать долго, небольшой интервал оповещений.
student$ psql -p 5433 -d replica_physical -U postgres
β=> ALTER SYSTEM SET hot_standby_feedback = on;
ALTER SYSTEM
β=> ALTER SYSTEM SET wal_receiver_status_interval = '1s';
ALTER SYSTEM
β=> \c - student
You are now connected to database "replica_physical" as user "student".
student$ sudo pg_ctlcluster 10 beta reload
Снова начинаем транзакцию...
β=> BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
β=> SELECT * FROM test;
id ---- 3 (1 row)
На мастере изменяем строку и выполняем очистку...
α=> UPDATE test SET id = 4;
UPDATE 1
α=> VACUUM VERBOSE test;
INFO: vacuuming "public.test" INFO: "test": found 0 removable, 2 nonremovable row versions in 1 out of 1 pages DETAIL: 1 dead row versions cannot be removed yet, oldest xmin: 630 There were 0 unused item pointers. Skipped 0 pages due to buffer pins, 0 frozen pages. 0 pages are entirely empty. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM
Благодаря обратной связи, очистка не может удалить старую версию строки (found 0 removable, 2 nonremovable row versions).
β=> SELECT * FROM test;
id ---- 3 (1 row)
β=> COMMIT;
COMMIT
α=> VACUUM VERBOSE test;
INFO: vacuuming "public.test" INFO: "test": found 1 removable, 1 nonremovable row versions in 1 out of 1 pages DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 631 There were 1 unused item pointers. Skipped 0 pages due to buffer pins, 0 frozen pages. 0 pages are entirely empty. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM
После завершения транзакции на реплике, мастер может выполнить очистку (found 1 removable...). Журнальные записи реплицируются, но ничего не поломают.