# Режимы репликации

> Асинхронные и синхронные режимы репликации под управлением Patroni.

---

Индекс LLMS: [llms.txt](/ru/llms.txt)

---

<a id="replication_modes"></a>
Patroni использует потоковую репликацию PostgreSQL. Подробнее см. [документацию Postgres](http://www.postgresql.org/docs/current/static/warm-standby.html#STREAMING-REPLICATION). По умолчанию Patroni настраивает PostgreSQL на асинхронную репликацию. Выбор схемы репликации зависит от требований бизнеса. Изучите асинхронную и синхронную репликацию, а также другие решения HA, чтобы выбрать подходящий вариант.

--------

## Долговечность в асинхронном режиме {#asynchronous-mode-durability}

В асинхронном режиме ради доступности кластер может потерять часть зафиксированных транзакций. При отказе или недоступности первичного сервера Patroni автоматически повышает достаточно исправный резервный сервер до первичного. Транзакции, не реплицированные на этот резервный сервер, остаются в «ответвившейся временной шкале» первичного и фактически невосстановимы[^1].

Объём транзакций, которые могут быть потеряны, контролируется параметром `maximum_lag_on_failover`. Поскольку позиция журнала транзакций первичного сервера не измеряется в реальном времени, в худшем случае при переключении теряется `maximum_lag_on_failover` байтов журнала плюс объём, записанный за последние `ttl` секунд (в среднем за `loop_wait`/2 секунд). Однако типичная задержка репликации в стабильном состоянии значительно меньше секунды.

По умолчанию при выборах лидера Patroni не учитывает текущую временную шкалу реплик, что иногда нежелательно. Чтобы узел с временной шкалой, отличной от прежнего первичного сервера, не стал новым лидером, задайте параметру `check_timeline` значение `true`.

--------

## Синхронная репликация PostgreSQL {#postgresql-synchronous-replication}

С Patroni можно использовать [синхронную репликацию](http://www.postgresql.org/docs/current/static/warm-standby.html#SYNCHRONOUS-REPLICATION) Postgres. Она обеспечивает согласованность кластера, подтверждая запись на вторичный сервер до возврата успешного результата подключённому клиенту. Цена синхронной репликации — повышенная задержка и сниженная пропускная способность записи, полностью зависящая от производительности сети.

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

Для простого теста синхронной репликации добавьте следующие строки в раздел `parameters` файлов конфигурации YAML:

```yaml
synchronous_commit: "on"
synchronous_standby_names: "*"
```

При синхронной репликации PostgreSQL используйте не менее трёх узлов данных Postgres, чтобы сохранить доступность записи при отказе одного узла.

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

<a id="synchronous_mode"></a>

--------

## Синхронный режим {#synchronous-mode}

Для сценариев, где потеря зафиксированных транзакций недопустима, включите [synchronous_mode](/ru/docs/patroni/replication_modes#synchronous_mode) Patroni. При включённом [synchronous_mode](/ru/docs/patroni/replication_modes#synchronous_mode) Patroni не повышает резервный сервер, пока не убедится, что тот содержит все транзакции, для которых клиент мог получить успешный статус фиксации[^2]. Поэтому система может быть недоступна для записи, хотя часть серверов работает. Системные администраторы всё ещё могут вручную переключить резервный сервер при отказе, даже если это приведёт к потере транзакций.

Включение [synchronous_mode](/ru/docs/patroni/replication_modes#synchronous_mode) не гарантирует долговечность фиксаций на нескольких узлах при любых обстоятельствах. Если подходящего резервного сервера нет, первичный всё равно принимает записи, но не гарантирует их репликацию. При отказе первичного сервера в этом режиме резервный сервер не повышается. Когда прежний первичный узел возвращается, он повышается автоматически, если системный администратор не выполнил ручное переключение. Благодаря этому синхронный режим применим в кластерах из 2 узлов.

Если при включённом [synchronous_mode](/ru/docs/patroni/replication_modes#synchronous_mode) резервный сервер отказывает, фиксации блокируются до следующей итерации Patroni, которая переводит первичный сервер в автономный режим (задержка записи в худшем случае — `ttl` секунд, в среднем — `loop_wait`/2 секунд). Ручная остановка или перезапуск резервного сервера не прерывает службу фиксации: до остановки PostgreSQL резервный сервер сообщает первичному, что освобождается от обязанностей синхронной реплики.

Если необходимо гарантировать долговечное хранение каждой записи как минимум на двух узлах, в дополнение к [synchronous_mode](/ru/docs/patroni/replication_modes#synchronous_mode) включите `synchronous_mode_strict`. Этот параметр не позволяет Patroni отключать синхронную репликацию на первичном сервере при отсутствии подходящих резервных кандидатов, если только транзакция Postgres явно не отключила `synchronous_commit`; все клиентские запросы записи блокируются до появления хотя бы одной синхронной реплики.

Когда `synchronous_mode_strict` включён и активные соединения репликации не удовлетворяют минимальному коэффициенту репликации, Patroni определяет `synchronous_standby_names` следующим образом:

1.  **Последние известные синхронные узлы доступны в ключе `/sync` DCS**: Patroni задаёт или сохраняет в `synchronous_standby_names` указанные там узлы. Например, если `/sync` содержит `leader=node1, sync_standby=node2,node3` и оба резервных сервера прекращают потоковую передачу, Patroni продолжает использовать:

    ```ini
    synchronous_standby_names = 'node2,node3'
    ```

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

2.  **Ручное переключение на асинхронный узел**: когда повышается узел, которого не было в ключе `/sync`, например через `patronictl failover --force`, значение `synchronous_standby_names` устанавливается равным прежнему первичному серверу, поскольку только он гарантированно содержит последние зафиксированные данные.

3.  **Ключ `/sync` пуст**: например, строгий режим только что включён или кластер недавно инициализирован и ещё не имеет реплик. Patroni задаёт:

    ```ini
    synchronous_standby_names = '__patroni_strict_sync_replica_placeholder__'
    ```

    Это встроенное сторожевое значение, не совпадающее ни с одним реальным именем узла. Оно блокирует все записи, пока подходящая реплика не начнёт потоковую передачу с первичного сервера. Заполнитель заменяет прежний шаблон `*`, который мог непреднамеренно позволить неподходящему узлу выполнить требование синхронизации.

> [!WARNING]
> Значение `__patroni_strict_sync_replica_placeholder__` зарезервировано Patroni и **не должно** использоваться как `name` узла Patroni в `patroni.yaml`. С таким именем Patroni откажется запускаться.

При активном строгом режиме Patroni выдаёт предупреждение журнала: `"No active replication connections and synchronous_mode_strict is requested. Commits will be delayed."` Оно выводится один раз на событие активации, а не при каждой итерации цикла HA.

Чтобы резервный сервер никогда не становился синхронным, задайте тегу `nosync` значение true. Это рекомендуется для резервных серверов за медленными сетевыми соединениями, которые снизили бы производительность в роли синхронной реплики. Тег `nostream`, равный true, даёт тот же эффект.

Синхронный режим можно включать и отключать командой `patronictl edit-config` или через REST-интерфейс Patroni. Инструкции приведены в разделе [динамическая конфигурация](/ru/docs/patroni/config/dynamic#dynamic).

Примечание: из-за реализации синхронной репликации в PostgreSQL транзакции можно потерять даже с `synchronous_mode_strict`. Если бэкенд PostgreSQL отменён в ожидании подтверждения репликации, например из-за отмены пакета при тайм-ауте клиента или отказе бэкенда, изменения транзакции становятся видимыми другим бэкендам. Они ещё не реплицированы и могут быть потеряны при повышении резервного сервера.

--------

## Коэффициент синхронной репликации {#synchronous-replication-factor}

Параметр `synchronous_node_count` управляет количеством синхронных резервных баз данных в Patroni. По умолчанию он равен `1` и не действует, если [synchronous_mode](/ru/docs/patroni/replication_modes#synchronous_mode) равен `off`. При включении Patroni поддерживает точное количество синхронных резервных баз по `synchronous_node_count` и корректирует состояние в DCS и `synchronous_standby_names` PostgreSQL при присоединении и выходе участников. Если значение превышает количество подходящих узлов, Patroni автоматически его уменьшает.

--------

## Максимальное отставание синхронного узла {#maximum-lag-on-synchronous-node}

По умолчанию Patroni сохраняет узлы, объявленные `synchronous` согласно представлению `pg_stat_replication`, даже если другие узлы опережают их. Это уменьшает количество изменений `synchronous_standby_names`. Поведение можно изменить параметром `maximum_lag_on_syncnode`, который определяет допустимое отставание реплики, всё ещё считающейся «синхронной».

Если резервных серверов несколько, Patroni использует максимальный LSN реплики, иначе — текущий LSN wal лидера. По умолчанию значение равно `-1`; при значении `0` или меньше Patroni не заменяет неисправный синхронный резервный сервер. Задайте достаточно высокое значение, чтобы Patroni не менял синхронные реплики слишком часто при большом объёме транзакций.

--------

## Реализация синхронного режима {#synchronous-mode-implementation}

В синхронном режиме Patroni хранит в DCS, в ключе `/sync`, состояние синхронизации с последним первичным сервером и текущими синхронными резервными базами. Состояние обновляется со строгими ограничениями порядка, обеспечивая следующие инварианты:

- Узел должен быть отмечен как последний лидер всякий раз, когда он может принимать транзакции записи. Отказ Patroni или незавершённая остановка PostgreSQL могут нарушить этот инвариант.
- Узел должен быть задан синхронным резервным сервером PostgreSQL, пока он опубликован как синхронный резервный сервер в ключе `/sync` DCS.
- Узел, не являющийся лидером или текущим синхронным резервным сервером, не может автоматически повысить себя.

Patroni назначает в `synchronous_standby_names` один или несколько синхронных резервных узлов только на основе параметра `synchronous_node_count`.

На каждой итерации цикла HA Patroni заново оценивает выбор синхронных резервных узлов. Если узлы текущего списка подключены и не запросили снятие синхронного статуса, список сохраняется. Иначе выбираются доступные для синхронизации участники кластера, сильнее всего продвинувшиеся в репликации.

### Пример: {#example}

#### Ключ `/config` в DCS {#config-key-in-dcs}

```yaml
synchronous_mode: on
synchronous_node_count: 2
...
```

#### Ключ `/sync` в DCS {#sync-key-in-dcs}

```json
{
    "leader": "node0",
    "sync_standby": "node1,node2"
}
```

#### postgresql.conf {#postgresqlconf}

```ini
synchronous_standby_names = 'FIRST 2 (node1,node2)'
```

В приведённых примерах только узлы `node1` и `node2` считаются синхронными и могут быть автоматически повышены при отказе первичного сервера (`node0`).

<a id="quorum_mode"></a>

--------

## Режим фиксации по кворуму {#quorum-commit-mode}

Начиная с PostgreSQL v10 Patroni поддерживает синхронную репликацию на основе кворума.

В этом режиме Patroni хранит в DCS состояние синхронизации с последним известным первичным сервером, количеством узлов для кворума и узлами, имеющими право голоса. В стабильном состоянии голосующие узлы — лидер и все синхронные резервные серверы. Состояние обновляется со строгими ограничениями порядка повышения узлов и `synchronous_standby_names`, чтобы любое способное достичь кворума подмножество голосующих всегда содержало хотя бы один узел с последней успешной фиксацией.

На каждой итерации цикла HA Patroni заново оценивает выбор синхронных резервных серверов и кворум по доступности узлов и запрошенной конфигурации кластера. В версиях PostgreSQL выше 9.6 все подходящие узлы добавляются как синхронные резервные серверы, как только их репликация догоняет лидера.

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

Синхронный режим на основе кворума включается установкой [synchronous_mode](/ru/docs/patroni/replication_modes#synchronous_mode) в `quorum` командой `patronictl edit-config` или через REST-интерфейс Patroni. Инструкции приведены в разделе [динамическая конфигурация](/ru/docs/patroni/config/dynamic#dynamic).

Остальные параметры, включая `synchronous_node_count`, `maximum_lag_on_syncnode` и `synchronous_mode_strict`, работают так же, как при `synchronous_mode=on`.

Если в режиме фиксации по кворуму с `synchronous_mode_strict` нет активных реплик, Patroni задаёт `synchronous_standby_names` как `ANY N (<last known voters>)`, сохраняя последних известных голосующих из `/sync`, либо как `ANY 1 (__patroni_strict_sync_replica_placeholder__)`, если в ключе `/sync` нет голосующих.

### Пример: {#example-1}

#### Ключ `/config` в DCS {#config-key-in-dcs-1}

```yaml
synchronous_mode: quorum
synchronous_node_count: 2
...
```

#### Ключ `/sync` в DCS {#sync-key-in-dcs-1}

```json
{
    "leader": "node0",
    "sync_standby": "node1,node2,node3",
    "quorum": 1
}
```

#### postgresql.conf {#postgresqlconf-1}

```ini
synchronous_standby_names = 'ANY 2 (node1,node2,node3)'
```

При отказе первичного сервера (`node0`) в приведённом примере два узла из `node1`, `node2`, `node3` получат последнюю транзакцию, но неизвестно какие. Чтобы определить, получил ли её `node1`, нужно сравнить его LSN с LSN **как минимум** одного узла (`quorum=1` в ключе `/sync`) из `node2` и `node3`. Если `node1` не отстаёт хотя бы от одного из них, можно гарантировать отсутствие видимой пользователю потери данных при повышении `node1`.

[^1]: Данные всё ещё существуют, но для их извлечения требуется ручная работа специалистов по восстановлению. Если Patroni разрешено перематывать состояние с `use_pg_rewind`, ответвившаяся временная шкала автоматически удаляется, чтобы снова присоединить отказавший первичный сервер к кластеру. Для правильной работы `use_pg_rewind` кластер должен быть инициализирован с `data page checksums` (параметр `--data-checksums` для `initdb`) и/или `wal_log_hints` должен быть равен `on`.

[^2]: Клиенты могут изменять поведение отдельных транзакций параметром PostgreSQL `synchronous_commit`. Транзакции со значениями `synchronous_commit` `off` и `local` могут быть потеряны при переключении, но не блокируются задержкой репликации.

---

Обратные ссылки:

- [Поддержка Citus](/ru/docs/patroni/citus/)
- [Динамическая конфигурация](/ru/docs/patroni/config/dynamic/)
- [YAML Конфигурация](/ru/docs/patroni/config/yaml/)
- [HA-кластер с несколькими центрами обработки данных](/ru/docs/patroni/ha_multi_dc/)
- [Введение](/ru/docs/patroni/readme/)
- [Примечания к выпускам](/ru/docs/patroni/releases/)
- [Patroni REST API](/ru/docs/patroni/rest_api/)
