# PgBouncer

Uygulamanın gerçek veritabanı bağlantısı buradan geçer (`DB_PORT=20201`). Doğrudan PostgreSQL portu (`DB_DIRECT_PORT=20200`) yalnız migration, `psql` ve dump içindir. ⚠ Portlar 11.08.2026'da blok 2'ye taşındı (eskiden 6433 / 5534).

## ⚠ Kurulum atlanırsa ne olur

`userlist.txt` **`3eegitim_app` ve `3eegitim_app_financial` rollerini içermek zorundadır.** İçermezse uygulama bağlanamaz.

Bu noktada yapılan yaygın "düzeltme" tehlikelidir: hata gitsin diye uygulamayı `postgres` rolüne almak. PostgreSQL superuser'ın `FORCE ROW LEVEL SECURITY`'yi **bypass etmesine izin verir** — uygulama ayağa kalkar, testler yeşil kalır, her müşteri her müşterinin verisini okur ve **hiçbir yerde hata görünmez** (plan chunk2, deney 1).

Eksik olan rolse rol eklenir. Uygulamanın bağlandığı rol değiştirilmez.

```bash
cp deployment/pgbouncer/userlist.txt.example deployment/pgbouncer/userlist.txt
chmod 600 deployment/pgbouncer/userlist.txt
# sifreleri .env'deki DB_PASSWORD (uygulama rolu) ve DB_FINANCIAL_PASSWORD
# ile ayni yapin — DB_USERNAME zaten uygulama rolu, ayri bir DB_APP_PASSWORD yok
```

Roller `0001_01_01_000100_create_application_roles` migration'ı ile oluşur; şifreleri `.env`'den okur ve mevcut rolün şifresini **değiştirmez** (çalışan worker'ları kesmemek için).

## `pool_mode = transaction` ve AD-04

Havuz transaction modundadır. Tenant bağlamı bu yüzden **yalnız transaction içinde `SET LOCAL`** ile kurulur. `SET` (LOCAL'siz) bir değer transaction'ı aşar ve bağlantıyı sonra alan isteğe sızar — canlı ortamda doğrulanmış davranış (deney 7 sızıntı olmadığını, deney 6 `COMMIT` sonrası değerin boş string döndüğünü gösteriyor).

`pool_mode`'u "sorun çıkarmasın diye" `session`'a çekmek bir çözüm değildir; AD-04'ün dayandığı varsayımı kaldırır.

## Stack

`deployment/scripts/stack.sh up pgbouncer` — `userlist.txt` yoksa PgBouncer **atlanır** ve uyarı verir (bind mount boş dizin oluşturup sessizce bozuk kurulum bırakmasın).

`userlist.txt` değiştikten sonra PgBouncer'ın yeniden okuması gerekir:

```bash
docker kill -s HUP 3eegitim_pgbouncer     # dosyayi yeniden okur, baglanti kesmez
# ya da
deployment/scripts/stack.sh up pgbouncer  # konteyneri yeniden yaratir
```

## ⚠ Şema değiştiren migration'dan sonra PgBouncer yeniden başlatılır

Tablo şeklini değiştiren bir migration'ın (kolon ekleme/silme/tip değişikliği) ardından uygulama şu hatayı verebilir:

```
SQLSTATE[0A000]: Feature not supported: cached plan must not change result type
```

Sebep havuzun kendisi: transaction modunda sunucu bağlantıları yeniden kullanılır ve o bağlantılar **eski tablo şekline göre hazırlanmış planları** taşır. `max_prepared_statements = 200` yeni bağlantılarda doğru davranışı sağlar ama **açık olanlardaki eski planı temizlemez**.

```bash
docker restart 3eegitim_pgbouncer
```

27.07.2026'da `backoffice_users`'a kolon eklenince birebir bu yaşandı. **Deploy sırasında migration'dan sonra bu adım vardır** (EGT-51).

## 27.07.2026 — kurulum üç yerden birden bozuktu

Uygulama panele girer girmez `password authentication failed` verdi. Üçü de "ilk kez gerçekten kullanıldığında" ortaya çıktı; test paketi PgBouncer'ı hiç kullanmıyor (doğrudan PostgreSQL'e, 20200'e bağlanıyor), yani **hiçbir test bu yolu koşmamıştı.**

| # | Kusur | Düzeltme |
|---|---|---|
| 1 | `userlist.txt` yalnız `postgres` içeriyordu — uygulama rolleri yoktu. Yukarıdaki uyarının birebir tarif ettiği durum | İki uygulama rolü `.env`'deki şifrelerle eklendi |
| 2 | `stack.sh up pgbouncer` tek başına çalışmıyordu: ağı ve `depends_on`'u postgresql compose dosyası tanımlıyor, o da yüklenmiyordu → *"undefined network"* | PgBouncer istendiğinde postgresql compose dosyası da yükleniyor |
| 3 | Bind mount yolları `./pgbouncer/...` idi; compose `--project-directory <repo kökü>` ile çağrıldığı için repo kökünde aranıyordu → *"not a directory"* | Yollar `./deployment/pgbouncer/...` oldu |

⚠ **Ders (PA-225'in aynısı):** çalışan konteyner, kurulumun doğru yazıldığının kanıtı değildir. 32 saattir ayakta olan PgBouncer, yeniden yaratılmayı denediğimiz ilk anda üç ayrı sebeple açılmadı.
