Как настроить соотношение max_connections в PostgreSQL и PgBouncer при разных значениях на балансере
Что проверяют
Понимаете ли вы, что соединение к PostgreSQL дорогой ресурс, а PgBouncer нужен, чтобы тысячи клиентов делили несколько десятков серверных соединений. «Разные значения на балансере» намекают на несколько экземпляров PgBouncer за HAProxy или сервисом Kubernetes. Следом спросят, как пересчитать бюджет, когда их число меняется.
Как отвечать
Разведите две стороны пула:
- клиентская:
max_client_connв PgBouncer, по умолчанию 100. Тут можно ставить тысячи, упор в файловые дескрипторы (ulimit -n), а не в базу; - серверная:
default_pool_size(по умолчанию 20) плюсreserve_pool_sizeна каждую пару «база + пользователь», сверху общий потолокmax_db_connections; - PostgreSQL:
max_connections, по умолчанию 100, меняется только перезапуском. Часть занимаютsuperuser_reserved_connections, репликация и мониторинг.
Формула: экземпляры PgBouncer × пулы × (default_pool_size + reserve_pool_size) плюс запас на админов, миграции и репликацию должно быть меньше max_connections. Если за балансером три PgBouncer, каждому достаётся примерно треть бюджета через max_db_connections. maxconn на балансере не должен превышать суммарный max_client_conn, иначе клиенты получат отказ уже на PgBouncer.
Бюджет не равен «сколько выдержит память». Активных соединений полезно держать порядка нескольких на ядро, остальные конкурируют за CPU и work_mem. Главный выигрыш даёт pool_mode = transaction: соединение отдаётся клиенту только на транзакцию.
Проверка: SHOW POOLS в админке PgBouncer (cl_waiting, maxwait) и pg_stat_activity в базе.
Где спотыкаются
Выставляют max_connections по числу клиентов, например 2000. Каждое соединение это отдельный процесс со своей памятью, и база деградирует раньше, чем кончатся слоты.
Забывают, что пул считается на пару «база + пользователь» и на каждый экземпляр PgBouncer. Добавили реплику пулера в деплойменте, и число соединений к базе удвоилось.
Включают transaction mode, не проверив приложение: SET на сессию, LISTEN и сессионные advisory lock там ломаются. Prepared statements поддерживаются только в свежих версиях PgBouncer через max_prepared_statements.