Координатор пулов

Координатор пулов ограничивает суммарное число серверных соединений к одной базе по всем пользователям пула. Когда лимит выбран, он может освободить слот: закрыть свободное соединение у пользователя с запасом и отдать этот слот тому, кто сейчас ждёт подключение к PostgreSQL.

Эта страница объясняет модель и сценарии применения. Рецепты настройки и разбор вывода SHOW POOL_COORDINATOR смотрите в Пуле под нагрузкой.

Какую задачу решает

Без координатора каждый пользовательский пул независим. pool_size равный 40 у пяти пользователей означает до 200 серверных соединений, и PostgreSQL приходится защищать свои лимиты уже на своей стороне.

max_db_connections в PgBouncer ограничивает общую сумму, но как только лимит достигнут, новые клиенты просто становятся в очередь. Соединения освобождаются только тогда, когда их текущий владелец сам закроет их по server_idle_timeout. Кто первым занял слоты, тот и удерживает их независимо от интенсивности использования, а медленные нагрузки никогда не уступают быстрым.

Координатор в pg_doorman ограничивает сумму и:

  • Вытесняет свободные соединения у пользователей с запасом, когда другому пользователю нужно вырасти.
  • Ранжирует пользователей по p95 времени транзакции, чтобы самые медленные пулы уступали слоты первыми. Пулы с быстрыми транзакциями сохраняют преимущество переиспользования; пулы с длинными транзакциями простаивают большую долю времени, поэтому забрать у них слот стоит дешевле.
  • Резервирует небольшое переполнение для коротких всплесков. Настраивается отдельно от основного лимита.
  • Защищает минимальный размер пула на пользователя. Соединения ниже этого минимума не вытесняются.

Когда использовать

Включайте координатор, когда:

  • На одной базе работают разные приложения или пользователи, и нужен верхний предел числа серверных соединений (max_connections PostgreSQL, RAM, файловые дескрипторы).
  • Одна нагрузка приходит всплесками и должна временно забирать простаивающие слоты у других, не закрепляя их за собой навсегда.
  • Вы работаете рядом с потолком соединений PostgreSQL и хотите управляемую деградацию вместо зависимости от того, кто первым занял слоты.

Координатор не нужен, когда:

  • pool_size каждого пользователя достаточно мал, чтобы их сумма укладывалась в max_connections PostgreSQL с запасом.
  • Нагрузки предсказуемы и заранее размечены.
  • Вы хотите простоту уровня PgBouncer. max_db_connections без вытеснения поддерживается, но не рекомендуется для общих баз.

Конфигурация

pools:
  shared_db:
    server_host: "127.0.0.1"
    server_port: 5432
    pool_mode: "transaction"

    # Общий лимит на всех пользователей этого пула.
    max_db_connections: 80

    # Резерв сверх max_db_connections для коротких всплесков.
    # Захватывается, только если в течение reserve_pool_timeout не нашлось свободного соединения.
    reserve_pool_size: 16
    reserve_pool_timeout: "3s"

    # Страховка для пользователя: соединения никогда не вытесняются у него, даже под давлением.
    # Сумма по пользователям должна быть <= max_db_connections.
    min_guaranteed_pool_size: 5

    # Льготный период для вытеснения: соединения младше этого возраста не вытесняются.
    # Защищает от колебания, когда нагрузка кратковременно простаивает.
    min_connection_lifetime: "30s"

    users:
      - username: "fast_app"
        password: "md5..."
        pool_size: 40

      - username: "batch_job"
        password: "md5..."
        pool_size: 60

Эффективный потолок: max_db_connections + reserve_pool_size = 96. Резерв поглощает короткие пики; если пик затягивается, включается вытеснение.

Как выбирается донор

Когда пользователь запрашивает новое серверное соединение, а лимит уже достигнут:

  1. Найти кандидатов со свободными соединениями. Пользователь, у которого все соединения активны, не может стать донором: его работа ещё выполняется.
  2. Пропустить защищённых. Пользователь ниже min_guaranteed_pool_size исключается.
  3. Пропустить недавно созданные соединения. Соединения младше min_connection_lifetime не вытесняются; это снижает колебания при коротких простоях.
  4. Ранжировать по излишку. Пользователи с наибольшим числом свободных соединений сверх min_guaranteed_pool_size получают высший ранг.
  5. Разрешить ничью по p95 времени транзакции. Среди пулов с одинаковым излишком первым уступает тот, у кого выше p95. Высокий p95 означает, что каждая транзакция дольше держит соединение, а значит одно вытеснение ломает меньше повторных выдач.

Выбранное свободное соединение закрывается; запрашивающий пользователь получает новое соединение из PostgreSQL.

Мониторинг

SHOW POOL_COORDINATOR показывает текущее состояние по каждой базе:

database    | max_db_conn | current | reserve_size | reserve_used | evictions | reserve_acq | exhaustions
shared_db   | 80          | 78      | 16           | 2            | 142       | 18          | 0
  • evictions быстро растёт — один пользователь регулярно остаётся без слотов. Поднимите max_db_connections или задайте этому пользователю min_guaranteed_pool_size.
  • reserve_acq высокий — всплески нормальны, но размер занижен; рассмотрите повышение max_db_connections вместо опоры на резерв.
  • exhaustions ненулевой — резерв тоже был полным. Клиенты упёрлись в query_wait_timeout, ожидая бэкенд. Поднимите лимит.

Prometheus: pg_doorman_pool_coordinator{type="..."} (gauge) и pg_doorman_pool_coordinator_total{type="evictions|reserve_acquisitions|exhaustions"} (counter). Смотрите Команды администратора и Справочник Prometheus.

Оговорки

  • Координатор работает только внутри одного пула (одной базы). Кросс-пуловые / кросс-баз ограничения не поддерживаются.
  • Вытеснение выбирает только свободные соединения. Пользователь, удерживающий все соединения в длинных транзакциях, не может стать донором, и другие пользователи могут ждать. Если ваша нагрузка устроена именно так, поднимите max_db_connections или разделите её по пулам.
  • min_guaranteed_pool_size — это нижняя граница для вытеснения, а не min_pool_size для прогрева. Эти соединения пул всё равно создаёт по требованию.
  • Задавать max_db_connections без min_guaranteed_pool_size — это режим PgBouncer: работает, но мелкие пользователи голодают под давлением. Для общих баз всегда задавайте оба.

Куда дальше