Что было. PostgreSQL с max_connections = 250, перед ним pgbouncer в режиме transaction с default_pool_size = 10 и max_client_conn = 10000. Приложение периодически получало FATAL: remaining connection slots are reserved for non-replication superuser connections, хотя автор был уверен, что pgbouncer держит соединения под контролем и их «никогда не больше 130».
В чём ошибка в рассуждении. pgbouncer создаёт отдельный пул на каждую пару «пользователь + база данных». У автора было около 150 баз. Даже при одном пользователе на базу это 150 пулов по 10 серверных соединений в пределе, то есть до 1500 потенциальных подключений к PostgreSQL при лимите 250. В обычное время пулы почти пусты (поэтому 130), но при всплеске нагрузки на десятки баз одновременно они разрастаются, и последние 3 слота (superuser_reserved_connections) остаются только для суперпользователя. Отсюда ошибка.
Что предложили.
- Уменьшить
default_pool_size(например, до 3–5) иmin_pool_sizeдо 0–1: при 150 базах и так набирается много. - Задать
max_db_connectionsв pgbouncer: жёсткий потолок на базу независимо от числа пользователей. - Поднять
max_connectionsв PostgreSQL в разумных пределах (каждое соединение это отдельный процесс и память) и, если нужно,superuser_reserved_connections. - Посмотреть, кто реально держит соединения:
SELECT usename, datname, state, count(*) FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC;и в консоли pgbouncerSHOW POOLS;. - Убить долгие простаивающие транзакции:
idle_in_transaction_session_timeoutв PostgreSQL (например,30s). Соединение в состоянииidle in transactionзанимает слот и не отдаёт его пулу.
Практическое правило. Сумма default_pool_size × число активных пар пользователь/база (плюс reserve_pool_size) должна быть меньше max_connections − superuser_reserved_connections − соединения, идущие мимо pgbouncer (миграции, cron, мониторинг). Если баз много, проще одна общая база со схемами, чем сотни маленьких.
Если pgbouncer нет. Та же ошибка означает, что приложение открывает соединений больше, чем разрешено: обычно каждый воркер держит свой пул. Ставить pgbouncer в режиме transaction и уменьшать пулы в приложении.
Параметры соединений описаны в документации PostgreSQL.