Режимы пула

pg_doorman поддерживает два режима пула: transaction и session. Режим задаётся для пула, при необходимости переопределяется для конкретного пользователя.

Режима statement нет. Покул на уровне отдельного оператора ломает больше драйверов, чем помогает, а pg_doorman агрессивно оптимизирован под транзакционный режим (кеш prepared statements, direct handoff, FIFO-планирование).

Транзакционный режим (рекомендуется)

pools:
  mydb:
    pool_mode: "transaction"

Backend-соединение удерживается в течение транзакции и возвращается в пул при COMMIT, ROLLBACK или неявном завершении.

Именно этот режим даёт ту самую эффективность соединений, ради которой существует pg_doorman: pool_size равный 40 обслуживает тысячи клиентов, пока транзакции остаются короткими.

Что работает в транзакционном режиме (там, где большинство пулеров проваливаются):

  • Prepared statements. pg_doorman кеширует их в рамках пула, переименовывает имена statement между backend-соединениями и прозрачно повторно готовит запрос. Драйверы, привязанные к unnamed-statement (Go pgx, .NET Npgsql, Python asyncpg), работают без настройки.
  • Pipelined-батчи и асинхронный поток Flush.
  • Cancel-запросы поверх TLS.
  • LISTEN / NOTIFY — но только внутри транзакции; кросс-транзакционные уведомления теряются (так же, как в PgBouncer).

Что в транзакционном режиме не работает:

  • SET и RESET вне транзакции. Используйте сессионный режим для клиентов, опирающихся на изменение GUC уровня сессии (SET TIME ZONE, SET search_path один раз на соединение).
  • advisory-блокировки, удерживаемые между транзакциями. Используйте сессионный режим.
  • курсоры, удерживаемые вне транзакций (WITH HOLD). Используйте сессионный режим.
  • SET LOCAL работает как ожидается — он ограничен транзакцией.

Сессионный режим

pools:
  legacy_app:
    pool_mode: "session"

Backend-соединение удерживается в течение клиентской сессии. В пул возвращается только при отключении клиента.

Используйте, когда:

  • Приложение использует состояние уровня сессии (SET search_path, SET TIME ZONE).
  • Приложение использует курсоры WITH HOLD.
  • Приложение использует advisory-блокировки между транзакциями.
  • Вы переезжаете с немодифицированного PgBouncer-развёртывания, работавшего в сессионном режиме, и хотите подмену один-в-один.

В сессионном режиме pool_size фактически равен максимальному числу одновременных клиентов. Размер пула подбирается так, чтобы соответствовать max_connections PostgreSQL минус резервы.

Переопределение для конкретного пользователя

Режим пула можно переопределить для конкретного пользователя:

pools:
  mydb:
    pool_mode: "transaction"
    users:
      - username: "app"
        password: "md5..."
        pool_size: 40
      - username: "admin_tools"
        password: "md5..."
        pool_size: 4
        pool_mode: "session"

Полезно, когда одному пользователю (инструменты эксплуатации, миграции) нужна семантика сессии, а основное приложение остаётся в транзакционном режиме.

Очистка при возврате в пул

Транзакционный режим в подробностях: когда бэкенд уходит обратно в пул, pg_doorman выполняет RESET ALL и ROLLBACK (если cleanup_server_connections: true, как по умолчанию). Это сбрасывает:

  • значения SET уровня сессии;
  • курсоры;
  • имена prepared statements, которые драйвер привязал к конкретным backend-именам (кеш prepared statements pg_doorman переживает — он индексируется текстом запроса, а не именем statement на бэкенде);
  • advisory-блокировки (pg_advisory_unlock_all неявно входит в RESET ALL).

DEALLOCATE ALL и DISCARD ALL со стороны клиента также инициируют сброс всего, что закешировано pg_doorman для этого клиента в кеше prepared statements. Кеш уровня пула не затрагивается.

Чтобы отключить очистку (ради производительности в жёстко контролируемых развёртываниях):

pools:
  mydb:
    pool_mode: "transaction"
    cleanup_server_connections: false

Делайте так только если вы уверены, что приложение никогда не оставляет состояние сессии.

Справочник