Patroni повысил реплику, но прежний primary ещё отвечает. Для HAProxy оба узла доступны по сети. Если проверять только порт PostgreSQL, новые соединения 1С могут попасть в две расходящиеся ветви базы.

Безопасное правило строже: HAProxy открывает запись, только когда подтверждён ровно один primary. Если primary нет или проверка нашла два, новые соединения останавливаются. Репозиторий gplv2/haproxy-postgresql применяет именно такое поведение.

Задача HAProxy здесь узкая. Он не выбирает лидера и не устраняет split brain внутри PostgreSQL. Он проверяет роли узлов и направляет 1С только на подтверждённый primary.

Что происходит при переключении

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

Соединение проходит четыре этапа:

  1. Клиент 1С обращается к общему адресу HAProxy.
  2. HAProxy опрашивает узлы PostgreSQL или Patroni.
  3. Проверка подтверждает роль каждого узла.
  4. HAProxy передаёт соединение единственному primary.

Узлы не отправляют HAProxy уведомление о смене роли. Проверки запускает сам балансировщик. Поэтому результат зависит от выбранного способа проверки и реакции на неоднозначное состояние.

Состояние кластераЧто видит проверкаРешение HAProxyЧто получает 1С
Подтверждён один primaryОдин узел допущен к записи, остальные работают как standbyНовые соединения идут на primaryОбычное подключение через прежний адрес
Primary не найденНи один узел не прошёл проверку ролиМаршрут записи закрытОшибку подключения вместо записи на реплику
Найдены два primaryДва узла сообщают допустимую рольВсе новые соединения закрытыОстановку подключения вместо записи в две ветви

Последняя строка описывает логику gplv2/haproxy-postgresql. Сам HAProxy без дополнительной проверки не считает число primary и не блокирует маршрут автоматически.

Проверка TCP-порта подтверждает только доступность процесса. Для маршрута записи нужна проверка роли.

HAProxy не заменяет Patroni и etcd

Patroni управляет экземпляром PostgreSQL на каждом узле. Решение о лидере он согласует через распределённое хранилище, чаще всего etcd.

HAProxy работает позже. Он получает результат выбора роли и решает, куда отправить клиентское соединение. Если лидер выбран неверно, балансировщик не исправит кластер.

Запрос select pg_is_in_recovery() тоже не доказывает наличие кворума. Он отвечает на более узкий вопрос: находится ли конкретный экземпляр в режиме восстановления.

На primary функция возвращает false, на standby — true. Этого хватает для фильтрации маршрута, но не для выбора нового лидера.

Так разделяются зоны ответственности:

Если все четыре части смешать в одной проверке, отказ одного компонента станет неотличим от смены роли. Диагностика после переключения займёт больше времени.

С Patroni роль проверяют через REST API

Patroni публикует состояние узла через REST API. HAProxy может опрашивать этот адрес и допускать сервер только при нужном ответе.

Адрес REST API не обязан совпадать с адресом PostgreSQL. В конфигурации HAProxy их нужно задать раздельно, иначе проверка уйдёт не на тот интерфейс.

Разбор способов проверки HAProxy на Forpes отдельно требует init-state=down. С этим параметром сервер начинает работу в состоянии down и ждёт успешной проверки. Без него узел может кратко получить трафик до первого подтверждения роли.

Проверка Patroni удобна, пока REST API доступен и отражает рабочее состояние кластера. Но она видит Patroni, а не весь маршрут клиентского подключения.

Если между HAProxy и PostgreSQL стоит PgBouncer, успешный ответ Patroni не проверяет сам пулер. Patroni может быть доступен, пока PgBouncer не принимает соединения 1С.

Во время паузы Patroni нужен другой способ проверки

Patroni умеет переходить в режим паузы для обновления и переконфигурирования. В этом режиме его REST API нельзя считать рабочим способом определения роли для HAProxy.

Разбор настройки HAProxy на Forpes предлагает два запасных варианта:

Оба способа проверяют состояние самого экземпляра. Для прямого запроса HAProxy нужен клиент psql и отдельная учётная запись PostgreSQL.

Права этой записи следует ограничить проверочным запросом. Подключать HAProxy под владельцем базы или учётной записью 1С для такой задачи не нужно.

ПроверкаЧто подтверждаетКогда выбиратьЧто она пропустит
Patroni REST APIPatroni видит узел в нужной ролиPatroni управляет переключением и не поставлен на паузуОтказ PgBouncer и ошибки клиентского маршрута к PostgreSQL
in_hot_standby или pg_is_in_recovery()Сам PostgreSQL работает как primary либо standbyPatroni не используется или временно поставлен на паузуОтказ промежуточного PgBouncer
External check через PgBouncerЗапрос проходит по тому же пути, что клиентское соединениеPgBouncer установлен на узлах базы и участвует в маршруте 1ССостояние компонентов, которые не входят в проверочный путь

Последний вариант описан в практическом руководстве Dinc Osman. HAProxy запускает внешний скрипт, а тот обращается к PostgreSQL через PgBouncer.

Проверяйте тот маршрут, которым пользуется 1С. Доступный Patroni ещё не доказывает, что соединение через PgBouncer работает.

