dataiotech Labs
dbaguidedataxtoolsdataopspheredataopsphereDataOps
dataopsphere.com
Reliability / Capacity & Scaling
11 dk / 11 min
2026-10-29

Uygulama Ölçeklenirken Bağlantı Bütçesini Nasıl Koruruz?

Uygulama instance sayısı artarken PostgreSQL backend bütçesi formülü, rolling deployment surge yönetimi, exponential backoff ve SRE nöbetçi eskalasyon kartı.

#reliability#capacity-planning#connection-budget#postgresql#pgbouncer#kubernetes#autoscaling#runbook#on-call

▸Kısa Açıklama ve Yönetici Özeti

Uygulama filonuz Kubernetes veya bulut ortamında yatayda ölçeklenirken (HPA - Horizontal Pod Autoscaler), veritabanı kapasitesi sihirli bir şekilde kendiliğinden genişlemez.

Bir mikroservisin pod sayısı 10'dan 50'ye çıktığında, her pod içindeki bağlantı havuzunun (HikariCP, Node pg-pool) açtığı soketler bir araya gelerek PostgreSQL'in max_connections tavanına çarpar. Üstelik rolling deployment anında eski ve yeni pod'ların aynı anda çalışması (deploy surge) bu baskıyı ikiye katlar.

Bu operasyonel runbook şu temel soruyu yanıtlar:

"Uygulama instance sayısı artarken PostgreSQL backend bütçesini matematiksel olarak nasıl koruruz; rolling deployment surge örtüşmesini, asenkron worker ve migration bağlantılarını nasıl hesaba katarız; istemci tarafında exponential backoff ve SRE alarm eskalasyonunu nasıl işletiriz?"

▸1. Üç Farklı Bağlantı Sayısını Birbirinden Ayırma

Bağlantı yönetiminde yapılan en temel kavram karmaşası, istemci soket sayısı ile veritabanı proses sayısını eşitlemektir. Sağlıklı bir mimaride üç farklı katmanın sayıları bağımsız izlenmelidir:

Mimari Topoloji & Akış Şeması
Mermaid şeması çiziliyor...
KatmanMetrik TanımıKaynak MaliyetiSağlıklı Değer Aralığı
1. İstemci Bağlantısı ($C_{\text{client}}$)Uygulama container'larından PgBouncer'a açılan açık TCP oturumlarıDüşük (PgBouncer epoll event-loop kullanır; soket başına ~10-20 KB bellek)Yüzlerce veya binlerce olabilir (örn. 500–5000)
2. PgBouncer Sunucu Bağlantısı ($S_{ ext{pool}}$)PgBouncer'ın PostgreSQL'e açık tuttuğu aktif/boşta sunucu bağlantılarıOrta (PostgreSQL tarafında açık soket tutar)vCPU sayısı ve sorgu I/O profiline göre kalibre edilmiş değer (örn. $2 imes ext{vCPU} + ext{disk}$ başlangıç heuristiği)
3. Gerçek PostgreSQL Backend Süreci ($B_{\text{backend}}$)PostgreSQL çekirdeğinde her oturum için fork edilen bağımsız Linux prosesiYüksek (Proses başına 5–10 MB RAM, work_mem, CPU context-switch)Donanım çekirdek sayısıyla orantılı: genellikle $\le 100$

▸2. İki Farklı Mimaride Kapasite Bütçe Hesabı

Kapasite hesabı tek bir formülle genellenemez; mimarinin doğrudan bağlantı mı yoksa PgBouncer ara katmanı mı kullandığına göre iki ayrı hesaplama yapılmalıdır:

#### Mimari A: Doğrudan PostgreSQL Bağlantısı (Direct Connection) Uygulama pod'larının veritabanına doğrudan bağlandığı senaryoda, gereken toplam kapasite pod sayısı ve havuz tavanlarının doğrudan toplamıdır:

$$\text{Doğrudan Kapasite Üst Sınırı} \ge \sum_{i=1}^{M} \left( N_i \times (1 + \text{Surge}_i) \times P_{\max,i} \right) + J_{\text{workers}} + M_{\text{migration}} + \text{Mon} + \text{Admin}_{\text{reserved}}$$

  • $M$: Veritabanına bağlanan mikroservis adedi.
  • $N_i$: $i$. servisin normal şartlardaki azami pod (instance) sayısı.
  • $\text{Surge}_i$: Kubernetes rolling update sırasında eski ve yeni pod'ların eşzamanlı çalışma katsayısı (maxSurge: 100% ise $1.0$).
  • $P_{\max,i}$: $i$. servisin pod başına azami havuz boyutu (örn. HikariCP maximumPoolSize = 10).
  • $J_{\text{workers}}$: Asenkron worker'lar (Celery, Temporal) için doğrudan ayrılan bağlantılar.
  • $M_{\text{migration}}$: CI/CD göç araçları (Flyway, Liquibase) için dedike bağlantı.
  • $\text{Mon}$: Gözlem toplayıcıları (Prometheus postgres_exporter).
  • $\text{Admin}_{\text{reserved}}$: Acil durum rezerve slotları (superuser_reserved_connections + PG 16 reserved_connections).

