Перейти к содержимому

21 Занятие 21: Отказоустойчивые решения / Кластер multimaster

Исходная страница в нативном Markdown: Отказоустойчивые решения / Кластер multimaster
Примечание

Эта страница извлечена из официального PDF в нативный Markdown. Текст слайдов, код и комментарии сохранены в порядке чтения; исходные PDF и HTML остаются эталоном макета. HTML source: pgpro_21_multimaster.html Archive: PGPRO-handouts-16.zip Страниц PDF: 27

Page 1: Отказоустойчивые решения / Кластер multimaster

Авторские права

© Postgres Professional, 2023–2026 Авторы: Алексей Береснев, Илья Баштанов, Павел Толмачев, Игорь Гнатюк Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)

Использование материалов курса

Некоммерческое использование материалов курса (презентации, демонстрации) разрешается без ограничений. Коммерческое использование возможно только с письменного разрешения компании Postgres Professional. Запрещается внесение изменений в материалы курса.

Обратная связь

Отзывы, замечания и предложения направляйте по адресу: edu@postgrespro.ru

Отказ от ответственности

Компания Postgres Professional не несет никакой ответственности за любые повреждения и убытки, включая потерю дохода, нанесенные прямым или непрямым, специальным или случайным использованием материалов курса. Компания Postgres Professional не предоставляет каких-либо гарантий на материалы курса. Материалы курса предоставляются на основе принципа «как есть» и компания Postgres Professional не обязана предоставлять сопровождение, поддержку, обновления, расширения и изменения.

Page 2: Темы

  • Возможности и ограничения
  • Архитектура и принцип работы
  • Распределенные транзакции
  • Поколения узлов
  • Проверка состояния узлов
  • Отказ узла
  • Режим 2+1

Page 3: Возможности

Синхронный симметричный кластер

  • на всех узлах одинаковые данные, все узлы ведущие
  • Распределенные транзакции
    • все узлы работают как единая СУБД с ACID-гарантиями
  • Отказоустойчивость и высокая доступность
  • Масштабируемость чтения

Комментарии преподавателя

Расширение multimaster позволяет преобразовать сервер в синхронный симметричный кластер без разделения ресурсов — данные будут одинаковы на всех узлах кластера, клиенты могут читать и писать данные на любой из узлов. В кластере поддерживаются распределенные транзакции, дающие обычные гарантии ACID, поэтому приложения могут работать с кластером как с единой СУБД.

Multimaster обеспечивает отказоустойчивость за счет автоматического восстановления данных на вернувшихся в строй узлах; кластер имеет высокую доступность — у него нулевое время простоя. Менять конфигурацию (в том числе и обновлять в рамках одной и той же основной версии) узлов можно без остановки обслуживания. Перенаправляя на разные узлы читающие транзакции, можно повысить пропускную способность таких операций.

https://postgrespro.ru/docs/enterprise/16/multimaster

Page 4: Ограничения

Не поддерживаются

  • ОС Windows и решения 1С
  • репликация нескольких баз данных
  • большие объекты (Large Objects)
  • операции неблокирующего построения и перестроения индексов
  • и работы с табличными пространствами
  • Особенности работы с данными
    • транзакции могут занимать длительное время
    • на уровне Read Committed могут происходить сбои сериализации
    • меняется поведение последовательностей
    • OID могут отличаться на разных узлах
  • Некоторые другие особенности и ограничения

Комментарии преподавателя

У расширения есть ряд ограничений.

Не поддерживается ОС Windows и продукты 1С.

Можно реплицировать только одну базу данных (но можно сделать несколько кластеров для разных баз данных).

Не поддерживаются большие объекты (Large Objects).

Не поддерживаются операции CREATE INDEX и REINDEX с предложением CONCURRENTLY, CREATE и DROP TABLESPACE.

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

На уровне изоляции Read Committed транзакции могут завершаться ошибкой сериализации.

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

Идентификаторы OID могут быть различными на разных узлах.

Существуют и некоторые другие ограничения использования мультимастера.

https://postgrespro.ru/docs/enterprise/16/multimaster#MULTIMASTER-LIMIT ATIONS

Page 5: Подготовка сервера

Остановим основной сервер.

student$ sudo systemctl stop postgrespro-ent-16.service

