Ana Sayfa / Blog / Database connection pool sizing: varsayılanlar neden yanlış

Database connection pool sizing: varsayılanlar neden yanlış

Her framework'ün connection pool default'u farklı ve genelde optimize değil. 19 yıllık deneyimden nasıl doğru değer hesaplıyorum.

Database connection pool sizing konusuna tecrübeli bir developer bile kafadan karar verir. “Pool size 20 olsun, yeter herhalde.” Pratikte bu yaklaşım düşük load’da sorun çıkarmıyor ama yük artınca garip performance problemleri başlıyor.

Son 10 yılda onlarca proje’de connection pool tuning yaptım. Bu yazıda pratik bir hesap yöntemini ve yaygın yanılgıları anlatacağım.

Klasik yanılgı: “ne kadar çok o kadar iyi”

İlk karşılaştığın reflex: “user traffic artacağına pool’u büyüteyim, güvenli olsun.” Pool size 20’den 200’e çıkarıyorsun. Performance genelde düşüyor.

Sebep: çoğu production database (PostgreSQL, MySQL) connection başına bir process/thread açıyor. Her connection memory, CPU context switching, lock contention demek. 200 connection’lu bir PostgreSQL server’ı 20 connection’lı olana göre daha yavaş çalışıyor.

Benchmarks gösteriyor ki PostgreSQL’de 8-16 CPU core’lu bir server için optimal connection sayısı 20-40 arası. 100’e çıkardığında throughput düşüyor.

HikariCP formülü

Hikari (Java’nın popular connection pool’u) dokumentasyonunda buldum, sonra her framework’te geçerli olduğunu gördüm:

pool_size = ((core_count * 2) + effective_spindle_count)

Core count: DB server’ın CPU sayısı. Effective spindle count: disk sayısı (SSD’de 1 çekirdek yeter).

8 CPU + SSD sunucu için: (8*2) + 1 = 17. Yuvarla, 20.

Bu formül “başlangıç noktası”. Load pattern’ına göre 15’e de indirebilirsin, 30’a da çıkarabilirsin.

Gerçek dünya hesabı

Uygulama sunucun 3 instance’lı. Her instance’in pool’u 20. DB tarafında görülen connection: 60.

DB server’ın izin verdiği max_connections 100. Geriye 40 connection rezerv kalıyor (admin bağlantısı, bakım tool’ları, read replica sync vb).

Ölçek 3’ten 5 instance’a çıktığında: 5*20=100, DB limit’ine ulaşıyor. Connection allocation failure başlıyor.

Bu yüzden pool size’ı total server count’a göre düşün:

per_instance_pool = (db_max_connections * 0.7) / app_instance_count

Yani 5 instance’lı, DB max 100 ise per instance 14 pool. Reserve %30.

Connection lifetime ve leak detection

Pool size dışında önemli bir konu: connection lifetime. Pool default’u genelde “sonsuz”. Bu bug’ları saklıyor:

  • Connection leak: kod DB connection aldı, return etmedi. Sessiz sessiz pool exhaust oluyor.
  • Stale connection: network timeout, firewall drop. Connection pool’da görünüyor ama gerçekte ölü.
  • Database failover: primary değişti ama pool eski bağlantıları tutuyor.

Fix:

max_connection_lifetime = 30 dakika
idle_timeout = 10 dakika
connection_validation_interval = 30 saniye

Bu ayarlarla:
– Her connection 30 dakikada bir recycle oluyor
– 10 dakika idle olan connection’lar kapanıyor
– Her 30 saniyede connection health check (SELECT 1)

Connection leak’leri de izliyoruz: her get connection + “kimin aldığı” stack trace. 30 saniyede return edilmeyen connection’larda alert.

Read/write split

Bir projede pool size sorunu farklı bir şekilde çıkıyor: read-heavy workload.