#### Mimari B: PgBouncer ile Çoğullanmış Mimari (PgBouncer Multiplexed) PgBouncer kullanıldığında istemci pod sayısı binlere çıksa dahi veritabanı backend sayısı pod sayısından bağımsızlaşır. Ancak birden fazla PgBouncer instance'ı, veritabanı/kullanıcı havuzları ve PgBouncer'ı atlayan doğrudan işler hesaba katılmalıdır:

$$\text{Yapılandırılmış Backend Tavanı} = \sum_{k=1}^{K} \left[ \sum_{d=1}^{D} \min\left(\text{max\_db\_connections}_d, \sum_{u=1}^{U} \left( \text{pool\_size}_{(u,d)} + \text{reserve\_pool}_{(u,d)} \right)\right) \right] + M_{\text{direct}} + \text{Mon} + \text{Admin}_{\text{reserved}}$$

  • $K$: Aktif PgBouncer instance sayısı (HA proxy kümesi veya sidecar adedi).
  • $D$ ve $U$: İlgili instance üzerinden erişilen aktif veritabanı ve kullanıcı rolleri adedi.
  • $\text{pool\_size}_{(u,d)}$ ve $\text{reserve\_pool}$: Her $(u, d)$ çifti için tanımlı havuz ve rezerv kapasitesi.
  • $M_{\text{direct}}$: DDL kilitleri veya oturum durumu gereksinimi nedeniyle PgBouncer'ı atlayarak doğrudan PostgreSQL'e bağlanan migration veya özel batch işleri.
  • Yapılandırılmış Tavan vs. Gözlenen Kullanım: Yapılandırılmış tavan tüm havuzların aynı anda dolduğu en kötü durum sınırıdır. Gözlenen backend kullanımı ise tipik OLTP yüklerinde çok daha düşüktür; boyutlandırma yapılırken hem yapılandırılmış tavan max_connections altında tutulmalı hem de gözlenen kullanım telemetriyle doğrulanmalıdır.
[!NOTE] Güvenlik Payı ve Zaman Aşımı Değerlendirmesi: Literatürde sıkça anılan "%20 güvenlik payı" veya "250ms bağlantı zaman aşımı" evrensel kurallar değildir: - Güvenlik Payı: Dalgalı OLTP yüklerinde ani kilit kuyrukları ve replikasyon gecikmelerini absorbe etmek için %15–25 güvenlik payı tavsiye edilirken, kontrollü batch veya analitik sistemlerde daha dar paylarla çalışılabilir. - Zaman Aşımı: 250ms tavanı, milisaniyelik yanıt süresi hedefleyen etkileşimli REST API'ler için hızlı hata verme (fail-fast) varsayımıdır; uzun çalışan analitik veya toplu iş kuyruklarında bu süre iş yükü SLA'ine göre belirlenmelidir.

▸3. Rolling Deployment ve %100 Surge Örtüşmesi (Deployment Surge)

En sık yaşanan üretim kesintilerinden biri, normalde sakin çalışan bir sistemin yeni versiyon yayına alınırken aniden FATAL: sorry, too many clients already vermesidir.

Mimari Topoloji & Akış Şeması
Mermaid şeması çiziliyor...

#### Çözüm ve Önlemler:

  1. 1
    Ara Katmanda PgBouncer Kullanmak: Uygulama podları doğrudan veritabanına bağlanmak yerine PgBouncer'a bağlanırsa, pod sayısı $10 \to 20$ olsa dahi PgBouncer'ın PostgreSQL'e açtığı backend sayısı sabit kalır (örn. 20 backend). Surge dalgası PostgreSQL'e değil, PgBouncer'ın hafif istemci soket kuyruğuna çarpar.
  2. 2
    Kubernetes `maxSurge` ve `maxUnavailable` İnce Ayarı: Eğer PgBouncer kullanılamıyorsa, maxSurge: 25% ve maxUnavailable: 25% yapılarak aynı anda açılan ek pod sayısı sınırlandırılmalıdır.
  3. 3
    Graceful Shutdown ve `preStop` Hook: Pod SIGTERM aldığında havuz kütüphanesinin mevcut işlemleri bitirip soketleri temizlemesi için terminationGracePeriodSeconds: 45 tanımlanmalıdır.

▸4. Çoklu Kullanıcı ve Veritabanı Kombinasyonu Çoğalması (`User × DB Multiplier`)

