F.31. multimaster
multimaster
— это расширение Postgres Pro Enterprise, которое в сочетании с набором доработок ядра превращает Postgres Pro Enterprise в синхронный кластер без разделения ресурсов, который обеспечивает масштабируемость OLTP для читающих транзакций, а также высокую степень доступности с автоматическим восстановлением после сбоев.
По сравнению со стандартным кластером PostgreSQL конструкции ведущий-ведомый, в кластере, построенном с использованием multimaster
, все узлы являются ведущими. Это даёт следующие преимущества:
Изоляция транзакций на уровне кластера
Синхронная логическая репликация и репликация DDL
Поддерживается работа с временными таблицами на каждом узле кластера
Устойчивость к сбоям и автоматическое восстановление узлов
Обновление серверов PostgreSQL «на ходу»
Важно
Поддержка расширения multimaster
в Postgres Pro Enterprise 9.6 прекращена. Для обеспечения стабильной работы расширения multimaster
рекомендуется перейти на последнюю версию Postgres Pro Enterprise.
Расширение multimaster
реплицирует вашу базу данных на все узлы кластера и позволяет выполнять пишущие транзакции на любом узле. Для согласованности данных в случае одновременных изменений multimaster
обеспечивает изоляцию транзакций в рамках всего кластера, реализуя MVCC (Multiversion Concurrency Control, Многоверсионное управление конкурентным доступом) на уровнях изоляции Read Committed и Repeatable Read. Каждая пишущая транзакция синхронно реплицируется на все узлы, что увеличивает задержку фиксации на время, требующееся для синхронизации. Читающие транзакции и запросы выполняются локально, без каких-либо ощутимых издержек.
Для обеспечения высокой степени доступности и отказоустойчивости кластера multimaster
использует протокол трёхфазной фиксации и контроль состояния для обнаружения сбоев. Кластер с N
ведущими узлами может продолжать работать, пока функционируют и доступны друг для друга большинство узлов. Чтобы в кластере можно было настроить multimaster
, он должен включать в себя как минимум два узла. Чаще всего для обеспечения высокой степени доступности достаточно трёх узлов. Так как на всех узлах кластера будут одни и те же данные, обычно нет смысла делать более пяти узлов в кластере.
Когда узел снова подключается к кластеру, multimaster
может автоматически привести его в актуальное состояние и наверстать упущенное, используя данные WAL из соответствующего слота репликации. Если данные WAL на момент времени, когда узел был исключён из кластера, оказываются недоступны, этот узел можно восстановить, воспользовавшись pg_basebackup.
Примечание
Применяя multimaster
, необходимо учитывать ограничения, связанные с репликацией. За подробностями обратитесь к Подразделу F.31.1.
Чтобы узнать больше о внутреннем устройстве multimaster
, обратитесь к Подразделу F.31.2.
F.31.1. Ограничения
Расширение multimaster
осуществляет репликацию данных полностью автоматическим образом. Вы можете одновременно выполнять пишущие транзакции и работать с временными таблицами на любом узле кластера. Однако при этом нужно учитывать следующие ограничения репликации:
Операционная система Microsoft Windows не поддерживается.
multimaster
может реплицировать только одну базу данных в кластере, заданную в переменнойmultimaster.conn_strings
. Если подключиться к другой базе данных, при попытке выполнения всех операций будет выдаваться соответствующее сообщение об ошибке.Вследствие ограничений логической репликации в реплицируемых таблицах должны быть первичные ключи или репликационные идентификаторы. Хотя таблицы без первичных ключей могут реплицироваться,
multimaster
не разрешает операцииUPDATE
иDELETE
в таких таблицах. За подробностями обратитесь кmultimaster.ignore_tables_without_pk
. Нежурналируемые таблицы не реплицируются, как и в стандартном PostgreSQL.Уровень изоляции. Расширение
multimaster
поддерживает только уровни изоляцииread committed
иrepeatable read
. УровеньSerializable
в настоящее время не поддерживается.Важно
На уровне
repeatable read
более вероятны сбои сериализации в момент фиксации. В отличие от стандартного PostgreSQL, в кластере multimaster на уровнеread committed
также могут происходить сбои сериализации (с кодом SQLSTATE 40001).Когда выполняется пишущая транзакция,
multimaster
блокирует задействуемые объекты только на том узле, где она выполняется. Но так как пишущие транзакции могут выполняться на всех узлах, другие транзакции могут попытаться в то же время изменить те же объекты, что и соседние узлы. В этом случае репликация первой транзакции будет невозможна, так как задействованные объекты уже будут заблокированы другой транзакцией. По той же причине и последующую транзакцию нельзя будет реплицировать на первый узел. В этом случае происходит распределённая взаимоблокировка. В результате одна из транзакций автоматически откатывается и её необходимо повторить. Приложение в такой ситуации должно быть готово повторять транзакции.Если у вас при типичной нагрузке происходит слишком много откатов транзакций, рекомендуется использовать уровень изоляции
read committed
. Однако уровеньread committed
всё же не гарантирует отсутствие взаимоблокировок в кластере multimaster. Если использование уровняread committed
не помогает, попробуйте направить все пишущие транзакции на один узел.В кластере с несколькими ведущими команда
ALTER SYSTEM
влияет только на конфигурацию текущего узла. Если вы хотите изменить параметры конфигурации во всём кластере, вам нужно выполнить эту команду на каждом узле.Генерация последовательностей. Во избежание конфликтов уникальных идентификаторов на разных узлах,
multimaster
меняет стандартное поведение генераторов последовательностей. По умолчанию для каждого узла идентификаторы генерируются, начиная с номера узла, и увеличиваются на число узлов. Например, в кластере с тремя узлами идентификаторы 1, 4 и 7 выделяются для объектов, создаваемых на первом узле, а 2, 5 и 8 резервируются для второго узла. Если число узлов в кластере изменяется, величина прироста идентификаторов корректируется соответственно. Таким образом, значения последовательностей будут не монотонными. Если важно, чтобы последовательность во всём кластере увеличивалась монотонно, задайте для параметраmultimaster.monotonic_sequences
значениеtrue
.Репликация DDL. Тогда как
multimaster
реплицирует данные на логическом уровне, DDL реплицируется на уровне операторов, что приводит к распределённой фиксации одного и того же оператора на разных узлах. В результате сложные сценарии с DDL, например хранимые процедуры и временные таблицы, могут работать не так, как в стандартном PostgreSQL.Задержка фиксации. В текущей реализации логической репликации
multimaster
передаёт данные узлам-подписчикам только после локальной фиксации, так что приходится ожидать двойной обработки транзакции: сначала на локальном узле, а затем на всех других узлах одновременно. Если транзакция производит запись в большом объёме, задержка может быть весьма ощутимой.
Если какие-либо данные должны присутствовать только на одном из узлов кластера, вы можете исключить таблицу с ними из репликации следующим образом:
SELECT mtm.make_table_local('table_name')
F.31.2. Архитектура
F.31.2.1. Репликация
Так как каждый сервер в кластере multimaster
может принимать запросы на запись, любой сервер может прервать транзакцию из-за параллельного изменения — так же как это происходит на одном сервере с несколькими обслуживающими процессами. Чтобы обеспечить высокую степень доступности и согласованность данных на всех узлах кластера, multimaster
применяет логическую репликацию и протокол трёхфазной фиксации E3PC.
Когда Postgres Pro Enterprise загружает разделяемую библиотеку multimaster
, код multimaster
создаёт поставщика и потребителя логической репликации для каждого узла и внедряется в процедуру фиксирования транзакций. Типичная последовательность действий при репликации данных включает следующие фазы:
Фаза
PREPARE
. Кодmultimaster
перехватывает каждый операторCOMMIT
и преобразует его в операторPREPARE
. Все узлы, получающие транзакцию через протокол репликации (узлы когорты), передают свой голос для одобрения или отклонения транзакции процессу-арбитру на исходном узле. Это гарантирует, что вся когорта может принять эту транзакцию и конфликт при записи отсутствует. Подробнее поддержка транзакций сPREPARE
в PostgreSQL рассматривается в описании PREPARE TRANSACTION.Фаза
PRECOMMIT
. Если все узлы когорты одобряют транзакцию, процесс-арбитр отправляет всем этим узлам сообщениеPRECOMMIT
, выражающее намерение зафиксировать эту транзакцию. Узлы когорты отвечают арбитру сообщениемPRECOMMITTED
. В случае сбоя все узлы могут использовать эту информацию для завершения транзакции по правилам кворума.Фаза
COMMIT
. Если результатPRECOMMIT
положительный, арбитр фиксирует транзакцию на всех узлах.
Важно
Расширение multimaster
в настоящее время поддерживает только уровни изоляции read committed
и repeatable read
, с которыми в рабочей нагрузке могут происходить сбои сериализации. За подробностями обратитесь к Подразделу F.31.1.
Если узел отказывает или отключается от кластера между фазами PREPARE
и COMMIT
, фаза PRECOMMIT
даёт гарантию, что оставшиеся в строю узлы имеют достаточно информации для завершения подготовленной транзакции. Сообщения PRECOMMITTED
помогают избежать ситуации, когда отказавший узел мог бы зафиксировать или прервать транзакцию, но не успел бы уведомить о состоянии транзакции другие узлы. При двухфазной фиксации (2PC, two-phase commit), такая транзакция заблокировала бы ресурсы (удерживала блокировки) до восстановления отказавшего узла. В противном случае была бы возможна несогласованность данных после восстановления отказавшего узла, например, если отказавший узел зафиксировал транзакцию, а оставшийся узел откатил её.
Для фиксирования транзакции арбитр должен получить ответ от большинства узлов. Например, в кластере из 2N
+ 1 узлов необходимо получить минимум N
+ 1 ответов. Таким образом multimaster
обеспечивает доступность кластера для чтения и записи пока работает большинство узлов и гарантирует согласованность данных при отказе узла или прерывании соединения. Подробнее механизм обнаружения сбоев описан в Подразделе F.31.2.2.
F.31.2.2. Обнаружение сбоя и восстановление
Так как multimaster
допускает запись на всех узлах, он должен ждать ответа с подтверждением транзакции от всех остальных узлов. Если не принять специальных мер, в случае отказа узла для фиксации транзакции пришлось бы ждать пока он не будет восстановлен. Чтобы не допустить этого, multimaster
периодически опрашивает узлы и проверяет их состояние и соединение между ними. Когда узел не отвечает на несколько контрольных обращений подряд, этот узел убирается из кластера, чтобы оставшиеся в строю узлы могли производить запись. Частоту обращений и тайм-аут ожидания ответа можно задать в параметрах multimaster.heartbeat_send_timeout
и multimaster.heartbeat_recv_timeout
, соответственно.
Работающие узлы не имеют возможности отличить отказавший узел, переставший обрабатывать запросы, от узла в недоступной сети, к которому могут обращаться пользователи БД, но не другие узлы. Во избежание конфликтов записи на узлах в разделённых сегментах сети, multimaster
разрешает выполнять запись только на тех узлах, которые видят большинство.
Например, предположим, что кластер с пятью ведущими узлами в результате сетевого сбоя разделился на две изолированных подсети так, что в одной оказалось два, а в другой — три узла кластера. На основе информации о доступности узлов multimaster
продолжит принимать запросы на запись на всех узлах в большем разделе и запретит запись в меньшем. Таким образом, кластер, состоящий из 2N
+ 1 узлов может справиться с отказом N
узлов и продолжать функционировать пока будут работать и связаны друг с другом N
+ 1 узлов.
Подсказка
Для кластеров с чётным числом узлов это поведение можно переопределить. За подробностями обратитесь к Подразделу F.31.3.3.
В случае частичного разделения сети, когда разные узлы связаны с другими по-разному, multimaster
находит подмножество полностью связанных узлов и отключает другие узлы. Например, в кластере с тремя узлами, если узел A может связаться и с B, и с C, а узел B не может связаться с C, multimaster
изолирует узел C для обеспечения согласованности данных на узлах A и B.
Если вы попытаетесь обратиться к отключённому узлу, multimaster
возвратит сообщение об ошибке, говорящее о текущем состоянии узла. Для предотвращения чтения неактуальных данных на нём запрещаются и запросы только на чтение. Кроме того, подключения клиентов к отключённому узлу могут разрываться, если установить переменную multimaster.break_connection
.
Каждый узел поддерживает свою структуру данных, в которой учитывает состояние всех узлов относительно него самого. Вы можете получить эту информацию в представлении mtm.get_nodes_state()
.
Когда ранее отказавший узел возвращается в кластер, multimaster
начинает автоматическое восстановление:
Вновь подключённый узел выбирает случайный узел кластера и начинает навёрстывать текущее состояние кластера, используя WAL.
Когда узел синхронизируется до минимальной задержки восстановления, все узлы кластера блокируются на запись (не допускают пишущие транзакции), чтобы процесс восстановления закончился. По умолчанию минимальная задержка восстановления равняется 10 КБ. Это значение можно изменить в переменной
multimaster.min_recovery_lag
.По завершении восстановления
multimaster
повышает вновь подключённый узел, переводит его в рабочее состояние и включает в схему репликации.
Примечание
Автоматическое восстановление возможно, только если отставание в WAL отказавшего узла от работающих не превышает значения multimaster.max_recovery_lag
. Если же отставание в WAL оказывается больше значения multimaster.max_recovery_lag
, этот узел можно восстановить вручную с одного из работающих узлов, используя pg_basebackup.
См. также
F.31.3. Установка и подготовка
Чтобы использовать multimaster
, необходимо установить Postgres Pro Enterprise на всех узлах кластера. В состав Postgres Pro Enterprise включены все необходимые зависимости и расширения.
F.31.3.1. Подготовка кластера
Установив Postgres Pro Enterprise на всех узлах, вы должны настроить кластер серверов средствами multimaster
.
Предположим, что вам нужно организовать кластер из трёх узлов с доменными именами node1
, node2
и node3
. Прежде всего разверните базу данных, которая будет реплицироваться, и выберите пользователя СУБД с правами суперпользователя для репликации:
Если вы делаете всё с нуля, инициализируйте кластер баз данных, создайте пустую базу данных
mydb
и пользователя СУБДmyuser
с правами суперпользователя на каждом узле кластера. Подробнее об этом можно прочитать в Разделе 18.2.Если у вас уже есть база данных
mydb
на сервереnode1
, создайте нового пользователя СУБДmyuser
с правами суперпользователя и проинициализируйте новые узлы на основе существующего, запустив pg_basebackup от имени этого пользователя. Выполните на каждом узле, который вы будете добавлять, следующую команду:pg_basebackup -D
каталог_данных
-h node1 -U myuserЗдесь
каталог_данных
— это каталог, содержащий данные кластера БД. Этот каталог указывается на этапе инициализации кластера или задаётся в переменной окруженияPGDATA
. Для выполнения этой задачи вместоmyuser
вы можете воспользоваться любым другим пользователем СУБД с административными правами.Более подробно использование pg_basebackup описано в pg_basebackup.
Когда база данных будет готова, выполните следующие действия на всех узлах кластера:
Разрешите репликацию базы
mydb
на каждый узел кластера для пользователяmyuser
, как описывается в Разделе 20.1. При этом важно использовать метод аутентификации, удовлетворяющий вашим требованиям безопасности.Измените файл конфигурации
postgresql.conf
следующим образом:Добавьте
multimaster
в переменнуюshared_preload_libraries
:shared_preload_libraries = 'multimaster'
Подсказка
Если переменная
shared_preload_libraries
уже определена вpostgresql.auto.conf
, вам потребуется изменить её значение с помощью команды ALTER SYSTEM. За подробностями обратитесь к Подразделу 19.1.2. Заметьте, что в кластере с несколькими ведущими командаALTER SYSTEM
влияет только на конфигурацию того узла, на котором запускается.Задайте уровень изоляции транзакций для вашего кластера. В настоящее время
multimaster
поддерживает уровни read committed и repeatable read.default_transaction_isolation = 'read committed'
Важно
На уровне
repeatable read
более вероятны сбои сериализации в момент фиксации. Если ваше приложение не обрабатывает такие сбои, рекомендуется использовать уровеньread committed
.Установите параметры PostgreSQL, связанные с репликацией.
wal_level = logical max_connections = 100 max_prepared_transactions = 300 max_wal_senders = 10 # не меньше количества узлов max_replication_slots = 10 # не меньше количества узлов
Вы должны сменить уровень репликации на
logical
, так как работаmultimaster
построена на логической репликации. Для кластера сN
узлами разрешите минимумN
передающих WAL процессов и слотов репликации. Так какmultimaster
неявно добавляет фазуPREPARE
кCOMMIT
каждой транзакции, в качестве максимального количества подготовленных транзакций задайтеN
*max_connections
. В противном случае подготовленные транзакции могут ждать выполнения в очереди.Убедитесь в том, что на каждом узле выделено достаточно фоновых рабочих процессов:
max_worker_processes = 250
Например, для кластера с тремя узлами и ограничением
max_connections
= 100 механизмуmultimaster
в пиковые моменты может потребоваться до 206 фоновых рабочих процессов: 200 рабочих процессов для обработки соединений соседних узлов, два — для передатчиков WAL, два — для приёмников WAL и ещё два для процессов-арбитров (передающего и принимающего). При выборе значения этого параметра не забывайте, что фоновые рабочие процессы могут быть нужны и другим модулям.Добавьте параметры, относящиеся к
multimaster
:multimaster.max_nodes = 3 # размер кластера multimaster.node_id = 1 # индекс этого узла в кластере, # начиная с 1 multimaster.conn_strings = 'dbname=mydb user=myuser host=node1 port=5432 arbiter_port=5433,dbname=mydb user=myuser host=node2 port=5432 arbiter_port=5433,dbname=mydb user=myuser host=node3 port=5432 arbiter_port=5433' # разделённый запятыми список строк # подключения к соседним узлам multimaster.arbiter_port = 5433
Переменная
multimaster.max_nodes
определяет максимальный размер кластера. Если вы планируете добавлять в кластер новые узлы, значениеmultimaster.max_nodes
должно быть больше начального числа узлов. В этом случае вы сможете добавлять новые узлы, не перезапуская Postgres Pro Enterprise, пока не будет достигнут этот максимум.В большинстве случаев для обеспечения высокой степени доступности достаточно трёх узлов. Так как на всех узлах кластера будут одни и те же данные, обычно нет смысла делать более пяти узлов в кластере.
Важно
В переменной
multimaster.node_id
задаются натуральные числа, начиная с 1, без пропусков в нумерации. Например, для кластера с пятью узлами задайте идентификаторы узлов 1, 2, 3, 4 и 5. При этом важно указывать в переменнойmultimaster.conn_strings
список узлов по порядку их идентификаторов. Значение переменнойmultimaster.conn_strings
должно быть одинаковым на всех узлах.Вы можете настроить свойства подключений, добавив другие параметры подключений libpq в строки подключения в переменной
multimaster.conn_strings
. По умолчаниюmultimaster
использует порт 5432 для установления соединений между узлами и порт 5433 для подключения к процессу арбитра. Если эти порты уже заняты, необходимо выбрать и указать другие порты, добавив параметрыport
иarbiter_port
в каждую строку подключения в переменнойmultimaster.conn_strings
.Если вы измените параметр
arbiter_port
, вы должны также задать этот порт в переменнойmultimaster.arbiter_port
. За подробностями обратитесь кmultimaster.arbiter_port
иmultimaster.conn_strings
.Чтобы проверить, свободны ли стандартные порты, вы можете выполнить следующую команду:
netstat -ln | grep -E '5432|5433'
Убедитесь в том, что эти порты не блокируются сетевым фильтром.
В зависимости от вашей схемы использования и конфигурации сети может потребоваться настроить и другие параметры
multimaster
. За подробностями обратитесь к Подразделу F.31.3.2.От имени суперпользователя ОС перезапустите Postgres Pro Enterprise:
pg_ctl -D
каталог_данных
-lpg.log
restart
Когда Postgres Pro Enterprise будет запущен на всех узлах, подключитесь к любому узлу под именем пользователя ОС postgres
и создайте расширение multimaster
в базе данных mydb
, которая будет реплицироваться:
psql -h node1 -d mydb CREATE EXTENSION multimaster;
Запрос CREATE EXTENSION
реплицируется на все узлы кластера.
Чтобы убедиться, что расширение multimaster
активно, прочитайте представление mtm.get_cluster_state()
:
SELECT mtm.get_cluster_state();
Если значение liveNodes
равняется allNodes
, значит ваш кластер успешно настроен и готов к использованию.
См. также
Настройка параметров конфигурации
F.31.3.1.1. Пример настройки кластера для CentOS, Oracle Linux и RHEL
В этом разделе показан пример настройки кластера с тремя узлами, которым назначены имена node1
, node2
и node3
. Репликация настраивается для базы данных с именем mydb
. Пользователем СУБД с правами суперпользователя, который будет выполнять репликацию, выбран myuser
. Все последующие команды должны выполняться пользователем ОС с правами администратора.
Сначала на всех узлах кластера от имени пользователя ОС
postgres
выполните следующие действия:# Настройка окружения каждого узла и создание пользователя myuser, # а также принадлежащей ему базы данных mydb. export PATH=/usr/pgproee-9.6/bin:$PATH export PGDATA=/var/lib/pgproee/9.6/data for i in `seq 1 3`; do echo "host replication myuser node$i md5" >> $PGDATA/pg_hba.conf echo "host mydb myuser node$i md5" >> $PGDATA/pg_hba.conf echo "node$i:5432:mydb:myuser:myuserpassword" >> ~/.pgpass done chmod 0600 ~/.pgpass cat << EOF | psql --dbname=postgres --username=postgres --port=5432 ALTER SYSTEM SET default_transaction_isolation = 'read committed'; ALTER SYSTEM SET wal_level = logical; ALTER SYSTEM SET max_connections = 100; ALTER SYSTEM SET max_prepared_transactions = 300; ALTER SYSTEM SET max_wal_senders = 10; ALTER SYSTEM SET max_replication_slots = 10; ALTER SYSTEM SET max_worker_processes = 250; ALTER SYSTEM SET shared_preload_libraries = 'multimaster'; CREATE USER myuser WITH SUPERUSER PASSWORD 'myuserpassword'; CREATE DATABASE mydb OWNER myuser; EOF # Определение числа узлов в кластере, назначение идентификаторов # и строк подключения для узлов. В этом примере предполагается, # что узлы называются node1, node2 и node3, а их идентификаторами # будут соответствующие числа, начиная с 1. cat << EOF >> $PGDATA/postgresql.conf multimaster.max_nodes = 3 multimaster.node_id = `hostname | awk '{ print substr($1,5,1) }'` multimaster.arbiter_port = 5433 multimaster.conn_strings = 'dbname=mydb user=myuser host=node1 port=5432 arbiter_port=5433,dbname=mydb user=myuser host=node2 port=5432 arbiter_port=5433,dbname=mydb user=myuser host=node3 port=5432 arbiter_port=5433' EOF
От имени пользователя ОС с правами суперпользователя перезапустите службу Postgres Pro Enterprise на всех узлах кластера:
sudo service postgrespro-enterprise-9.6 restart
Теперь создайте расширение
multimaster
на одном из узлов кластера, от имени пользователя ОСpostgres
. На все другие узлы оно будет реплицировано автоматически. Следующий пример иллюстрирует создание расширения на узле node1.psql --dbname=mydb --username=myuser --host=node1 --port=5432 -c "CREATE EXTENSION IF NOT EXISTS multimaster"
Кластер настроен и готов к использованию.
F.31.3.2. Настройка параметров конфигурации
Хотя вы можете использовать multimaster
и в стандартной конфигурации, для более быстрого обнаружения сбоев и более надёжного автоматического восстановления может быть полезно скорректировать несколько параметров.
F.31.3.2.1. Установка тайм-аута для обнаружения сбоев
Для проверки доступности соседних узлов multimaster
периодически опрашивает все узлы. Таймаут для обнаружения сбоев можно регулировать с помощью следующих переменных:
Переменная
multimaster.heartbeat_send_timeout
определяет интервал между опросами. По умолчанию её значение равно 200ms.Переменная
multimaster.heartbeat_recv_timeout
определяет интервал для ответа. Если за указанное время ответ от какого-то узла не будет получен, он считается отключённым и исключается из кластера. По умолчанию её значение равно 1000ms.
Значение multimaster.heartbeat_send_timeout
имеет смысл выбирать, исходя из типичных задержек ping между узлами. С уменьшением отношения значений recv/send сокращается время обнаружения сбоев, но увеличивается вероятность ложных срабатываний. При установке этого параметра учтите также типичный процент потерь пакетов между узлами кластера.
F.31.3.2.2. Настройка параметров автоматического восстановления
Когда узел кластера на время выходит из строя, multimaster
может автоматически восстановить его состояние, используя WAL, собранный на других узлах кластера. Для управления параметрами восстановления предназначены следующие переменные:
multimaster.min_recovery_lag
— задаёт минимальное расхождение WAL между восстанавливаемым узлом и текущим состоянием кластера. По умолчанию этот параметр равен 10 КБ. Когда ранее отключённый узел достигает состояния, отстающего от текущего наmultimaster.min_recovery_lag
,multimaster
прекращает фиксировать любые транзакции на активных узлах, пока данный узел окончательно не достигнет текущего состояния кластера. Когда данные будут полностью синхронизированы, ранее отключённый узел повышается и становится активным, после чего кластер в целом продолжает работу.multimaster.max_recovery_lag
— задаёт максимальный размер WAL. По достижении пределаmultimaster.max_recovery_lag
WAL для отключённого узла будет перезаписан. После этого автоматическое восстановление будет невозможно. В этом случае вы можете восстановить узел вручную, скопировав данные с одного из действующих узлов с помощью pg_basebackup.
По умолчанию значение multimaster.max_recovery_lag
равняется 1 ГБ. При увеличении multimaster.max_recovery_lag
увеличивается окно, в течение которого возможно автоматическое восстановление, но требуется больше места на диске для хранения WAL.
См. также
F.31.3.3. Определение кворума в кластерах с чётным числом узлов
По умолчанию multimaster
определяет состояние кворума в подмножестве узлов, учитывая состояние большинства: кластер может продолжать работать, только если функционирует большинство узлов и эти узлы могут связаться друг с другом. Для кластеров с чётным количеством узлом этот подход не является оптимальным. Например, если в результате сбоя сети кластер разделяется пополам или отказывает один из двух узлов в кластере, все узлы прекращают принимать запросы, несмотря на то, что половина узлов кластера работает нормально.
Для реализации отказоустойчивости в таких случаях вы можете поменять поведение multimaster
, основанное на состоянии большинства, одним из следующих способов:
Установить отдельный узел-рефери для признания кворума в подмножестве узлов, состоящем ровно из половины узлов кластера.
Выбрать главный узел, который будет продолжать работу независимо от состояния других узлов. Используйте эту возможность только в кластере с двумя узлами.
Важно
Во избежание «раздвоения» не используйте в одном кластере и главный узел, и рефери.
F.31.3.3.1. Установка отдельного узла-рефери
Рефери — это узел, голос которого позволяет определить, какое подмножество узлов составляет кворум, когда кластер разделяется пополам. Узел-рефери не хранит никакие данные кластера, поэтому он не создаёт значительную нагрузку и может быть размещён практически в любой системе, где установлен Postgres Pro Enterprise.
Чтобы настроить рефери в своём кластере:
Установите Postgres Pro Enterprise на узле, который вы планируете сделать рефери, и создайте расширение
referee
:CREATE EXTENSION referee;
Разрешите в файле
pg_hba.conf
доступ к узлу-рефери.На всех узлах кластера укажите строку подключения к рефери в файле
postgresql.conf
:multimaster.referee_connstring =
строка_подключения
Здесь
строка_подключения
должна содержать все параметры libpq, необходимые для обращения к рефери.
Первое подмножество узлов, которому удаётся подключиться к рефери, получает выигрышный голос и продолжает работать. Рефери сохраняет результат голосования до возвращения в строй всех остальных узлов кластера. Затем результат аннулируется и в случае следующего сбоя в сети будет выбираться новый кворум.
Во избежание «раздвоения» в кластере допускается использование только одного рефери. Не используйте рефери, если вы уже выбрали главный узел.
F.31.3.3.2. Настройка главного узла
Если вы назначите один из узлов главным, этот узел продолжит принимать запросы, даже если окажется изолированным в результате сбоя сети или выхода из строя других узлов. Эта возможность полезна в кластере с двумя узлами и для быстрого восстановления одного узла в разрушенном кластере.
Важно
Если в кластере больше двух узлов, назначение одного из узлов главным может привести к проблемам «раздвоения» кластера в случае сбоев в сети и уменьшить число возможных сценариев отказоустойчивости. В этих случаях имеет смысл установить отдельный узел-рефери.
Чтобы сделать один из узлов главным, установите параметр multimaster.major_node
на этом узле:
ALTER SYSTEM SET multimaster.major_node TO on SELECT pg_reload_conf();
Не устанавливайте параметр major_node
на более чем одном узле кластера. Если включить его на нескольких узлах, это может повлечь проблемы «раздвоения» кластера. Если в вашем кластере настроен рефери, использование параметра major_node
запрещается.
F.31.4. Администрирование кластера multimaster
F.31.4.1. Наблюдение за состоянием кластера
В составе расширения multimaster
есть несколько представлений, позволяющих наблюдать за текущим состоянием кластера.
Для проверки свойств определённого узла воспользуйтесь представлением mtm.get_nodes_state()
:
SELECT mtm.get_nodes_state();
Для проверки состояния кластера в целом воспользуйтесь представлением mtm.get_cluster_state()
:
SELECT mtm.get_cluster_state();
Выдаваемая ими информация подробно описана в Подразделе F.31.5.2.
F.31.4.2. Добавление узлов в кластер
Используя расширение multimaster
, вы можете добавлять или удалять узлы кластера, не останавливая службу баз данных.
Чтобы добавить узел, необходимо изменить конфигурацию кластера на работающих узлах, загрузить все данные на новом узле, используя pg_basebackup, и запустить этот узел.
Предположим, что у нас есть работающий кластер с тремя узлами с доменными именами node1
, node2
и node3
. Чтобы добавить node4
, следуйте этим указаниям:
Проверьте, не достигает ли текущее число узлов кластера значения, заданного в переменной
multimaster.max_nodes
. Если это значение достигается, увеличьтеmultimaster.max_nodes
на каждом узле и перезапустите все узлы. Вы можете перезапустить узлы по одному, не останавливая работу всей базы данных. Если предельное число узлов не достигнуто, перейдите к следующему шагу.Определите, какая строка подключения будет использоваться для обращения к новому узлу. Например, для базы данных
mydb
, пользователяmyuser
и нового узлаnode4
строка подключения может быть такой:"dbname=mydb user=myuser host=node4"
. За подробностями обратитесь кmultimaster.conn_strings
.В
psql
, подключённом к любому из работающих узлов, выполните:SELECT mtm.add_node('dbname=mydb user=myuser host=node4');
Эта команда меняет конфигурацию кластера на всех узлах и запускает слоты репликации для нового узла.
Подключитесь к новому узлу и скопируйте все данные с одного из работающих узлов на новый узел:
pg_basebackup -D
каталог_данных
-h node1 -xpg_basebackup копирует весь каталог данных с
node1
, вместе с параметрами конфигурации.Измените параметры в
postgresql.conf
наnode4
:multimaster.node_id = 4 multimaster.conn_strings = 'dbname=mydb user=myuser host=node1, dbname=mydb user=myuser host=node2, dbname=mydb user=myuser host=node3, dbname=mydb user=myuser host=node4'
Запустите Postgres Pro на новом узле:
pg_ctl -D
каталог_данных
-lpg.log
startКогда узел синхронизируется до достижения минимальной задержки восстановления, все узлы кластера блокируются от записи (не выполняют пишущие транзакции), пока новый узел не получит все изменения. По завершении восстановления данных
multimaster
повышает новый узел, переводит его в активное состояние и включает в схему репликации.
Чтобы новая конфигурация была загружена в случае перезапуска PostgreSQL, обновите параметры конфигурации на всех узлах кластера:
Измените параметр
multimaster.conn_strings
с учётом добавления нового узла.Разрешите в файле
pg_hba.conf
репликацию на новый узел.
См. также
F.31.4.3. Удаление узлов из кластера
Расширение multimaster
предоставляет функцию mtm.stop_node()
, которая может временно или навсегда удалить узлы из кластера.
Чтобы временно исключить узел из кластера, вызовите функцию mtm.stop_node()
, передав ей идентификатор узла. Например, чтобы исключить узел 3, выполните следующую команду на любом другом узле кластера:
SELECT mtm.stop_node(3);
Эта команда исключает узел 3 из кластера и прекращает репликацию на этот узел.
Пока задержка WAL между этим узлом и текущим состоянием кластера меньше значения multimaster.max_recovery_lag
, вы можете восстановить данный узел, выполнив следующую команду:
SELECT mtm.recover_node(3);
В противном случае следуйте процедуре, описанной в Подразделе F.31.4.4.
Примечание
Если вы просто отключите узел, он тоже будет исключён из кластера. Однако все транзакции в кластере будут заморожены на некоторое время, пока другие узлы не определят, что он отключён. Этот интервал времени определяется параметром multimaster.heartbeat_recv_timeout
.
Чтобы навсегда удалить узел из кластера:
Вызовите функцию
mtm.stop_node()
с параметромdrop_slot
, равнымtrue
:SELECT mtm.stop_node(3, true);
В результате будут отключены слоты репликации для узла 3 на всех узлах кластера и репликация на этот узел будет прекращена.
Измените параметры
multimaster.node_id
иmultimaster.conn_strings
вpostgresql.conf
на оставшихся узлах кластера, чтобы они отражали новое состояние кластера.Отредактируйте файл
pg_hba.conf
на оставшихся узлах кластера, чтобы отключить репликацию на удалённый узел, если требуется.
Если вы позже захотите возвратить узел в кластер, вам придётся добавить его как новый узел. За подробностями обратитесь к Подразделу F.31.4.2.
F.31.4.4. Восстановление узла кластера вручную
Расширение multimaster
может автоматически восстановить отказавший узел при наличии WAL на момент времени, когда узел отключился от кластера. Однако если объём изменений данных на активных узлах превышает допустимый размер WAL, заданный переменной multimaster.max_recovery_lag
, автоматическое восстановление невозможно. В этом случае вы можете восстановить отказавший узел вручную.
Предположим, что узел node2
покинул кластер, в котором было три узла, и его нужно восстановить вручную. Типичная процедура восстановления выглядит так:
В
psql
, подключённом к любому из работающих узлов, создайте слот репликации для отключённого узла, выполнив следующую команду:SELECT mtm.recover_node(2);
здесь 2 — идентификатор отключённого узла, заданный в переменной
multimaster.node_id
.Подключитесь к узлу
node2
и скопируйте все данные с одного из работающих узлов:pg_basebackup -D
каталог_данных
-h node1 -xpg_basebackup копирует весь каталог данных с
node1
, вместе с параметрами конфигурации.На восстановленном узле задайте в
multimaster.node_id
то же значение, что было на этом узле до сбоя.Убедитесь в том, что между восстановленным узлом и остальными узлами кластера настроена репликация.
Запустите Postgres Pro на восстановленном узле:
pg_ctl -D
каталог_данных
-lpg.log
startКогда узел синхронизируется до достижения минимальной задержки восстановления, все узлы кластера блокируются от записи (не выполняют пишущие транзакции), пока восстановленный узел не получит все изменения. По завершении восстановления данных
multimaster
повышает новый узел, переводит его в активное состояние и включает в схему репликации.
См. также
F.31.5. Справка
F.31.5.1. Переменные GUC
multimaster.node_id
Идентификатор узла — натуральное число, однозначно идентифицирующее узел в кластере. Нумерация узлов должна начинаться с 1 и не допускать пропусков. Например, в кластере с пятью узлами они должны иметь идентификаторы 1, 2, 3, 4 и 5.
multimaster.conn_strings
Строки подключения для всех узлов в кластере multimaster, разделённые запятыми. Значение
multimaster.conn_strings
должно быть одинаковым на всех узлах. Каждая строка подключения должна включать имя реплицируемой базы данных и доменное имя узла кластера. Например: 'dbname=mydb host=node1,dbname=mydb host=node2,dbname=mydb host=node3'. Дополнительно вы можете добавить другие параметры подключения, переопределяющие параметры по умолчанию. Строки подключения должны указываться в порядке идентификаторов узлов, задаваемых в переменнойmultimaster.node_id
. Строка подключения к i-му узлу должна находиться в i-той позиции. Если вы установили нестандартный порт в переменнойmultimaster.arbiter_port
на каком-либо узле, вы должны задать его в параметреarbiter_port
в строке подключения к этому узлу.multimaster.max_nodes
Максимально допустимое число узлов в кластере. Если вы планируете добавлять в кластер новые узлы, значение
multimaster.max_nodes
должно быть больше начального числа узлов. В этом случае вы сможете добавлять новые узлы, не перезапуская Postgres Pro Enterprise, пока не будет достигнут этот максимум. В большинстве случаев для обеспечения высокой степени доступности в кластере достаточно трёх узлов. Так как на всех узлах кластера будут одни и те же данные, обычно нет смысла делать более пяти узлов в кластере. Максимально допустимое число узлов не может превышать 64.По умолчанию число узлов, заданное в переменной
multimaster.conn_strings
multimaster.arbiter_port
Порт, через который процесс-арбитр принимает подключения. Если вы меняете значение по умолчанию, вы должны задать его в параметре
arbiter_port
в строке подключения для соответствующего узла.По умолчанию: 5433
multimaster.heartbeat_send_timeout
Интервал между контрольными обращениями, в миллисекундах. Процесс-арбитр рассылает широковещательные контрольные сообщения всем узлам для выявления проблем с соединениями.
По умолчанию: 200
multimaster.heartbeat_recv_timeout
Таймаут, в миллисекундах. Если за это время не поступит ответ на контрольные сообщения, узел будет исключён из кластера.
По умолчанию: 1000
multimaster.min_recovery_lag
Минимальное отставание в WAL восстанавливаемого узла от текущего состояния кластера, в килобайтах. Когда достигается такое отставание в процессе восстановления узла, в кластере блокируется запись до завершения восстановления.
По умолчанию: 10 КБ
multimaster.max_recovery_lag
Максимальный размер отставания в WAL, в килобайтах. Когда узел отключается от кластера, другие узлы копируют данные WAL для всех новых транзакций в слот репликации для этого узла. По достижении значения
multimaster.max_recovery_lag
слот репликации для отключившегося узла удаляется с целью не допустить переполнения. После этого автоматическое восстановление узла становится невозможным. В этом случае вы можете восстановить узел вручную, скопировав данные с одного из работающих узлов, используя pg_basebackup или подобное средство. Если записать в эту переменную ноль, слот не будет удаляться.По умолчанию: 1 ГБ
multimaster.ignore_tables_without_pk
Логическая переменная. Эта переменная разрешает/запрещает репликацию операций
INSERT
для таблиц, не имеющих первичных ключей. По умолчанию такая репликация разрешена. При включении этого параметра операцииINSERT
для таких таблиц не реплицируются. Вне зависимости от этого значения, операции DDL для таблиц без первичных ключей реплицируются всегда, а операцииUPDATE
иDELETE
не реплицируются из-за ограничений логической репликации.По умолчанию отключено.
multimaster.cluster_name
Имя кластера. Если вы определяете эту переменную, настраивая кластер,
multimaster
требует, чтобы это имя было одинаковым на всех узлах кластера.multimaster.break_connection
Разрывать соединения клиентов, подключённых к узлу, при отключении данного узла от кластера. Если этот параметр равен
false
, клиенты остаются подключёнными к узлу, но получают ошибку с сообщением о том, что узел оказался в меньшинстве.По умолчанию:
false
multimaster.major_node
Узел с этим флагом продолжает работать даже в отсутствие кворума. Это может потребоваться для нарушения симметрии в кластере с двумя узлами или для быстрого восстановления одного узла в разрушенном кластере.
Важно
Этот параметр следует использовать с осторожностью во избежание проблем «раздвоения» кластера:
Не используйте параметр
major_node
в кластерах с более чем двумя узлами. Если главный узел окажется изолированным при сбое сети, тогда как большинство узлов останутся связанными, работать продолжит и главный узел, и подмножество узлов, образующее кворум. Вместо главного узла в таких кластерах следует использовать отдельный рефери.Ни при каких условиях не назначайте главным более одного узла в кластере.
multimaster.referee_connstring
Строка подключения для обращения к узлу-рефери. Если вы используете рефери, этот параметр нужно задать на всех узлах кластера.
multimaster.max_workers
Максимальное число рабочих процессов
walreceiver
на этом сервере.Важно
Манипулируя этим параметром, проявляйте осторожность. Если число одновременных транзакций во всём кластере превышает заданное значение, это может приводить к необнаруживаемым взаимоблокировкам.
multimaster.trans_spill_threshold
Максимальный размер транзакции, в килобайтах. При достижении этого предела транзакция записывается на диск.
По умолчанию: 100 МБ
multimaster.monotonic_sequences
Определяет режим генерирования последовательностей для уникальных идентификаторов. Эта переменная может принимать следующие значения:
false
(по умолчанию) — идентификаторы на каждом узле генерируются, начиная с номера узла, и увеличиваются на число узлов. Например, в кластере с тремя узлами идентификаторы 1, 4 и 7 выделяются для объектов, создаваемых на первом узле, а 2, 5 и 8 резервируются для второго узла. Если число узлов в кластере изменяется, величина прироста идентификаторов корректируется соответственно.true
— генерируемая последовательность увеличивается монотонно во всём кластере. Идентификаторы узлов на каждом узле генерируются, начиная с номера узла, и увеличиваются на число узлов, но если очередное значение меньше идентификатора, уже сгенерированного на другом узле, оно пропускается. Например, в кластере с тремя узлами, если идентификаторы 1, 4 и 7 уже выделены на первом узле, идентификаторы 2 и 5 будут пропущены на втором. В этом случае первым идентификатором на втором узле будет 8. Таким образом следующий сгенерированный идентификатор всегда больше предыдущего, вне зависимости от узла кластера.
По умолчанию:
false
multimaster.remote_functions
Содержит разделённый запятыми список имён функций, которые должны выполняться удалённо на всех узлах кластера вместо выполнения на одном и последующий репликации результатов.
F.31.5.2. Функции
-
mtm.get_nodes_state()
Показывает состояние всех узлов в кластере. Возвращает кортеж со следующими значениями:
id
,integer
Идентификатор узла.
enabled
,boolean
Показывает, не был ли узел исключён из кластера. Узел может быть отключён, если он не откликается на контрольные обращения в течение интервала
heartbeat_recv_timeout
. Когда узел начинает отвечать на контрольные обращения,multimaster
может автоматически восстановить узел и вернуть его в активное состояние. Автоматическое восстановление узла возможно только если сохраняется его слот репликации. В противном случае вы можете восстановить узел вручную.connected
,boolean
Показывает, подключён ли узел к процессу, передающему WAL.
slot_active
,boolean
Показывает, активен ли слот репликации для данного узла. Для отключённого узла слот остаётся активным до достижения значения
max_recovery_lag
.stopped
,boolean
Показывает, была ли остановлена репликация с этим узлом функцией
mtm.stop_node()
. Остановленный узел находится в том же состоянии, что и отключённый, но он не восстанавливается автоматически. Чтобы повторно активизировать такой узел, нужно вызватьmtm.recover_node()
.catchUp
,boolean
В процессе восстановления узла показывает, были ли данные восстановлены до значения
min_recovery_lag
.slotLag
,bigint
Размер данных WAL, которые этот слот репликации сохраняет для отключённого/остановленного узла. Этот слот будет удалён, когда
slotLag
достигнет значенияmax_recovery_lag
.avgTransDelay
,bigint
Средняя задержка фиксации, вызванная этим узлом, в микросекундах.
lastStatusChange
,timestamp
Время, когда этот узел последний раз менял своё состояние (включён/отключён).
oldestSnapshot
,bigint
Старейший глобальный снимок, существующий на этом узле.
SenderPid
,integer
Идентификатор процесса, передающего WAL.
SenderStartTime
,timestamp
Время запуска передатчика WAL.
ReceiverPid
,integer
Идентификатор процесса, принимающего WAL.
ReceiverStartTime
,timestamp
Время запуска приёмника WAL.
connStr
,text
Строка подключения к этому узлу.
connectivityMask
,bigint
Битовая маска, представляющая соединения с соседними узлами. Каждый бит представляет соединение с узлом.
nHeartbeats
,integer
Число откликов, полученных от этого узла.
-
mtm.collect_cluster_info()
Собирает данные, возвращаемые функцией
mtm.get_cluster_state()
, со всех доступных узлов. Чтобы эта функция работала, помимо соединений для репликации вpg_hba.conf
должны быть разрешены обычные соединения с узлом с заданной строкой подключения.-
mtm.get_cluster_state()
Показывает состояние расширения
multimaster
. Возвращает кортеж со следующими значениями:id
,integer
Идентификатор узла.
status
,text
Состояние узла. Возможные значения:
Initialization
(инициализация),Offline
(недоступен),Connected
(подключён),Online
(работает),Recovery
(восстановление),Recovered
(восстановлен),InMinor
(в меньшинстве),OutOfService
(не работает).disabledNodeMask
,bigint
Битовая маска отключённых узлов.
disconnectedNodeMask
,bigint
Битовая маска недоступных узлов.
catchUpNodeMask
,bigint
Битовая маска узлов, завершивших восстановление.
liveNodes
,integer
Число включённых узлов.
allNodes
,integer
Число узлов в кластере. Количество активных узлов, составляющих большинство, вычисляется, исходя из этого значения.
nActiveQueries
,integer
Число запросов, в настоящее время выполняющихся на этом узле.
nPendingQueries
,integer
Число запросов, ожидающих выполнения на этом узле.
queueSize
,bigint
Размер очереди ожидающих запросов, в байтах.
transCount
,bigint
Общее число реплицированных транзакций, обработанных этим узлом.
timeShift
,bigint
Глобальный сдвиг снимка, вызванный рассинхронизацией часов на узлах, в микросекундах.
recoverySlot
,integer
Узел, с которого отказавший узел получает обновления данных во время автоматического восстановления.
xidHashSize
,bigint
Размер хеша xid2state.
gidHashSize
,bigint
Размер хеша gid2state.
oldestXid
,bigint
Идентификатор старейшей транзакции на этом узле.
configChanges
,integer
Число изменений состояния (включено/отключено) с момента последнего перезапуска.
stalledNodeMask
,bigint
Битовая маска узлов, для которых были удалены слоты репликации.
stoppedNodeMask
,bigint
Битовая маска узлов, остановленных функцией
mtm.stop_node()
.lastStatusChange
,timestamp
Время последнего изменения состояния.
-
mtm.add_node(
connstr
text
) Добавляет новый узел в кластер.
Аргументы:
connstr
— строка подключения для нового узла. Например, для базы данныхmydb
, пользователяmyuser
и нового узлаnode4
строка подключения будет такой:"dbname=mydb user=myuser host=node4"
.Тип:
text
-
mtm.alter_sequences()
Исправляет уникальные идентификаторы на всех узлах кластера. Это может потребоваться после восстановления всех узлов из одной базовой копии.
-
mtm.stop_node(
node
integer
,drop_slot
bool
default false) Исключает узел из кластера.
Аргументы:
node
— идентификатор узла, который будет удалён (этот идентификатор задавался в переменнойmultimaster.node_id
).Тип:
integer
drop_slot
— Необязательный параметр. Определяет, должен ли вместе с узлом удаляться слот репликации. Передайте в этом параметреtrue
, если вы не планируете восстанавливать этот узел в будущем.Тип:
boolean
По умолчанию:
false
-
mtm.recover_node(
node
integer
) Создаёт слот репликации для узла, который ранее был удалён вместе со своим слотом.
Аргументы:
node
— идентификатор узла, который нужно восстановить.
-
mtm.make_table_local(
'имя_таблицы'
regclass
) Останавливает репликацию для указанной таблицы.
Аргументы:
table_name
— имя таблицы, которую вы хотите исключить из схемы репликации.Тип:
regclass
-
mtm.copy_table(
table_name
regclass
,node_id
integer
) Копирует существующую таблицу на другой узел. Вы можете использовать эту функцию для восстановления повреждённых данных на одном или нескольких узлах кластера. Эту функцию нужно вызывать на узле, с которого копируется таблица.
Аргументы:
table_name
— имя таблицы, которую вы хотите копировать.Тип:
regclass
node_id
— идентификатор узла, на который будет копироваться таблица.Тип:
integer
-
mtm.broadcast_table(
table_name
regclass
) Копирует существующую таблицу на все работающие узлы кластера. Вы можете использовать эту функцию для восстановления повреждённых данных. Эту функцию нужно вызывать на узле, с которого копируется таблица.
Аргументы:
table_name
— имя таблицы, которую вы хотите копировать.Тип:
regclass
F.31.6. Совместимость
Расширение multimaster
в настоящее время проходит почти все регрессионные тесты PostgreSQL, за исключением нескольких тестов особых случаев, связанных с работой временных таблиц и обновления типа enum
, что не всегда выполняется в PostgreSQL под контролем транзакций. Прямо сейчас мы продолжаем работу с тем, чтобы обеспечить полную совместимость со стандартным PostgreSQL.
F.31.7. Авторы
Postgres Professional, Москва, Россия.
F.31.7.1. Благодарности
Механизм репликации основан на логическом декодировании и предыдущей версии расширения pglogical
, которым поделилась с сообществом команда 2ndQuadrant.
Протокол трёхфазной фиксации транзакций E3PC основан на следующих работах:
Idit Keidar, Danny Dolev. Increasing the Resilience of Distributed and Replicated Database Systems.
Tim Kempster, Colin Stirling, Peter Thanisch. A More Committed Quorum-Based Three Phase Commit Protocol.