Реплика PostgreSQL для Directum RX: что она даёт, чем не является и как переключаться без сюрпризов
Потоковая реплика PostgreSQL под базой Directum RX. Асинхронная и синхронная репликация, слоты и диск, отставание и как его мерить, конфликты запросов на реплике, ручное и автоматическое переключение, возврат старого мастера через pg_rewind, переключение сервисов RX на новый адрес. И почему реплика не заменяет резервную копию.
Коротко. Реплика даёт копию на секунды позади и быстрое ручное переключение, но не защищает от ошибок и не переключается сама. Слот плюс
max_slot_wal_keep_size, мониторинг в байтах,wal_log_hintsдляpg_rewind, резервные копии отдельно, и переключение отрепетировано на тестовом стенде вместе с RX.
Реплику базы Directum RX обычно поднимают с формулировкой «для отказоустойчивости». Через год выясняется, что реплика отстала на неделю и никто не заметил, переключаться на неё никто не репетировал, а ошибочно удалённые данные исчезли и с реплики тоже, потому что она честно повторила удаление. Разбираем, что реплика PostgreSQL действительно даёт, где её границы и что подготовить заранее, чтобы переключение прошло спокойно.
Симптом
- На ведущем сервере кончается место: каталог
pg_walрастёт из-за слота отставшей или выключенной реплики. - Отчёт или выгрузка на реплике обрываются с
canceling statement due to conflict with recovery. - Мониторинг показывает огромное отставание реплики ночью, хотя она в порядке.
- При аварии мастера переключились на реплику, а потом старый мастер поднялся сам, и часть сервисов писала в него.
- После потери данных по ошибке оператора реплику пытаются использовать как резервную копию, но удаление на ней уже применено.
Что реплика даёт и чего не даёт
Потоковая реплика получает журнал изменений (WAL) от ведущего сервера и применяет его у себя. Она всё время находится в режиме восстановления и принимает только чтение.
| Даёт | Не даёт |
|---|---|
| Копию базы на другом сервере, отстающую на секунды | Защиту от ошибок: DELETE и DROP применяются на реплике так же быстро |
| Возможность переключиться на неё за минуты при потере мастера | Автоматическое переключение: для него нужен отдельный менеджер кластера |
| Место для тяжёлых запросов на чтение: отчёты, выгрузки, BI | Разгрузку самой RX: её сервисы подключены к ведущему серверу |
| Источник для резервного копирования без нагрузки на мастер | Резервную копию: нет истории, нельзя вернуться на вчера |
Поэтому реплика дополняет резервное копирование и не заменяет его. Восстановление на момент до ошибки даёт только резервная копия с архивом WAL. Как проверять, что такая копия у вас есть, рассказано в статье про бэкап RX на PostgreSQL.
Асинхронная или синхронная
По умолчанию репликация асинхронная: мастер подтверждает транзакцию, не дожидаясь реплики. При внезапной потере мастера последние секунды транзакций могут не дойти до реплики.
Синхронная репликация (synchronous_standby_names и synchronous_commit) ждёт подтверждения от реплики.
Потерь при отказе мастера нет, но каждая запись ждёт сеть, а если единственная синхронная реплика недоступна,
мастер перестаёт подтверждать записи, и RX встаёт. Синхронную схему делают с двумя репликами и правилом
«любая одна из двух» (ANY 1 (replica1, replica2)), тогда выключение одной реплики не останавливает систему.
Для большинства инсталляций RX хватает асинхронной реплики плюс архив WAL: потеря нескольких секунд при аварии мастера допустима, а лишняя задержка на каждой записи заметна пользователям.
Слоты репликации и диск
Слот репликации гарантирует, что мастер не удалит WAL, который реплика ещё не получила. Без слота отставшая реплика может обнаружить, что нужный ей сегмент уже удалён, и её придётся пересоздавать (разбор ошибки).
Обратная сторона: если реплика выключена или сломана, слот держит WAL бесконечно, и каталог pg_wal на мастере
растёт до заполнения диска. Мастер с полным диском останавливается. Защита:
max_slot_wal_keep_size(PostgreSQL 13 и новее) ограничивает, сколько WAL может удерживать слот. Превысивший слот становится недействительным, реплику придётся пересоздать, но мастер выживает.- Мониторинг удерживаемого объёма по каждому слоту:
SELECT slot_name, active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots;
- Удаление слотов реплик, которые больше не нужны:
SELECT pg_drop_replication_slot('имя');.
Подробнее о том, почему растёт pg_wal, в статье про архив и слоты.
Как мерить отставание
На мастере pg_stat_replication показывает каждую реплику и три задержки: запись, сброс на диск и применение.
SELECT application_name, client_addr, state, sync_state,
write_lag, flush_lag, replay_lag,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS replay_bytes
FROM pg_stat_replication;
Если реплики нет в этом списке, она не подключена, и это авария, даже если отставание никто не меряет.
На реплике часто смотрят время последней применённой транзакции: now() - pg_last_xact_replay_timestamp().
У этой метрики известная ловушка: ночью, когда в базу никто не пишет, применять нечего, и «отставание» растёт
на часы при полностью исправной реплике. Надёжнее мерить в байтах на мастере или сравнивать
pg_last_wal_receive_lsn() и pg_last_wal_replay_lsn() на самой реплике.
Функции мастера на реплике не работают, и запрос мониторинга, который их вызывает, получает
recovery is in progress. Запросы мониторинга
должны проверять роль сервера через pg_is_in_recovery().
Запросы на реплике
Долгий запрос на реплике может мешать применению WAL: например, мастер вычистил строки, которые запрос
ещё читает. PostgreSQL ждёт max_standby_streaming_delay (по умолчанию 30 секунд) и отменяет запрос с
canceling statement due to conflict with recovery.
| Настройка | Эффект | Цена |
|---|---|---|
Увеличить max_standby_streaming_delay |
запросы реже отменяются | реплика отстаёт на время долгих запросов |
hot_standby_feedback = on |
мастер не вычищает строки, нужные запросам реплики | раздувание таблиц на мастере при долгих запросах |
Для реплики, на которой гоняют отчёты, обычно увеличивают задержку и держат отдельную реплику для переключения, где долгих запросов нет. Совмещать роли можно, но тогда реплика для переключения может оказаться отставшей ровно в момент аварии.
Переключение
Вручную
- Убедиться, что мастер действительно недоступен и не вернётся сам. Если он может подняться, его нужно остановить и не дать запуститься: два сервера, принимающих запись, хуже, чем простой.
- Продвинуть реплику:
SELECT pg_promote();(PostgreSQL 12 и новее) илиpg_ctl promote. - Переключить клиентов на новый адрес.
- Проверить, что новый мастер принимает запись:
SELECT pg_is_in_recovery();должно вернутьf.
Как RX узнает новый адрес
Варианты, от простого к надёжному:
- Поменять адрес в строке подключения и перезапустить сервисы. Работает всегда, но это ручной шаг в момент аварии.
- Общее имя в DNS или виртуальный адрес, который переносится на новый мастер. Строка подключения не меняется, сервисы переподключаются сами после обрыва соединений (в журналах в этот момент будут 57P01 и 57P03).
- Несколько адресов в строке подключения. Npgsql умеет перебирать адреса и выбирать ведущий сервер
(
Host=db01,db02;Target Session Attributes=primary). Поддерживает ли это ваша версия RX без побочных эффектов, проверяйте на тестовом стенде. Keycloak и другие Java-приложения делают то же черезtargetServerType=primary, и когда ведущего нет, пишут Could not find a server with specified targetServerType.
Автоматически
Автоматическое переключение делает менеджер кластера: Patroni с хранилищем состояния (etcd, Consul) и виртуальным адресом или прокси, у Postgres Pro Enterprise есть встроенный механизм BiHA. Менеджер решает, кто мастер, переносит адрес и не даёт старому мастеру принимать запись. Это заметно сложнее в сопровождении, и без опыта эксплуатации кластер с автоматическим переключением ломается чаще, чем одиночный сервер. Для многих инсталляций RX надёжнее ручное переключение по отрепетированной инструкции.
Возврат старого мастера
Старый мастер после переключения нельзя просто запустить как реплику: у него могут быть транзакции, которых
нет на новом. pg_rewind откатывает его до точки расхождения и подключает к новому мастеру без полного
копирования. Для этого заранее должны быть включены wal_log_hints = on или контрольные суммы страниц.
Если ни того, ни другого нет, старый мастер пересоздаётся с нуля через pg_basebackup.
Что сделать
- Реплика со слотом и
max_slot_wal_keep_size, мониторинг отставания в байтах и наличия реплики вpg_stat_replication. - Резервное копирование и архив WAL отдельно от реплики. Копию можно снимать с реплики, чтобы не нагружать мастер:
pg_probackupи другие инструменты это умеют. wal_log_hints = onна обоих серверах заранее, чтобы после переключения работалpg_rewind.- Письменная инструкция переключения с проверками и репетиция на тестовом стенде с RX: переключить, проверить вход, создание документа, фоновые задания, вернуть.
- Решение, как RX узнаёт новый адрес: DNS, виртуальный адрес или правка строки подключения.
Чего не делать
- Не считать реплику резервной копией. Ошибки оператора она повторяет.
- Не оставлять слот выключенной реплики. Он заполнит диск мастера.
- Не делать единственную реплику синхронной. Её выключение остановит запись в базу.
- Не мерить отставание только временем последней транзакции. Ночью оно врёт.
- Не продвигать реплику, пока старый мастер может подняться сам.
- Не переключаться впервые во время настоящей аварии.
Похожая картина у вас?
Разберём ваш контур за 30 минут бесплатного созвона: версия RX, состав сервисов, что болит сильнее всего.
Написать