PgBouncer devreye alındığında dahi kapasite kontrolsüz bırakılamaz. PgBouncer bağlantı havuzlarını tek bir global torbada toplamaz; her `(veritabanı, kullanıcı)` çifti için ayrı bir sunucu havuzu tahsis eder.

$$\text{Potansiyel Backend Sayısı} = N_{\text{veritabanı}} \times N_{\text{kullanıcı}} \times \text{default\_pool\_size}$$

Örnek Risk ve Kapasite Modeli (Triad Doğrulaması):

  • $K = 2$ PgBouncer instance (Yüksek erişilebilirlik kümesi)
  • Her instance üzerinde $D = 2$ veritabanı $ imes U = 2$ kullanıcı rolü (Toplam 4 bağımsız havuz)
  • default_pool_size = 15, reserve_pool_size = 5
  • max_db_connections = 35, max_user_connections = 30
  • PgBouncer harici doğrudan işler: 2 migration + 5 monitoring + 5 admin reserved = 12 doğrudan backend

Kapasite Hesabı Adımları:

  1. 1
    Tek bir pooler instance'ı: Her biri 15 bağlantılık 4 havuz için ham talep $4 imes 15 = 60$ backend'dir.
  2. 2
    Sınır denetimi: 2 kullanıcı rolü için $2 imes 30 = 60$ ve 2 veritabanı için $2 imes 35 = 70$ tavanlarına tam uyar ($min(60, 70, 60) = 60$).
  3. 3
    İki instance toplamı: $K imes 60 = 2 imes 60 = 120$ backend.
  4. 4
    Doğrudan işler eklendiğinde hesaplanan kapasite bütçesi: $120 + 12 = 132$ backend süreci!

Eğer PostgreSQL sunucunuzda max_connections = 50 ise, tüm havuzların eşzamanlı dolması durumunda ortaya çıkacak planlanan kapasite açığı 82 slot olarak belirlenir. Bu değer tekil bir anda 132 aktif soketin açıldığı veya kaçınılmaz bir sistem kilitlenmesi anlamına gelmez; tüm havuzların tepe doygunluğa ulaşması hâlinde planlanması gereken teorik bütçe tavanıdır.

Dengeli Koruma Tedbiri (`pgbouncer.ini`):

ini
[pgbouncer] default_pool_size = 4 ; 4 havuz x 4 = 16 backend / instance min_pool_size = 1 reserve_pool_size = 1 max_db_connections = 10 ; Veritabanı başına tavan max_user_connections = 10 ; Kullanıcı rolü başına tavan ; 2 instance x 16 = 32 pooler + 10 doğrudan = 42 <= 50 max_connections

▸5. İstemci Direnci ve Exponential Backoff Mühendisliği

Veritabanı anlık bir yük dalgasına girdiğinde veya havuz dolduğunda, yüzlerce uygulamanın aynı anda milisaniyelik aralıklarla tekrar bağlanmaya çalışması (Thundering Herd / Retry Storm) sistemi tamamen kilitler.

İstemci tarafında üç kritik dayanıklılık paterni uygulanmalıdır:

#### A. Truncated Exponential Backoff + Full Jitter Formülü Tüm istemcilerin aynı anda yeniden denemesini engellemek için gecikmeye rastgele bir sapma (jitter) eklenmelidir:

$$t_{\text{bekleme}} = \text{random}\left(0, \min(t_{\max}, t_{\text{taban}} \times 2^{\text{deneme}})\right)$$

Örnek Parametreler:

  • $t_{\text{taban}} = 100\text{ ms}$
  • $t_{\max} = 3000\text{ ms}$
  • Azami deneme: 3 kez (3 denemede bağlantı alınamazsa HTTP 503 dönerek hızlıca başarısız olun).