В этой демонстрации часть команд выполняется от имени пользователя ОС postgres. Обращайте внимание на приглашения перед командами. Для текущей демонстрации инициализированы и настроены три экземпляра Postgres Pro Enterprise. Каталоги данных и порты: /var/lib/pgpro/ent-16-MM-1, порт 5001 /var/lib/pgpro/ent-16-MM-2, порт 5002 /var/lib/pgpro/ent-16-MM-3, порт 5003 Для самостоятельной настройки кластеров обратитесь к решению практики. Дополнительные параметры первого экземпляра:

/var/lib/pgpro/ent-16-MM-1/conf.d/multimaster.conf
cluster_name = 'node1'
port = 5001
shared_preload_libraries = 'multimaster'
wal_level = logical
max_connections = 100           # значение по умолчанию
max_prepared_transactions = 300
max_wal_senders = 10            # значение по умолчанию
max_replication_slots = 10      # значение по умолчанию
wal_sender_timeout = 0
max_worker_processes = 320

Остальные экземпляры настроены аналогично.

Инициализация кластера multimaster

Узлы кластера уже подготовлены, теперь необходимо инициализировать весь кластер multimaster из трех узлов:

student$ psql -p 5001 -U postgres
student$ psql -p 5002 -U postgres
student$ psql -p 5003 -U postgres

Подготовим роль и базу данных для работы мультимастера на каждом из узлов. Для первого узла:

=> CREATE USER multimaster_user WITH SUPERUSER;
CREATE ROLE
=> CREATE DATABASE pgpro_multimaster OWNER multimaster_user;
CREATE DATABASE
=> \c pgpro_multimaster multimaster_user
You are now connected to database "pgpro_multimaster" as user "multimaster_user".

Для второго узла:

=> CREATE USER multimaster_user WITH SUPERUSER;
CREATE ROLE
=> CREATE DATABASE pgpro_multimaster OWNER multimaster_user;
CREATE DATABASE
=> \c pgpro_multimaster multimaster_user
You are now connected to database "pgpro_multimaster" as user "multimaster_user".

И для третьего узла:

=> CREATE USER multimaster_user WITH SUPERUSER;

Page 6: Page 6

CREATE ROLE
=> CREATE DATABASE pgpro_multimaster OWNER multimaster_user;
CREATE DATABASE
=> \c pgpro_multimaster multimaster_user
You are now connected to database "pgpro_multimaster" as user "multimaster_user".

Теперь объединим узлы в кластер. Для этого на любом из узлов добавим расширение multimaster в БД pgpro_multimaster…

=> CREATE EXTENSION multimaster;
CREATE EXTENSION
  • … и запустим функцию инициализации кластера. Первый параметр — это строка подключения к текущему узлу, второй — массив строк подключения к остальным узлам:
=> SELECT mtm.init_cluster(
     'dbname=pgpro_multimaster user=multimaster_user port=5001',
   '{"dbname=pgpro_multimaster user=multimaster_user port=5002",
     "dbname=pgpro_multimaster user=multimaster_user port=5003"}');
 init_cluster
--------------
(1 row)

Функция возвращает пустой ответ в случае успеха, либо ошибку инициализации.

Page 7: Архитектура

