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.
Ограничение числа соединений на уровне базы с приоритетным вытеснением. max_db_connections задаёт суммарное число backend-соединений к одной базе; когда лимит исчерпан, idle-соединения вытесняются у пользователей с наибольшим избытком, ранжируя их по p95 времени транзакции — самые медленные пулы отдают соединения первыми. Резервный пул поглощает короткие всплески. Per-user min_guaranteed_pool_size защищает критичные нагрузки.
В PgBouncer max_db_connections есть, но без вытеснения и без честности распределения. В Odyssey аналога нет.
Когда PgDoorman работает рядом с PostgreSQL на одной машине и switchover Patroni убивает локальный бэкенд, PgDoorman опрашивает Patroni REST API эндпоинт /cluster, выбирает живого члена кластера (предпочтение отдаётся sync_standby) и направляет новые соединения туда за 1–2 TCP round trips. Локальный бэкенд остаётся в cooldown; fallback-соединения используют короткий lifetime, чтобы пул вернулся к локальному узлу после восстановления.
Одна строка в [general] включает функцию для всех пулов. Никакого внешнего HAProxy, никакого consul-template.
Замените бинарник, не потеряв ни одного клиента. Новый процесс сразу принимает новые соединения, а существующие клиенты завершают свои транзакции на старом. 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-эндпоинт встроен.
Сравнение
| PgDoorman | PgBouncer | Odyssey | |
|---|---|---|---|
| Многопоточность | Да | Нет | Да |
| 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 PgBouncer | vs 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. Никаких паролей в открытом виде в конфиге.
Руководство по установке → · Справочник по конфигурации →
Куда дальше
- Впервые с PgDoorman? Начните с Обзора, затем Установка и Базовое использование.
- Мигрируете с PgBouncer или Odyssey? Прочитайте Сравнение и Аутентификация.
- Используете Patroni? См. Patroni-assisted fallback и
patroni_proxy. - Готовитесь к production? Прочитайте Пул под нагрузкой и Pool Coordinator.
- Эксплуатация PgDoorman? См. Binary upgrade, Сигналы, Troubleshooting.