#### B. Connection Acquisition Timeout (Fail-Fast İlkesi) Uygulama havuzundan bağlantı alma süresi sınırsız olmamalıdır:

  • connectionTimeout = 250ms (Örnek SLA senaryo varsayımı; iş yükü SLO'nuza göre kalibre edilmelidir)
  • Bir web thread'i bu süre (örneğin senaryo varsayımı 250ms) içinde havuzdan bağlantı alamazsa kuyrukta dakikalarca beklememeli, HTTP 503 Service Unavailable dönerek arkasındaki kuyruğu boşaltmalıdır.

▸6. SRE Alarm Eşikleri ve Nöbetçi Eskalasyon Protokolü

Statik ve tek bir alarm eşiği yerine kademeli 3 seviyeli alarm matrisi işletilmelidir:

SeviyeTetikleyici KoşulDeğerlendirme PenceresiSorumluNöbetçi Aksiyonu
P3 - Warning$\frac{\text{Aktif Bağlantı}}{\max\_connections} > %70$for: 10mServis Ekibi (Slack)Pod artışı veya arka plan job zamanlamasını gözden geçir
P2 - Critical$\frac{\text{Aktif Bağlantı}}{\max\_connections} > %85$for: 3mNöbetçi SRE / DBA (PagerDuty)Rezerve slot ile pg_stat_activity bağlan; kilitli işlem veya sızıntı kontrolü yap
P1 - Emergency$\frac{\text{Aktif Bağlantı}}{\max\_connections} > %95$ veya FATAL: too many clients loglarıfor: 1mIncident Commander + SRE + DB LeadAcil tahliye: idle in transaction oturumları sonlandır, batch job'ları durdur

▸7. Nöbetçi SRE / On-Call Olay Müdahale Kartı (Runbook)

markdown
# Olay Müdahale Kartı: PostgreSQL Bağlantı Kapasitesi Tükenmesi - Şiddet Seviyesi: P1 / P2 - İlk Doğrulama Adımları: 1. Grafana panosundan gerçek backend sayısı (pg_stat_database.numbackends) ile PgBouncer istemci kuyruğunu (maxwait) karşılaştır. 2. Sorun veritabanı backend satürasyonu mu, yoksa PgBouncer kuyruk tıkanması mı? - Teşhis Komutu (Rezerve Süper Kullanıcı Slotu ile): psql -h db-primary -U db_admin -d postgres -c " SELECT count(*), state, wait_event_type FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY 2, 3;" - Acil Müdahale Seçenekleri: A. Hedefli Operatör Müdahalesi (DİKKAT: Toplu pg_terminate_backend kesinlikle otomatik cron veya ilk refleks olarak çalıştırılmamalıdır. Aktif transaction'ı anında ROLLBACK'e zorlar, veri işlem kaybına ve istemcide agresif yeniden bağlanma fırtınasına yol açar. Yalnızca kilit kuyruğunu tıkayan tekil PID izole edildikten sonra operatör kararıyla çalıştırılmalıdır): -- 1. Önce engellenen kilit PID'sini ve işlem süresini doğrula: SELECT pid, usename, client_addr, state, now() - xact_start AS xact_duration, query FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - xact_start > interval '2 minutes' ORDER BY xact_duration DESC LIMIT 5; -- 2. Yalnızca doğrulanmış ve sistem kilitlenmesine yol açan tekil hedef PID'yi sonlandır: SELECT pg_terminate_backend(<dogrulanmis_hedef_pid>); B. Eğer yeni deploy kaynaklı surge yaşanıyorsa: kubectl rollout pause deployment/<servis-adi> C. Eğer arka plan ETL havuzu tıkamışsa: ALTER ROLE etl_worker CONNECTION LIMIT 2; - Olay Sonrası Aksiyon: * Kapasite bütçe formülünü yeni pod sayılarıyla güncelle. * HPA azami sınırını ve Kubernetes maxSurge parametresini gözden geçir.

▸8. Sonraki Adım ve Tamamlayıcı İçerikler (Triad)

  • 🔍 Mimari Derin İnceleme ve Kapasite (dbaguide): Bağlantı yaşam döngüsü (fork() maliyeti), oturum durumları, max_connections ve ayrılmış slot sınırları ile iki katmanlı havuzlama mekaniği.

👉 PostgreSQL Bağlantı Yönetimi: Bağlantı Yaşam Döngüsü, Kapasite ve Havuzlama *(Acil durum müdahalesi için destekleyici runbook: PostgreSQL Bağlantı Tükenmesi Teşhisi)*

  • 🛠️ Araç ve Mimari Seçimi (dataxtools): HikariCP ile PgBouncer birlikte nasıl konumlandırılır, session vs. transaction modlarının sınırları nelerdir?

👉 Uygulama Havuzu ile PgBouncer Hangi İhtiyacı Çözer?


▸Resmî Kaynaklar ve İnceleme Notu

Topluluk Katkısı & Benchmark

Restore Tatbikatı & Kurtarma Deneyiminizi Paylaşın

Altyapınızdaki gerçek tatbikat sonuçlarını, RTO/RPO hedeflerinizi ve karşılaştığınız operasyonel engelleri anonim veya isimli olarak paylaşın. Sektör benchmark'ına katkıda bulunun.

3 SİTELİ TRİAD SERİSİ

Tamamlayıcı Ekosistem İçerikleri: PostgreSQL Bağlantı Kapasitesi ve Bağlantı Havuzu: Teşhis, Araç Seçimi ve SRE Ölçeklenme Bütçesi

Aynı mühendislik konusunu farklı açılardan (teşhis, araç seçimi, olay yönetimi) ele alan diğer dataiotech yayınları:

Bu mimariyi üretimde test etmek ister misiniz?

DataOpsphere CI/CD şablonlarını ve Docker Compose ortamlarını kopyalayıp hemen çalıştırabilirsiniz.