Read replica’nız varsa, read query’leri ayrı pool’dan gelsin:

  • Write pool: 20 connection, primary’a bağlı
  • Read pool: 60 connection, replica’ya bağlı (birden fazla replica varsa her biri)

Read query’ler daha hızlı olduğundan pool dönüş hızı da yüksek, daha çok connection’ı handle edebiliyor.

Long-running query’ler özel handle

Analytics veya raporlama sorguları 30+ saniye sürüyor mu? Bunlar için ayrı pool:

  • Main pool: 20 connection, OLTP workload
  • Analytics pool: 5 connection, OLAP/report workload

Böylece bir raporun uzun query’si main uygulamanın connection pool’unu bloke etmiyor. Dedicated connection var.

Bazı durumlarda analytics için tamamen ayrı replica kullanmak daha iyi. Raporlar replica’dan gider, OLTP primary’de kalır.

Connection pool metrics

Production’da mutlaka monitor etmen gereken:

  1. Active connections: Şu an kullanılan sayı
  2. Idle connections: Bekleyen sayı
  3. Pool saturation: Active / Max
  4. Wait time: Connection almak için beklenen süre
  5. Timeout count: Connection timeout yüzünden fail olan request
  6. Leak detection alerts: Return edilmeyen connection stack trace’leri

HikariCP, PgBouncer gibi pool’lar bu metric’leri export ediyor. Datadog, Grafana dashboards.

Pool saturation %90’a yaklaşırsa pool büyüt veya DB load’u azalt.

PgBouncer gibi external pooler

PostgreSQL’de bir abstraction layer: PgBouncer. Uygulama → PgBouncer → Database. Benefits:

  • Uygulamanızın connection’ı PgBouncer’a gidiyor (fast)
  • PgBouncer DB’ye daha az connection tutuyor (connection multiplexing)
  • Database’te daha az process/memory overhead

Benim kullanım örneğim: 10 uygulama instance’ı her biri pool size 20. Database’de 200 connection açılmış olur. PgBouncer ile 10 instance * 20 = 200 connection PgBouncer’a, ama PgBouncer DB’ye 20-30 connection açıp multiplex ediyor.

Downside: PgBouncer’ın transaction pooling mode’unda bazı PostgreSQL feature’ları çalışmıyor (prepared statement cache, session-level vars). Session mode kullanırsan multiplex avantajı çıkıyor.

Pratik başlangıç değerleri

Yeni projede ne ile başlayayım?

  • Small app (1-2 instance, <100 concurrent user): pool size 10
  • Medium (3-5 instance, 500-1000 user): pool size 20
  • Large (10+ instance, 5000+ user): per instance 10-15, total DB connection count’a bak, gerekirse PgBouncer
  • Analytics workload: ayrı pool, size 5-10

Sonra load test et. Benchmark.js veya k6 ile. Connection pool saturation, wait time grafik’leri. Gerçek datayla tune et.

Troubleshooting

Connection pool’da problem çıktığında sormam gereken sorular:

  1. Pool saturation var mı? Active > %80 ise büyüt.
  2. Leak var mı? Long-running connection’lar var mı? Stack trace ekle.
  3. DB load mu yüksek? Pool’u büyütmek çözüm değil, DB çöker. Önce query optimize et.
  4. Timeout’lar mı artıyor? Pool size yeterli ama DB yavaş. DB profiling zamanı.
  5. Connection aging problem mi? Stale connection varsa lifetime/idle timeout ayarla.

Sonuç

Connection pool size magic number değil, hesap yapabileceğin bir değer. HikariCP formülüyle başla, DB max_connection’a dikkat et, instance count’u göz önünde bulundur. Read/write split, analytics split, external pooler bunlar büyük sistemlerde ek araçlar.

Yeni projede default 10-20 ile başla, monitoring kur, gerçek datayla tune et. “Pool size 200 olsun güvenli olsun” deme, çünkü genelde tam tersi etkiyi yaratıyor.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç