Настройка дополнительных пулов подключения (Read Pool) Global ERP#

1. Общие положения#

Документ описывает архитектуру и порядок настройки дополнительных пулов подключения (Read Pool) в Global ERP для работы с читающими репликами PostgreSQL.

Вся функциональность разделяется на два логических режима:

  • Синхронные реплики — поддерживаемый и рекомендованный режим.

  • Асинхронные реплики — режим находится в разработке и в текущих версиях системы не поддерживается.

2. Синхронные реплики#

2.1 Назначение и область применения#

PostgreSQL имеет ограниченные возможности вертикального масштабирования, упирающиеся в физические ресурсы сервера (CPU, RAM, дисковая подсистема). Практическая рекомендация при эксплуатации продуктивных систем — средняя загрузка CPU мастер-базы не должна превышать ~50%.

При достижении данного порога дальнейший рост нагрузки рекомендуется обеспечивать за счёт подключения читающих реплик, на которые переносится часть read-нагрузки (вплоть до 100%). Это позволяет:

  • снизить нагрузку на мастер-базу;

  • стабилизировать производительность при росте числа пользователей и отчётов;

  • обеспечить масштабирование без увеличения ресурсов мастер-базы.

Ключевой принцип работы#

В режиме синхронных реплик сервер приложений распределяет все запросы чтения, выполняемые вне активных транзакций.

  • все write-запросы и любые запросы внутри транзакций всегда выполняются на мастер-базе;

  • все read-запросы вне транзакций рассматриваются как равнозначные и распределяются глобально;

  • отсутствует управление конкретными SQL-запросами — невозможно указать, какой именно запрос должен выполняться на мастер-базе или реплике;

  • управление осуществляется только процентным распределением read-нагрузки между пулами.

Это является фундаментальным архитектурным ограничением данного режима.

Планируемые улучшения#

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

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

2.2 Поддерживаемые сценарии и ограничения#

  • Поддерживаются только синхронные реплики PostgreSQL.

  • Асинхронная репликация в данном режиме не используется.

  • Распределение читающих запросов осуществляется глобально на уровне системы, а не на уровне отдельных интерфейсов или SQL-запросов.

Внимание

Каждая подключаемая реплика должна быть синхронной.

2.3 Рекомендации по количеству реплик#

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

Основная причина данной рекомендации — защита мастер-ноды от резкого возврата нагрузки при отказе одной из реплик:

  • при использовании одной реплики значительная часть read-нагрузки уходит с мастера;

  • при отказе этой реплики вся нагрузка мгновенно возвращается на мастер;

  • в условиях высокой загрузки мастера это может привести к деградации или отказу системы.

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

2.4 Типы синхронной репликации PostgreSQL#

PostgreSQL поддерживает несколько режимов синхронной репликации, управляемых параметром synchronous_commit:

  • off — коммит не ожидает подтверждений;

  • local — ожидание записи WAL на диск мастера;

  • on (по умолчанию) — ожидание записи WAL на синхронную реплику;

  • remote_write — ожидание записи WAL в ОС реплики;

  • remote_flush — ожидание fsync WAL на реплике;

  • remote_apply — ожидание применения WAL на реплике.

Для работы читающих реплик в Global ERP критически необходим режим synchronous_commit = remote_apply.

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

2.5 Архитектурные ограничения и риски#

Основной риск — блокировка COMMIT на мастере#

При использовании synchronous_commit = remote_apply подтверждение COMMIT на мастере происходит только после применения WAL на всех синхронных репликах.

Это означает:

  • производительность системы определяется самой медленной репликой;

  • деградация одной реплики напрямую влияет на скорость работы мастера;

  • при блокировке WAL replay на реплике COMMIT-операции на мастере будут ожидать.

Конфликты WAL replay#

Длительные read-запросы на реплике могут конфликтовать с применением WAL (VACUUM, DDL, обновление visibility map).

В этом случае PostgreSQL вынужден:

  • либо задерживать применение WAL;

  • либо отменять конфликтующий read-запрос.

До момента разрешения конфликта COMMIT на мастере может быть заблокирован.

2.6 Управление конфликтами WAL replay#

Для ограничения времени блокировки WAL replay используются параметры:

  • max_standby_streaming_delay — для потоковой репликации;

  • max_standby_archive_delay — для восстановления из архива.

После превышения заданного лимита конфликтующие read-запросы принудительно отменяются.

Отмена read-запросов является штатным и ожидаемым механизмом, направленным на защиту мастер-базы и поддержание реплик в актуальном состоянии.