Source slide page 7: Архитектура{#source-figure-007 caption=“Source slide page 7: Архитектура”}

Комментарии преподавателя

Расширение multimaster состоит из патчей ядра СУБД (входят в Postgres Pro) и непосредственно кода расширения.

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

Для обеспечения согласованности данных подтверждение распределенных транзакций выполняется с помощью улучшенного трехфазного протокола фиксации (E3PC). Для этого в кластере изначально должно быть не менее трех узлов.

Высокая степень доступности достигается тем, что узлы можно выводить из кластера и добавлять к кластеру «на лету». При этом клиенты могут быть подключены к любому узлу — все узлы принимают и читающую, и пишущую нагрузку.

https://postgrespro.ru/docs/enterprise/16/multimaster#MULTIMASTER-ARC HITECTURE

Page 8: Процессы

Source slide page 8: Процессы{#source-figure-008 caption=“Source slide page 8: Процессы”}

Комментарии преподавателя

За разные задачи в мультимастере отвечают разные процессы:

● Процесс mtm-monitor запускается на каждом узле и управляет остальными процессами кластера.

● Процесс mtm-logrep-receiver получает поток логической репликации от walsender. Изменения применяются пулом процессов mtm-logrep-receiver-dynworker.

● Процесс mtm-dmq-sender посылает mtm-dmq-receiver служебную информацию, необходимую для реализации распределенных транзакций, а также выполняет периодический опрос доступности узлов (DMQ — distributed message queue, распределенная очередь сообщений).

● Процессы mtm-resolver и mtm-campaigner служат для согласованного исключения узлов из кластера и возвращения узлов в кластер; им отвечает процесс mtm-replier.

На рисунке показаны только процессы-источники одного узла и соответствующие им процессы-приемники другого узла. На самом деле и те процессы, и другие работают на каждом узле. Более того, их количество зависит от числа узлов в кластере: например, на каждом узле будет запущен процесс walsender для каждого из остальных узлов кластера.

https://postgrespro.ru/docs/enterprise/16/multimaster#MULTIMASTER-BGW ORKERS

Page 9: Процессы

Посмотрим на процессы, которые запускаются на каждом узле кластера (postmaster.pid находится в первой строке файла postmaster.pid, расположенного в каталоге PGDATA):

student$ ps -o pid,command --ppid 852417
    PID COMMAND
 852421 postgres: node1: checkpointer
 852422 postgres: node1: background writer
 852424 postgres: node1: walwriter
 852425 postgres: node1: autovacuum launcher
 852426 postgres: node1: cfs-worker-0
 852428 postgres: node1: mtm-dmq-sender
 852429 postgres: node1: logical replication launcher
 853698 postgres: node1: multimaster_user pgpro_multimaster [local] idle
 853972 postgres: node1: mtm-monitor
 854152 postgres: node1: mtm-replier
 854153 postgres: node1: mtm-campaigner
 854154 postgres: node1: mtm-resolver
 854155 postgres: node1: mtm-dmq-receiver-1-3
 854157 postgres: node1: mtm-dmq-receiver-1-2
 854377 postgres: node1: walsender multimaster_user pgpro_multimaster [local]
START_REPLICATION
 854462 postgres: node1: mtm-logrep-receiver-1-2
 854463 postgres: node1: walsender multimaster_user pgpro_multimaster [local]
START_REPLICATION
 854493 postgres: node1: mtm-logrep-receiver-1-3

Количество процессов довольно велико, следует обеспечить их достаточными ресурсами.

Page 10: Фиксация изменений

Source slide page 10: Фиксация изменений{#source-figure-010 caption=“Source slide page 10: Фиксация изменений”}

Комментарии преподавателя

Протокол трехфазной фиксации расширяет стандартный протокол двухфазной фиксации, реализованный в PostgreSQL, и работает следующим образом.

  1. Prepare. Когда на узле (который считается координатором транзакции) выполняется оператор COMMIT, транзакция подготавливается (PREPARE TRANSACTION) и передается на остальные доступные узлы. Эти узлы отвечают подтверждением или отказом.

Если какой-либо узел ответил отказом, транзакция обрывается.

  1. Precommit. Узел-координатор рассылает остальным узлам команду Precommit, которая выражает намерение зафиксировать транзакцию.

Для продолжения достаточно, чтобы подтверждением ответило большинство узлов (кворум). Если достаточного количества подтверждений не набирается, транзакция обрывается (ROLLBACK PREPARED).

  1. Commit. Транзакция фиксируется на всех узлах (COMMIT PREPARED).

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

https://www.sciencedirect.com/science/article/pii/S0022000098915665

Page 11: Поколения узлов

Source slide page 11: Поколения узлов{#source-figure-011 caption=“Source slide page 11: Поколения узлов”}

Комментарии преподавателя

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

Узел всегда входит в какое-либо поколение. Если узел узнает, что существует поколение с бо́льшим номером, он старается перейти в него.

Новое поколение формируется большинством узлов при любом изменении списка доступных узлов кластера: в момент его запуска, при сбое узла и при добавлении в кластер нового узла.

Транзакцию можно зафиксировать только после того, как она будет подготовлена на всех представителях поколения.

На слайде показано поколение 1 в кластере из трех узлов.

Page 12: Проверка состояния кластера

Проверим статус узла кластера:

=> SELECT * FROM mtm.status() \gx
-[ RECORD 1 ]------+--------
my_node_id         | 1
status             | online
connected          | {1,2,3}
gen_num            | 1
gen_members        | {1,2,3}
gen_members_online | {1,2,3}
gen_configured     | {1,2,3}

На любом из узлов кластера можно получить список всех узлов:

=> SELECT id, conninfo, enabled, connected FROM mtm.nodes() \gx
-[ RECORD 1 ]-------------------------------------------------------
id        | 1
conninfo  | dbname=pgpro_multimaster user=multimaster_user port=5001
enabled   | t
connected | t
-[ RECORD 2 ]-------------------------------------------------------
id        | 2
conninfo  | dbname=pgpro_multimaster user=multimaster_user port=5002
enabled   | t
connected | t
-[ RECORD 3 ]-------------------------------------------------------
id        | 3
conninfo  | dbname=pgpro_multimaster user=multimaster_user port=5003
enabled   | t
connected | t

Проверим работу кластера — создадим таблицу на первом узле. Рекомендуется использовать таблицы с явно определенным первичным ключом — мы так и поступим:

=> CREATE TABLE multimaster_tbl (id integer PRIMARY KEY);
CREATE TABLE

Добавим в таблицу несколько строк:

=> INSERT INTO multimaster_tbl VALUES (1),(2),(3);
INSERT 0 3

Проверим доступность таблицы multimaster_tbl на втором узле:

=> SELECT * FROM multimaster_tbl;
 id
----
  1
  2
  3
(3 rows)

Таблица и строки доступны на втором узле — кластер работает.

Page 13: Отказ узла

Source slide page 13: Отказ узла{#source-figure-013 caption=“Source slide page 13: Отказ узла”}

Комментарии преподавателя

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

https://postgrespro.ru/docs/enterprise/16/multimaster#MULTIMASTER-FAIL URE-DETECTION-AND-RECOVERY

Page 14: Отказ узла

Для демонстрации отказа узла остановим экземпляр Postgres Pro на третьем сервере:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-3 \
  stop
waiting for server to shut down........ done
server stopped

Проверим доступные узлы кластера:

=> SELECT * FROM mtm.status() \gx
-[ RECORD 1 ]------+--------
my_node_id         | 1
status             | online
connected          | {1,2}
gen_num            | 2
gen_members        | {1,2}
gen_members_online | {1,2}
gen_configured     | {1,2,3}

Номер поколения увеличился. Запишем еще три строки в таблицу, теперь на втором узле:

=> INSERT INTO multimaster_tbl VALUES (11),(22),(33);
INSERT 0 3

Проверим корректную работу кластера — прочитаем все строки на первом узле:

=> SELECT * FROM multimaster_tbl;
 id
----
  1
  2
  3
 11
 22
 33
(6 rows)

Добавленные строки видны — кластер продолжает работать даже при отсутствии одного из узлов.

Page 15: Восстановление узла

Source slide page 15: Восстановление узла{#source-figure-015 caption=“Source slide page 15: Восстановление узла”}

Комментарии преподавателя

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

После окончания восстановления третий узел предлагает включить его в кластер, формируя новое поколение 3.

Page 16: Восстановление узла

Текущее состояние узлов кластера:

=> SELECT * FROM mtm.status() \gx
-[ RECORD 1 ]------+--------
my_node_id         | 1
status             | online
connected          | {1,2}
gen_num            | 2
gen_members        | {1,2}
gen_members_online | {1,2}
gen_configured     | {1,2,3}

Вернем третий узел в состав кластера. Для этого запустим экземпляр Postgres Pro на третьем узле:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-3 \
  -l /var/lib/pgpro/ent-16-MM-3/pgpro-ent.log \
  start
waiting for server to start.... done
server started

Сразу после подключения видим, что третий узел пока что не готов к работе:

student$ psql -p 5003 -U postgres
=> SELECT * FROM mtm.status() \gx
-[ RECORD 1 ]------+--------
my_node_id         | 1
status             | online
connected          | {1,2,3}
gen_num            | 2
gen_members        | {1,2}
gen_members_online | {1,2}
gen_configured     | {1,2,3}

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

=> SELECT * FROM mtm.status() \gx
-[ RECORD 1 ]------+--------
my_node_id         | 1
status             | online
connected          | {1,2,3}
gen_num            | 3
gen_members        | {1,2,3}
gen_members_online | {1,2,3}
gen_configured     | {1,2,3}

Все добавленные ранее строки видны на третьем узле.

=> \c pgpro_multimaster multimaster_user
You are now connected to database "pgpro_multimaster" as user "multimaster_user".
=> SELECT * FROM multimaster_tbl;
 id
----
  1
  2
  3
 11
 22
 33
(6 rows)

Page 17: Page 17

Page 18: Режим 2+1

Source slide page 18: Режим 2+1{#source-figure-018 caption=“Source slide page 18: Режим 2+1”}

Комментарии преподавателя

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

Помимо этого, существует режим работы кластера мультимастера 2+1 (его еще называют режимом работы с рефери). В этом случае в кластер входят два полноценных узла, а третий узел работает только как голосующий — он принимает решения о подтверждении и отмене транзакций.

На узле-рефери достаточно установить Postgres Pro Enterprise и добавить расширение referee. Узел-рефери не хранит никакие данные кластера, поэтому нагрузка на узел невелика и он не требователен к ресурсам.

https://postgrespro.ru/docs/enterprise/16/multimaster#SETTING-UP-A-REF EREE

Page 19: Режим 2+1

Source slide page 19: Режим 2+1{#source-figure-019 caption=“Source slide page 19: Режим 2+1”}

Для перехода в режим работы 2+1 удалим из кластера один из узлов (третий):

=> SELECT mtm.drop_node(3);
 drop_node
-----------
(1 row)

На удаленном узле добавим расширение referee:

=> CREATE EXTENSION referee;
CREATE EXTENSION

Остановим остальные два узла:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-1 \
  stop
waiting for server to shut down........ done
server stopped
postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-2 \
  stop
waiting for server to shut down........ done
server stopped

На каждом из двух узлов в файл конфигурации необходимо добавить строку подключения к узлу-рефери:

/var/lib/pgpro/ent-16-MM-1/conf.d/referee.conf
multimaster.referee_connstring = 'dbname=pgpro_multimaster user=multimaster_user
port=5003'
/var/lib/pgpro/ent-16-MM-2/conf.d/referee.conf
multimaster.referee_connstring = 'dbname=pgpro_multimaster user=multimaster_user
port=5003'

Запускаем любой из основных узлов. Например второй:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-2 \
  -l /var/lib/pgpro/ent-16-MM-2/pgpro-ent.log \
  start
waiting for server to start.... done
server started
student$ psql -p 5002 -U multimaster_user -d pgpro_multimaster

Запускаем другой узел:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-1 \
  -l /var/lib/pgpro/ent-16-MM-1/pgpro-ent.log \
  start
waiting for server to start.... done
server started
student$ psql -p 5001 -U multimaster_user -d pgpro_multimaster

Проверим статус кластера:

=> SELECT * FROM mtm.status() \gx

Page 20: Page 20

-[ RECORD 1 ]------+-------
my_node_id         | 2
status             | online
connected          | {1,2}
gen_num            | 6
gen_members        | {1,2}
gen_members_online | {1,2}
gen_configured     | {1,2}

Проверим также, что ранее созданная таблица на месте:

=> SELECT * FROM multimaster_tbl;
 id
----
  1
  2
  3
 11
 22
 33
(6 rows)

Снова добавим строки в таблицу multimaster_tbl:

=> INSERT INTO multimaster_tbl VALUES (111),(222),(333);
INSERT 0 3
=> SELECT * FROM multimaster_tbl;
 id
-----
   1
   2
   3
  11
  22
  33
 111
 222
 333
(9 rows)

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

student$ sudo grep referee /var/lib/pgpro/ent-16-MM-2/pgpro-ent.log
2026-07-28 00:01:13.730 MSK [856270] LOG:  [MTM] [mtm-campaigner] got referee grant in
generation num=5
2026-07-28 00:01:13.912 MSK [856272] LOG:  [MTM] [mtm-resolver] resolving as referee
winner

Первый узел сообщает, что не может получить выигрышный голос (grant) при голосовании, так как такой голос уже выдан второму узлу:

student$ sudo grep referee /var/lib/pgpro/ent-16-MM-1/pgpro-ent.log
2026-07-28 00:01:14.458 MSK [857526] WARNING:  [MTM] [mtm-campaigner] 'select
referee.request_grant(1, 5)' failed: ERROR:  grant was already issued to node 2 in
generation 5
CONTEXT:  PL/pgSQL function referee.request_grant(integer,bigint) line 19 at RAISE
2026-07-28 00:01:14.541 MSK [857526] WARNING:  [MTM] [mtm-campaigner] 'select
referee.request_grant(1, 5)' failed: ERROR:  grant was already issued to node 2 in
generation 5
CONTEXT:  PL/pgSQL function referee.request_grant(integer,bigint) line 19 at RAISE

Page 21: Итоги

Postgres Pro Multimaster — это кластерное решение со всеми ведущими узлами, обеспечивающее автоматическое восстановление узлов после сбоев

  • Не требует установки дополнительного ПО
  • Сфера применения — системы высокой доступности

Page 22: Практика

  1. Настройте кластер для режима работы 2+1. 2. Имитируйте отказ одного из двух узлов и проверьте работоспособность кластера. 3. Верните отключенный узел в строй.

Комментарии преподавателя

  1. Подготовьте три дополнительных экземпляра Postgres Pro Enterprise: для каждого из них инициализируйте каталог данных, настройте аутентификацию и задайте параметры. Подробности можно посмотреть в решении практики.

Page 23: 1. Настройка кластера для режима работы 2+1

Остановим основной сервер.

student$ sudo systemctl stop postgrespro-ent-16.service

Инициализируем кластер из двух узлов и третий узел для рефери. Сначала подготовим три дополнительных экземпляра сервера Postgres Pro. Создаем каталог PGDATA (если экземпляр работает, его предварительно надо остановить):

student$ sudo rm -rf /var/lib/pgpro/ent-16-MM-1
student$ sudo mkdir /var/lib/pgpro/ent-16-MM-1
student$ sudo chown postgres: -R /var/lib/pgpro/ent-16-MM-1

Инициализируем каталог данных:

postgres$ /opt/pgpro/ent-16/bin/initdb -U postgres -k -D /var/lib/pgpro/ent-16-MM-1
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
initdb: --icu-locale not specified, using lc_collate
Using language tag "en-US" for ICU locale "en_US.UTF-8".
The database cluster will be initialized with this locale configuration:
  provider:    icu
  ICU locale:  en-US
  LC_COLLATE:  en_US.UTF-8
  LC_CTYPE:    en_US.UTF-8
  LC_MESSAGES: en_US.UTF-8
  LC_MONETARY: ru_RU.UTF-8
  LC_NUMERIC:  ru_RU.UTF-8
  LC_TIME:     ru_RU.UTF-8
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".
Data page checksums are enabled.
fixing permissions on existing directory /var/lib/pgpro/ent-16-MM-1 ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Europe/Moscow
creating configuration files ... ok
performing automatic tuning ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or
--auth-local and --auth-host, the next time you run initdb.
Success. You can now start the database server using:
    /opt/pgpro/ent-16/bin/pg_ctl -D /var/lib/pgpro/ent-16-MM-1 -l logfile start

Роль multimaster_user должна иметь право использовать протокол репликации и подключаться к базе pgpro_multimaster, настройки аутентификации это разрешают:

postgres$ grep ^local /var/lib/pgpro/ent-16-MM-1/pg_hba.conf
local   all             all                                     trust
local   replication     all                                     trust

Конфигурационный файл:

postgres$ echo include_dir \'conf.d\' >> /var/lib/pgpro/ent-16-MM-1/postgresql.conf
postgres$ mkdir /var/lib/pgpro/ent-16-MM-1/conf.d
/var/lib/pgpro/ent-16-MM-1/conf.d/multimaster.conf
cluster_name = 'node1'
shared_preload_libraries = 'multimaster'

Page 24: Page 24

wal_level = logical
max_connections = 100           # значение по умолчанию
max_prepared_transactions = 300
max_wal_senders = 10            # значение по умолчанию
max_replication_slots = 10      # значение по умолчанию
wal_sender_timeout = 0
max_worker_processes = 320
port = 5001

Запускаем экземпляр:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-1 \
  -l /var/lib/pgpro/ent-16-MM-1/pgpro-ent.log \
  start
waiting for server to start.... done
server started

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

student$ psql -p 5001 -U postgres
=> CREATE USER multimaster_user WITH SUPERUSER;
CREATE ROLE
=> CREATE DATABASE pgpro_multimaster OWNER multimaster_user;
CREATE DATABASE
=> \c pgpro_multimaster multimaster_user
You are now connected to database "pgpro_multimaster" as user "multimaster_user".

Аналогично нужно подготовить еще два экземпляра на портах 5002 и 5003. На третьем узле добавим расширение referee:

student$ psql -p 5003 -U multimaster_user -d pgpro_multimaster
=> CREATE EXTENSION referee;
CREATE EXTENSION

На каждом из двух основных узлов добавим в файл конфигурации строку подключения к узлу-рефери:

/var/lib/pgpro/ent-16-MM-1/conf.d/referee.conf
multimaster.referee_connstring = 'dbname=pgpro_multimaster user=multimaster_user
port=5003'
/var/lib/pgpro/ent-16-MM-2/conf.d/referee.conf
multimaster.referee_connstring = 'dbname=pgpro_multimaster user=multimaster_user
port=5003'

Перезапустим первый и второй узлы, чтобы применить настройки:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-1 \
  stop
waiting for server to shut down........ done
server stopped
postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-2 \
  stop
waiting for server to shut down....... done
server stopped
postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-1 \
  -l /var/lib/pgpro/ent-16-MM-1/pgpro-ent.log \
  start

Page 25: Page 25

waiting for server to start.... done
server started
student$ psql -p 5001 -U multimaster_user -d pgpro_multimaster
postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-2 \
  -l /var/lib/pgpro/ent-16-MM-2/pgpro-ent.log \
  start
waiting for server to start.... done
server started
student$ psql -p 5002 -U multimaster_user -d pgpro_multimaster

Инициализируем кластер мультимастера.

=> CREATE EXTENSION multimaster;
CREATE EXTENSION

Теперь запустим функцию инициализации кластера. Первый параметр — это строка подключения к текущему узлу, второй — подключение ко второму узлу:

=> SELECT mtm.init_cluster(
     'dbname=pgpro_multimaster user=multimaster_user port=5001',
   '{"dbname=pgpro_multimaster user=multimaster_user port=5002"}');
 init_cluster
--------------
(1 row)
=> SELECT * FROM mtm.status() \gx
-[ RECORD 1 ]------+-------
my_node_id         | 1
status             | online
connected          | {1,2}
gen_num            | 3
gen_members        | {1,2}
gen_members_online | {1,2}
gen_configured     | {1,2}

Создадим таблицу с первичным ключом multimaster_tbl и добавим в нее несколько строк:

=> CREATE TABLE multimaster_tbl (id integer PRIMARY KEY);
CREATE TABLE
=> INSERT INTO multimaster_tbl VALUES (1),(2),(3);
INSERT 0 3

Проверим доступность таблицы multimaster_tbl на втором узле:

=> SELECT * FROM multimaster_tbl;
 id
----
  1
  2
  3
(3 rows)

2. Имитация сбоя и проверка работоспособности

Имитируем отказ узла. Для этого отключим один из узлов, например, второй:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-2 \
  stop
waiting for server to shut down..... done
server stopped
=> SELECT * FROM mtm.status() \gx

Page 26: Page 26

-[ RECORD 1 ]------+-------
my_node_id         | 1
status             | online
connected          | {1}
gen_num            | 4
gen_members        | {1}
gen_members_online | {1}
gen_configured     | {1,2}

Добавим еще несколько строк в таблицу:

=> INSERT INTO multimaster_tbl VALUES (11),(22),(33);
INSERT 0 3

Оставшийся узел сохранил работоспособность.

3. Возвращение узла в строй

Вернем в строй второй узел:

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-2 \
  -l /var/lib/pgpro/ent-16-MM-2/pgpro-ent.log \
  start
waiting for server to start.... done
server started
student$ psql -p 5002 -U multimaster_user -d pgpro_multimaster

На втором узле проверим, что таблица доступна:

=> SELECT * FROM multimaster_tbl;
 id
----
  1
  2
  3
 11
 22
 33
(6 rows)

Кластер мультимастера продолжает работать в режиме 2+1.

Уборка

Остановим и удалим экземпляры multimaster и запустим основной.

postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-1 \
  stop
waiting for server to shut down..... done
server stopped
postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-2 \
  stop
waiting for server to shut down.... done
server stopped
postgres$ /opt/pgpro/ent-16/bin/pg_ctl \
  -w \
  -D /var/lib/pgpro/ent-16-MM-3 \
  stop
waiting for server to shut down....... done
server stopped
student$ sudo rm -rf /var/lib/pgpro/ent-16-MM-1 /var/lib/pgpro/ent-16-MM-2 /var/lib/pgpro/ent-16-MM-3
student$ sudo systemctl start postgrespro-ent-16.service

Page 27: Page 27