PostgreSQLSeniorSystem design

Что такое connection pooling и зачем он нужен? Чем PgBouncer отличается от Pgpool? Чем session/transaction/statement pooling отличаются?

Connection pooling переиспользует ограниченный набор реальных backend-процессов PostgreSQL. PgBouncer — лёгкий пулер без лишних функций; Pgpool-II добавляет routing и репликацию. Режимы: session сохраняет state сессии, transaction — самый эффективный, statement — почти не используется.

Зачем нужен connection pooling

Каждое соединение с PostgreSQL — отдельный backend-процесс (~5–10 МБ RSS). При 1000 одновременных клиентах это 5–10 ГБ только на процессы, плюс затраты на context switch. Параметр max_connections ограничивает число backend'ов; превышение возвращает ошибку FATAL: sorry, too many clients already. Пулер принимает тысячи клиентских соединений, но держит к PostgreSQL небольшой набор server connections.

PgBouncer vs Pgpool-II

  • PgBouncer: легковесный однофункциональный пулер (C, ~1 МБ RSS). Умеет только pooling. Конфигурируется через pgbouncer.ini; ключевые параметры: pool_mode, max_client_conn, default_pool_size, server_reset_query. Стандартный выбор для web workload.
  • Pgpool-II: многофункциональный прокси — pooling + load balancing read replicas + online recovery + watchdog HA. Значительно сложнее в настройке; оправдан при нужде в routing между primary/replica или встроенном failover.

Три режима pooling

Session pooling

Server connection закрепляется за клиентом на время всей сессии и возвращается в пул только при disconnect. Сохраняет всё session-local state: temp tables, prepared statements, SET-переменные, advisory locks. Коэффициент мультиплексирования минимален — пул помогает только ограничить пиковое число backend'ов.

Transaction pooling

Server connection возвращается в пул после каждого COMMIT или ROLLBACK. Позволяет держать N server connections для M >> N клиентов. Это наиболее производительный режим, но ломает session-level state: prepared statements, temp tables, SET LOCAL, advisory locks не сохраняются между транзакциями. Требует аудита приложения.

Statement pooling

Server connection возвращается в пул после каждого отдельного SQL-выражения. Запрещает multi-statement транзакции — практически не используется в реальных приложениях.

# /etc/pgbouncer/pgbouncer.ini
[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp

[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 2000
default_pool_size = 25
reserve_pool_size = 5
reserve_pool_timeout = 3
server_reset_query = DISCARD ALL
server_check_query = SELECT 1
log_connections = 0
log_disconnections = 0

Мониторинг состояния пула через псевдо-базу pgbouncer:

-- подключиться: psql -p 6432 pgbouncer
SHOW POOLS;
SHOW STATS;
SHOW CLIENTS;

Подводные камни

  • Transaction pooling + prepared statements = intermittent ERROR: prepared statement does not exist. Отключают server-side prepare в драйвере (например, в asyncpg: statement_cache_size=0; в psycopg3: prepare_threshold=None).
  • SET search_path, SET TimeZone и другие сессионные параметры не сохраняются между транзакциями в transaction mode; server_reset_query = DISCARD ALL обнуляет state при возврате соединения в пул.
  • Advisory locks привязаны к server connection, а не к клиенту — в transaction mode lock может «потеряться».
  • Пулер не ускоряет медленные запросы; он лишь защищает от connection storm. Если все 25 server connections заняты долгими запросами, клиенты встают в очередь.
  • Head-of-line blocking: один медленный запрос блокирует весь пул при маленьком default_pool_size.
  • TLS между приложением и PgBouncer и между PgBouncer и PostgreSQL конфигурируются отдельно; часто забывают внутренний сегмент.
  • PgBouncer не поддерживает LISTEN/NOTIFY в transaction mode — соединения для подписки нужно выносить мимо пулера.
  • При выборе default_pool_size ориентируйся на количество CPU PostgreSQL-сервера: эмпирическое правило — 2–4 × vCPU.

Common mistakes

  • Считать pooling ускорителем любого запроса.
  • Включать transaction mode без проверки ORM.
  • Путать PgBouncer и Pgpool по назначению.

What the interviewer is testing

  • Просит объяснить session/transaction modes.
  • Проверяет prepared statements и temp tables.
  • Уточняет sizing пула.

Sources

Related topics