pgbouncer — pula połączeń dla appservera¶
Appserver łączy się z PostgreSQL przez pgbouncer w trybie session.
Włączone domyślnie, bez żadnej zmiany w .env.
Po co¶
Django w appserverze otwiera nowe połączenie z bazą przy każdym żądaniu i zamyka je na końcu. Pomiar na publikacje.up.lublin.pl (27.09.2026): samo połączenie kosztowało 18 ms, a pierwsze zapytanie na świeżym połączeniu kolejne ~8 ms (zimny cache katalogu) — przy medianie całego żądania 100 ms.
pgbouncer trzyma gotowe połączenia z bazą i oddaje je kolejnym żądaniom. Znika tworzenie procesu PostgreSQL i zimny cache; zostaje tańsze logowanie do samego pgbouncera (SCRAM).
To nie jest ochrona przed zalewem ruchu — tę dają limity nginx i appservera.
Co idzie przez pulę¶
| Serwis | Połączenie z bazą |
|---|---|
| appserver | przez pgbouncer |
| authserver, workerserver, celerybeat, denorm-queue, flower | bezpośrednio |
netdata (bpp_monitor) |
bezpośrednio — ma widzieć prawdziwy serwer |
| backup / restore | docker exec w dbserver — bez zmian |
pgbouncer łączy się z DJANGO_BPP_DB_HOST/DJANGO_BPP_DB_PORT — czyli
z dbserver albo z bazą zewnętrzną. Ta sama konfiguracja
w obu trybach. Logowanie tym samym kontem co dotąd
(DJANGO_BPP_DB_USER/_PASSWORD).
Zmienne ($BPP_CONFIGS_DIR/.env)¶
| Zmienna | Domyślnie | Znaczenie |
|---|---|---|
BPP_APPSERVER_DB_HOST |
pgbouncer |
dokąd łączy się appserver |
BPP_APPSERVER_DB_PORT |
6432 |
port dla powyższego |
PGBOUNCER_POOL_SIZE |
80 |
maks. połączeń pgbouncera do bazy |
PGBOUNCER_MAX_CLIENT_CONN |
1000 |
maks. połączeń appservera do pgbouncera |
PGBOUNCER_VERSION |
v1.25.2-p0 |
tag obrazu edoburu/pgbouncer |
PGBOUNCER_MEM_LIMIT / _CPU_LIMIT |
64m / 0.5 |
limity zasobów |
Pusta wartość = domyślna; błędna liczba = OSTRZEZENIE w logu i domyślna.
Appserver bezpośrednio do bazy (z pominięciem puli):
potem make up. Usługa pgbouncer dalej działa i dalej musi być zdrowa.
Pula a max_connections¶
Do tego samego serwera łączą się też bezpośrednio Celery, denorm-queue
i reszta. Przy starcie pgbouncer odczytuje max_connections i — jeśli pula
się nie mieści — przycina ją z ostrzeżeniem w logu:
bpp-pgbouncer: OSTRZEZENIE: PGBOUNCER_POOL_SIZE=80 przekracza max_connections=100 minus rezerwa 40 — przycinam do 60. Obniz BPP_NGINX_GLOBAL_CONN do <= 60 albo podnies DBSERVER_MEM_LIMIT (autotune: 100 polaczen na 1 GB).
Diagnostyka¶
make pgbouncer-stats # stan puli: kolejka, wolne/zajęte, sesje wiszące bez zapytań
make db-connection-stats # kto trzyma połączenia z PostgreSQL, rozbite na kontenery
docker compose logs pgbouncer | grep bpp-pgbouncer # konfiguracja przy starcie
docker compose logs pgbouncer | grep stats # co 60 s; pole "wait" > 0 = kolejka
Obie komendy są tylko do odczytu. Przy podejrzeniu „baza nie wyrabia” idź po kolei:
make db-connection-stats— ile zmax_connectionsjest zajęte i przez kogo. Przy działającej puli pgbouncer ma twardy sufit (PGBOUNCER_POOL_SIZE), więc jeśli PostgreSQL odrzuca klientów (too many clients/zbyt wielu klientów), przepełnienie robi reszta: usługi łączące się bezpośrednio. Kolumnaidle-tx> 0 przez dłużej niż minutę to otwarta transakcja trzymająca blokady.make pgbouncer-stats— czy pula jest pełna.w kolejce> 0 znaczy, że appserver czeka na wolne połączenie (po 15 s dostaje błąd).
idle w PostgreSQL nie znaczy „wolne” w puli
Pula pracuje w trybie session: połączenie z bazą jest przypisane do
klienta na całe jego połączenie, także między zapytaniami. Backend
pgbouncera widoczny w pg_stat_activity jako idle może więc być wolnym
połączeniem puli albo sesją, którą appserver trzyma i nic na niej nie robi.
Rozróżnia je dopiero make pgbouncer-stats (Serwery: N przypisane
klientom | M wolne).
Sekcja „czas od ostatniego zapytania” w make pgbouncer-stats wyłapuje sesje,
które trzymają połączenie, ale nie pytają bazy. Jedna próbka nic nie
rozstrzyga — powtórz pomiar za kilkanaście minut pod ruchem:
- liczba stała, mniej więcej równa liczbie procesów gunicorna, to narzut startowy (każdy worker trzyma jedno połączenie) — niegroźny;
- liczba rosnąca znaczy, że appserver nie oddaje połączeń po żądaniu
i w końcu zapcha pulę. Doraźnie pomaga wyższe
PGBOUNCER_POOL_SIZE(o ilemax_connectionsma zapas — patrzmake db-connection-stats), przyczyna jest w obrazie BPP.
Długie zapytanie też ma dawny czas ostatniego zapytania — porównaj z kolumną
active w make db-connection-stats.
Awarie¶
pgbouncer jest infrastrukturą jak dbserver: gdy jest unhealthy, appserver
nie startuje, a make up kończy się błędem. Celery, denorm i panele działają
dalej.