PgDoorman

Многопоточный пулер соединений для PostgreSQL, написанный на Rust. Drop-in замена для PgBouncer, Odyssey и PgCat. В production в Ozon уже больше трёх лет под нагрузками Go (pgx), .NET (Npgsql), Python (asyncpg, SQLAlchemy) и Node.js.

Скачать PgDoorman 3.6.0 · Сравнение · Benchmarks

Что отличает PgDoorman

Три вещи, которых вы не найдёте в PgBouncer и Odyssey.

Pool Coordinator

Ограничение числа соединений на уровне базы с приоритетным вытеснением. max_db_connections задаёт суммарное число backend-соединений к одной базе; когда лимит исчерпан, idle-соединения вытесняются у пользователей с наибольшим избытком, ранжируя их по p95 времени транзакции — самые медленные пулы отдают соединения первыми. Резервный пул поглощает короткие всплески. Per-user min_guaranteed_pool_size защищает критичные нагрузки.

В PgBouncer max_db_connections есть, но без вытеснения и без честности распределения. В Odyssey аналога нет.

Подробнее →

Patroni-assisted Fallback

Когда PgDoorman работает рядом с PostgreSQL на одной машине и switchover Patroni убивает локальный бэкенд, PgDoorman опрашивает Patroni REST API эндпоинт /cluster, выбирает живого члена кластера (предпочтение отдаётся sync_standby) и направляет новые соединения туда за 1–2 TCP round trips. Локальный бэкенд остаётся в cooldown; fallback-соединения используют короткий lifetime, чтобы пул вернулся к локальному узлу после восстановления.

Одна строка в [general] включает функцию для всех пулов. Никакого внешнего HAProxy, никакого consul-template.

Подробнее →

Graceful Binary Upgrade

Замените бинарник, не потеряв ни одного клиента. Новый процесс сразу принимает новые соединения, а существующие клиенты завершают свои транзакции на старом. TLS, состояние соединения и cancel keys переносятся корректно.

PgBouncer требует SO_REUSEPORT с отдельными процессами (что приводит к разбалансировке пулов). В Odyssey аналогичного механизма нет.

Подробнее →

Почему PgDoorman

  • Drop-in замена. Прозрачно кэширует и переименовывает prepared statements в транзакционном режиме — никаких DISCARD ALL, DEALLOCATE или хаков в драйверах.
  • Многопоточность. Один общий пул на все рабочие потоки. PgBouncer однопоточен; запуск нескольких инстансов через SO_REUSEPORT приводит к разбалансировке пулов.
  • Подавление thundering herd. Когда 200 клиентов одновременно борются за 4 idle-соединения, PgDoorman ограничивает число параллельных создаваемых backend-соединений и направляет ожидающих на возвращаемые соединения через direct handoff — большинство получают соединение за микросекунды.
  • Ограниченная хвостовая задержка. Строгий FIFO через каналы direct-handoff удерживает p99 в пределах 10% от p50 независимо от числа клиентов. Опережающая замена при истечении server_lifetime — никаких всплесков при ротации соединений.
  • Обнаружение разорванных бэкендов. Когда клиент держит открытую транзакцию, а бэкенд умирает (failover, OOM kill), PgDoorman сразу возвращает ошибку. Другие пулеры ждут TCP keepalive и оставляют клиентов висеть на минуты.
  • Сделано для эксплуатации. Конфиг в YAML или TOML с человекочитаемыми длительностями ("30s", "5m"). pg_doorman generate --host your-db интроспектирует PostgreSQL и собирает конфиг. pg_doorman -t валидирует его перед деплоем. Prometheus-эндпоинт встроен.

Сравнение

PgDoormanPgBouncerOdyssey
МногопоточностьДаНетДа
Prepared statements в транзакционном режимеДаНачиная с 1.21Начиная с 1.3
Полная поддержка extended query protocolДаДаЧастично
Pool Coordinator с приоритетным вытеснениемДаНетНет
Patroni-assisted fallback (встроенный)ДаНетНет
Опережающая замена при истечении server_lifetimeДаНетНет
Обнаружение застрявших бэкендов (idle-in-transaction)ДаНетНет
Graceful binary upgradeДаОграниченноНет
Server-side TLS (mTLS, горячая перезагрузка)ДаНетНет
Auth: passthrough SCRAM (без пароля в открытом виде в конфиге)ДаНетДа
Auth: JWTДаНетНет
Auth: PAM / pg_hba.conf / auth_queryДаДаДа
Auth: LDAPНетНачиная с 1.25Да
Конфиг YAML / TOMLДаНет (INI)Нет (свой формат)
JSON структурированное логированиеДаНетДа
Перцентили задержки (p50/90/95/99)ДаНетДа
Режим проверки конфига (-t)ДаНетНет
Авто-конфиг из PostgreSQLДаНетНет
Встроенный Prometheus-эндпоинтДаВнешнийДа

Полная матрица фич →

Бенчмарки

AWS Fargate (16 vCPU), pool size 40, pgbench 30 с на тест:

Сценарийvs PgBouncervs Odyssey
Extended protocol, 500 клиентов + SSL×3.5+61%
Prepared statements, 500 клиентов + SSL×4.0+5%
Simple protocol, 10 000 клиентов×2.8+20%
Extended + SSL + Reconnect, 500 клиентов+96%~0%

Полные результаты →

Быстрый старт

Запуск через Docker:

docker run -p 6432:6432 \
  -v $(pwd)/pg_doorman.yaml:/etc/pg_doorman/pg_doorman.yaml \
  ghcr.io/ozontech/pg_doorman

Минимальный конфиг (pg_doorman.yaml):

general:
  host: "0.0.0.0"
  port: 6432
  admin_username: "admin"
  admin_password: "change_me"

pools:
  mydb:
    server_host: "127.0.0.1"
    server_port: 5432
    pool_mode: "transaction"
    users:
      - username: "app"
        password: "md5..."   # хэш из pg_shadow / pg_authid
        pool_size: 40

server_username и server_password опущены намеренно — PgDoorman переиспользует MD5-хэш клиента или SCRAM ClientKey для аутентификации в PostgreSQL. Никаких паролей в открытом виде в конфиге.

Руководство по установке → · Справочник по конфигурации →

Куда дальше