Правило «ровно один primary» закрывает неоднозначный маршрут

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

Рабочая логика выглядит так:

Такую схему применяет последняя версия gplv2/haproxy-postgresql. Встроенный ACL проекта блокирует соединения, если число обнаруженных primary отличается от одного.

Это свойство конкретного проекта, а не стандартное поведение любого backend в HAProxy. При собственной конфигурации ту же логику придётся задать отдельно.

Cloudflare тоже использует HAProxy для защиты от записи между двумя primary. В опубликованном разборе их архитектуры балансировщик проверяет состояние узлов PostgreSQL и не направляет запись в обе ветви.

Старое соединение нужно завершить после потери роли

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

Если оставить эту сессию, клиент продолжит обращаться к прежнему серверу. Новые соединения уже пойдут на новый primary, а старые останутся на потерявшем роль узле.

Параметр on-marked-down=shutdown-sessions завершает соединения после перевода сервера в состояние down. Его рекомендует разбор настройки HAProxy на Forpes.

После разрыва 1С должна открыть новое соединение через ту же точку входа. HAProxy передаст его текущему primary.

Бесшовная перезагрузка конфигурации решает другую задачу. Она сохраняет старые сессии при чтении нового haproxy.cfg. При смене роли судьбу соединений задаёт on-marked-down=shutdown-sessions.

РискОшибочная настройкаЗащитное правило
Запись попадает на standbyHAProxy проверяет только открытый порт PostgreSQLПроверять роль экземпляра перед допуском в маршрут
Запись расходится между двумя primaryКаждый узел с ролью primary независимо получает состояние upОткрывать маршрут только при одном подтверждённом primary
Клиент остаётся на прежнем узлеПосле down HAProxy сохраняет действующие сессииНастроить on-marked-down=shutdown-sessions
Обслуживание Patroni закрывает все проверкиHAProxy зависит только от Patroni REST APIНа время паузы применять прямую проверку PostgreSQL
PgBouncer сломан, но узел считается доступнымПроверка обращается только к PatroniПроверять роль через клиентский маршрут с PgBouncer

HAProxy снижает риск записи в две ветви только при проверке роли и запрете неоднозначного маршрута.

Как выбрать способ проверки

Если 1С подключается к HAProxy напрямую, а затем к PostgreSQL, начинайте с Patroni REST API. Такой вариант проще связать с ролями, которыми управляет Patroni.

Заранее подготовьте прямую проверку PostgreSQL для обслуживания Patroni. Не меняйте способ проверки впервые во время обновления: сначала испытайте его на стенде.

Если между HAProxy и PostgreSQL работает PgBouncer, проверка должна проходить через него. Иначе вы контролируете состояние базы, но оставляете пулер вне проверки.

External check сложнее встроенного HTTP-запроса. HAProxy понадобятся psql, отдельный пользователь и исполняемый проверочный скрипт. Зато результат относится к полному клиентскому пути.

Выбор сводится не к удобству конфигурации, а к границе отказа. Проверка должна охватывать компоненты между точкой входа 1С и PostgreSQL.

Испытайте переключение по полному пути

Проверка отдельных команд не подтверждает доступность 1С. Испытание должно начинаться клиентским подключением и заканчиваться прикладной записью.

Сначала зафиксируйте штатное состояние. HAProxy должен видеть один primary, а новое соединение 1С — попадать на него.

Затем отключите текущий primary способом, предусмотренным вашим регламентом. Patroni должен повысить реплику, после чего HAProxy перестанет выдавать прежний узел.

Откройте новое соединение 1С через прежний адрес. Выполните контролируемую запись и проверьте, что она появилась на новом primary.

Верните старый узел. Он должен присоединиться как standby и не получать соединения для записи.

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

Резервирование PostgreSQL не закрывает отказ серверов приложений. Эту границу и порядок переключения ragent, rmngr и rphost описывает архитектура автоматического failover уровня 1С.

Отдельно смоделируйте два primary

Обычный failover проверяет переход от одного primary к другому. Он не показывает поведение HAProxy при неоднозначной роли.

На испытательном контуре подайте проверке два ответа primary. Новые соединения к маршруту записи должны прекратиться.

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

После возврата к единственному primary проверьте восстановление маршрута. Новые соединения должны снова попадать только на допустимый узел.

Точные интервалы проверок и таймауты нельзя выбрать без требований к простою. На них также влияют параметры Patroni и время повышения реплики.

Сначала задайте допустимое время недоступности 1С. Затем согласуйте интервалы Patroni, HAProxy и клиента так, чтобы они не спорили друг с другом.

Протокол приёмки

Конфигурацию можно принимать, если выполняются пять условий:

  1. В штатном состоянии HAProxy допускает один primary.
  2. После failover 1С подключается через прежний адрес.
  3. Standby не получает соединения для записи.
  4. Узел, потерявший роль, лишается старых клиентских сессий.
  5. При нуле или двух primary новые соединения закрываются.

Главное правило помещается в одну строку: маршрут записи открыт только при одном подтверждённом primary. Все остальные состояния должны завершаться отказом подключения, а не догадкой балансировщика.

haproxy patroni postgresql split brain отказоустойчивость