Пользовательский опыт (UX)#

При автоматической отмене read-запроса пользователь получит понятное и явное сообщение об ошибке, содержащее следующую информацию:

  • операция была прервана автоматически;

  • причина прерывания — поддержание реплики в актуальном состоянии;

  • действие, рекомендуемое пользователю — повторить операцию позже.

Такое поведение является ожидаемым для архитектуры с синхронными репликами и не свидетельствует о сбое системы.

В перспективе рассматривается доработка сервера приложений, позволяющая:

  • автоматически повторять отменённые read-запросы;

  • выполнять повтор либо на той же реплике, либо на мастер-базе;

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

2.7 Параметр hot_standby_feedback#

При использовании читающих реплик параметр hot_standby_feedback должен быть включён.

Назначение параметра:

  • передача информации о snapshot’ах standby на мастер;

  • снижение вероятности конфликтов между VACUUM на мастере и WAL replay на реплике.

Важно понимать, что hot_standby_feedback:

  • не гарантирует, что VACUUM или autovacuum на мастере не будет выполнен;

  • не блокирует уже запущенные процессы VACUUM;

Параметр предназначен для уменьшения частоты возникновения конфликтов между длительными read-запросами на репликах и очисткой старых версий строк (cleanup) на мастере.

Основной эффект использования:

  • VACUUM на мастере может быть отложен до завершения read-запросов на реплике;

  • снижается вероятность отмены read-запросов из-за конфликтов WAL replay.

Как следствие возможен временный рост таблиц и bloat. Требуется мониторинг VACUUM и состояния таблиц.

2.8 Позиция вендора по использованию синхронных реплик#

Вендор осознанно принимает следующие риски:

  • возможный рост таблиц из-за отложенного VACUUM;

  • возможность блокировки COMMIT при длительных read-запросах на репликах.

При этом:

  • отмена read-запросов с помощью max_standby_streaming_delay считается штатным механизмом;

  • длительные аналитические запросы на активно изменяемых таблицах требуют отдельной настройки и должны выполняться на мастер-базе, без использования читающих реплик;

  • режим признаётся пригодным для продуктивных систем при корректной эксплуатации.

2.9 Архитектура приложения и распределение нагрузки#

Сервер приложений не выполняет функции балансировки и отказоустойчивости.

Распределение нагрузки:

  • write-запросы → WritePool;

  • read-запросы вне транзакций → ReadPool.

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

Вендором рекомендуется использование HAProxy в качестве балансировщика нагрузки.

Текстовая схема:

Application Server
  |
  |-- WritePool --> Master DB
  |
  |-- ReadPool --> Load Balancer (HAProxy)
                    |
                    |-- Replica 1
                    |-- Replica 2
                    |-- Master (резерв)

Балансировщик нагрузки выполняет:

  • распределение read-нагрузки между репликами;

  • автоматическое исключение деградировавших или недоступных реплик;

  • переключение на мастер-ноду в случае отказа всех реплик.

При отказе реплики балансировщик автоматически исключает её из распределения нагрузки без изменения конфигурации приложения.

3. Асинхронные реплики (в разработке)#

Поддержка асинхронных реплик находится в разработке и в текущих версиях Global ERP не поддерживается.

Планируемая архитектура:

  • асинхронных реплик может быть произвольное количество;

  • сервер приложений никогда не использует их автоматически;

  • выбор реплики будет осуществляться на прикладном уровне (интерфейсы, отчёты, печатные формы);

  • управление запросами будет выполняться на уровне бизнес-логики, а не SQL.

Как и в случае синхронных реплик, для асинхронного режима требуется внешний балансировщик нагрузки:

  • балансировщик скрывает конкретные узлы БД от сервера приложений;

  • обеспечивает отказоустойчивость;

  • содержит резервную мастер-ноду для fallback-сценариев.

Использование балансировщика нагрузки является обязательным элементом архитектуры асинхронных реплик.

4. Настройка подключения#

4.1 Настройка Standalone#

Для подключения читающих реплик в режиме standalone используется extraConnectionPools.

В файле global3config.xml необходимо заполнить секцию <databases>.

Пример конфигурации:

