Настройка дополнительных пулов подключения (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