<databases>
        <database alias="<db_alias>" driver="org.postgresql.Driver" schema="PUBLIC"
                connectionType="proxyShared" authenticationType="btk"
                maxActiveThreadCount="1000"
                activeThreadTimeout="20">

                <users>
                        <user name="username" password="password"/>
                </users>

                <extraConnectionPools>
                        <!-- WritePool - мастер-база -->
                        <pool name="WritePool" schema="PUBLIC"
                                url="jdbc:postgresql://<host>:<port>/<dbname>"
                                acceptPrimary="true"
                                acceptSecondary="true"
                                acceptTxSession="true"
                                acceptNoTxSession="false"
                                priority="1"
                                minPoolSize="100"
                                maxPoolSize="110"
                                initialPoolSize="2"
                                inactiveConnectionTimeout="30"
                                poolTimeout="10"
                                timeBetweenEvictionRunsMillis="5000"
                                usageRatio="1" />

                        <!-- ReadPool - внешний балансировщик между репликами -->
                        <pool name="ReadPool" schema="PUBLIC"
                                url="jdbc:postgresql://<host>:<port>/<dbname>"
                                acceptPrimary="true"
                                acceptSecondary="true"
                                acceptTxSession="false"
                                acceptNoTxSession="true"
                                priority="100"
                                minPoolSize="100"
                                maxPoolSize="110"
                                initialPoolSize="2"
                                inactiveConnectionTimeout="30"
                                poolTimeout="2"
                                timeBetweenEvictionRunsMillis="5000"
                                usageRatio="1" />
                </extraConnectionPools>
        </database>
</databases>

4.2 Описание параметров пулов подключения#

Используемые параметры пула (Tomcat JDBC Pool) интерпретируются следующим образом:

  • minPoolSize — минимальное количество свободных соединений;

  • maxPoolSize — максимальное количество соединений в пуле;

  • initialPoolSize — количество соединений при старте сервера;

  • usageRatio — относительный вес пула при распределении нагрузки (является константой и не подлежит изменению);

  • acceptTxSession — принимает соединения внутри транзакций;

  • acceptNoTxSession — принимает соединения вне транзакций;

  • poolTimeout — время ожидания свободного соединения;

  • inactiveConnectionTimeout — таймаут неактивного соединения.

Подробная документация по параметрам пула доступна по ссылке: https://help.globalerp.ru/books/gs-docs-sphinx/master/reference/configuration/global3_config_xsd/configuration/databases/database/extraconnectionpools/Pool.html

4.3 Режим высокой доступности#

Для добавления дополнительных пулов подключений в режиме высокой доступности необходимо:

  • внести изменения в global3config.xml в архиве profile.zip по пути: globalserver/template/config/global3.config.xml;

  • использовать конфигурацию, аналогичную режиму standalone;

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

5. Конфигурация балансировщика нагрузки (HAProxy)#

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

Балансировщик нагрузки решает следующие задачи:

  • распределение read-запросов между репликами;

  • исключение недоступных или деградировавших реплик;

  • fallback на мастер-ноду при отказе всех реплик;

  • прозрачность для сервера приложений (сервер не знает о топологии БД).

Ключевые принципы конфигурации HAProxy:

  • read-пул указывает на виртуальный адрес балансировщика;

  • мастер-база должна присутствовать в конфигурации как резервная;

  • health-check должен учитывать доступность БД, а не только сетевую связность;

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

Пример конфигурации HAProxy:

global
    #log /dev/log local0
    #log /dev/log local1 notice
    #log /var/log/haproxy/haproxy.log local0 info
    log 127.0.0.1:514 local0
    chroot /var/lib/haproxy
    user _haproxy
    group _haproxy
    daemon

defaults
    log global
    mode tcp
    option tcplog
    option dontlognull
    timeout connect 10s 
    timeout client  24h 
    timeout server  600s
    option tcpka
    retries 3

frontend stats
    mode http
    bind :8404
    stats enable
    stats refresh 10s
    stats uri /stats
    stats show-modules
    log-format "%ci:%cp [%t] %ft %b/%s %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"

frontend db_rw_frontend
    bind *:6434
    mode tcp
    log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %ts %ac/%fc/%bc/%sc/%rc %sq/%bq"
    default_backend db_rw_backend

frontend db_ro_frontend
    bind *:6433
    mode tcp
    log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %ts %ac/%fc/%bc/%sc/%rc %sq/%bq"
    default_backend db_ro_backend

backend db_rw_backend
    mode tcp
    balance roundrobin
    option tcp-check
    # Серверы СУБД
    server v00011 192.168.129.91:6432 check
    server v00012 192.168.129.92:6432 check

backend db_ro_backend
    mode tcp
    balance roundrobin
    option tcp-check
    # Серверы СУБД
    server v0013 192.168.129.113:5432 check
    server v0014 192.168.129.120:5432 check
    server v0015 192.168.129.121:5432 check
    server v0012 192.168.129.92:5432 check backup