Geçen ay İstanbul’da bir fintech ekibinin PostgreSQL sunucusuna baktığımda tablo tanıdıktı. CPU fena değil, disk idare eder, ama bağlantı sayısı şişmiş. İşin komiği, sorguların çoğu zaten çalışmıyordu bile — sistem resmen bağlantı yönetimine takılıp kalmıştı. Hani bazen problemi veritabanında sanırsın… meğer mesele kapıda bekleyen kalabalıkmış.
Dürüst olmak gerekirse, PostgreSQL tarafında her yeni bağlantının bir maliyeti var. Küçük bir maliyet değil bu; bellek yiyor, süreç açıyor, kimlik doğrulama yapıyor, TLS varsa sistem ekstra nefes nefese kalıyor. Açık konuşayım — ilk (belki yanılıyorum ama) bakışta “birkaç yüz bağlantı ne olacak ki” diyorsunuz,. Iş prod ortamına gelince rakamlar tokat gibi geliyor.
Asıl sorun: Çok bağlantı = daha çok hız değil
Birçok ekip “too many connections” hatasını görünce içgüdüsel olarak max_connections değerini yükseltiyor. Ben de 2023 yazında Ankara’daki bir SaaS projesinde buna benzer bir refleks gördüm; ekip önce limiti artırdı, sonra RAM kullanımı zipladı. Asıl darboğaz daha görünür hale geldi. Siz hiç denediniz mi? Semptomu susturdular, hastalık içeride büyümeye devam etti.
Şöyle söyleyeyim, İşin aslı şu: PostgreSQL’de her istemci bağlantısı arka tarafta ayrı bir iş yükü yaratıyor. Her biri kabaca birkaç MB bellek tüketiyor; sayı 200’e çıktığında sadece bağlantı altyapısı için gigabaytlar konuşmaya başlıyorsunuz. 500 bağlantıda ise sistemin önemli bir kısmı sorgu çalıştırmadan önce. Harcanmış oluyor, bu da hem cache için ayrılan alanı daraltıyor hem uygulama performansını domino taşı gibi etkiliyor.
Bir şey dikkatimi çekti: Burada kritik nokta şu: çok sayıda uygulama bağlantısı ile az sayıda veritabanı bağlantısı aynı şey değil. Pooling tam olarak bunu ayırıyor. Uygulamanız yüzlerce istek atabilir ama arkada veritabanına bağlı gerçek oturum sayısı sabit kalır… işte “sihir” dediğimiz şey biraz bu.
Neden özellikle modern mimarilerde patlıyor?
Mikroservis tarafında olay daha da karmaşıklaşıyor (yanlış duymadınız). Diyelim ki 20 container replica var ve her biri 10 açık DB bağlantısı tutuyor — hop, 200’e çıktınız bile. Rolling deployment sırasında eski sürüm kapanmadan yenisi ayağa kalkarsa sayı anlık olarak ikiye katlanabiliyor. Bir de üstüne serverless fonksiyonları eklerseniz… orası tam vahşi batı.
Bunu yazarken aklıma geçen kış Köln’de görüştüğüm bir ekip geldi. AWS Lambda tabanlı küçük iş akışları vardı ve her tetikleme yeni bir PostgreSQL oturumu açıyordu (kendi tecrübem). Trafik hafifken sorun yoktu, ama kampanya gününde “bağlantılar niye tükendi?” sorusu ortalığa düştü. Cevap basitti: uygulama ölçeklenmişti, veritabanının nefesi kesilmişti.
Peki neden?
Bağlantı açmanın gizli faturası
TLS el sıkışması var, kimlik doğrulama var, katalog verisi yükleme var. Bunların hepsi bedava değil. Yeni bir oturum açmak bazen 50-100 ms kaybettiriyor ve tekil kullanıcı bunu hissetmeyebilir, ama saniyede yüzlerce kez olunca durum değişiyor. Açıkçası burada en sinir bozucu şey gecikmeden çok dalgalanma — yani sistem stabil görünürken aniden tökezliyor.
Kısacası mesele sadece hız değil, öngörülebilirlik meselesi de var.
Peki pooler ne yapıyor?
Connection pooler’ın yaptığı şey kaba haliyle şu: uygulamanın önüne oturup “ben senin adına veritabanıyla sınırlı sayıda ilişki kurarım” diyor. Uygulamada bin tane istemci olabilir ama arkada mesela sadece 20-30 gerçek DB bağlantısı dolaşır. Bu yüzden yoğun saatlerde bile kaynak kullanımı daha kontrollü olur.
Bana göre bunun en büyük avantajı ani trafik artışlarında ortaya çıkıyor. En çok da e-ticaret ya da B2B SaaS’ta kampanya saatlerinde uygulama trafiği zıp diye yükselirken database’i duvara toslatmıyorsunuz (ki bu çoğu kişinin gözünden kaçıyor). Kağıt üstünde süper olan şey pratikte gerçekten işe yarıyor mu? Çoğu zaman evet — ama doğru ayarlanırsa.
| Sorun | Poolsuz yapı | Pooling sonrası |
|---|---|---|
| Açık DB oturumu | Saat başına hızla artar | Sabit veya kontrollü kalır |
| Bellek baskısı | Yüksek | Daha düşük |
| Kampanya/ani trafik riski | Zayıf dayanıklılık | Daha güvenli marj |
| TLS el sıkışması yükü | Sık tekrar eder | Daha seyrek yaşanır |
| “too many connections” | Sık görülür | Ciddi biçimde azalır |
Tercihler masası: PgBouncer mı Supavisor mı built-in mi?
Yani, Lafı gevelemeden söyleyeyim: tek doğru yok. Küçük startup ile enterprise ihtiyacı aynı olmuyor. İnternetin Bağışıklık Sistemi: AI Güvenlikte Yeni Oyun yazımızda bu konuya da değinmiştik. Daha fazla bilgi için PDF Dünyasında Bir Nefes: Ücretsiz ve Limitsiz Araçlar yazımıza bakabilirsiniz.
İşte tam da bu noktada devreye giriyor.
PgBouncer neden hâlâ standart?
PgBouncer yıllardır sahnede ve boşuna değil. Hafif, hızlı ve özellikle transaction pooling modunda oldukça etkili çalışıyor. Küçük ekiplerde kurulumu nispeten kolay, operasyon yükü de makul seviyede kalıyor. Geçen sene İzmir’de danışmanlık verdiğim bir projede sadece PgBouncer’a geçerek idle connection sayısını dramatik biçimde düşürdük; ekip yeni sunucu almadan rahatladı.
Supavisor nerede öne çıkıyor?
Supabase dünyaine yakın duran ekipler için Supavisor bayağı mantıklı görünüyor. Yönetim tarafında modern bir hissiyat veriyor, özellikle cloud-native çalışan takımlarda işleri sadeleştirebiliyor. Ama açık söyleyeyim — her senaryoda sihirli değnek değil. Bazı ekipler için fazla yeni ya da fazla “ekosistem bağımlılığı” kokusu verebilir. Bu konuyla ilgili 3 Katmanlı Design Token Neden Önemli? yazımıza da göz atmanızı tavsiye ederim.
Kısa bir not düşeyim buraya. Bu konuyla ilgili Google Meet, Slack ve RAG ile Toplantı Hafızası Kurmak yazımıza da göz atmanızı tavsiye ederim.
Built-in yaklaşım ne kadar yeterli?
PostgreSQL’in kendi içinde sunduğu bazı mekanizmalar başlangıç için iş görebilir, ama klasik pooler kadar esnek değiller. Küçük test ortamlarında tamamdır, hatta “idare eder” bile diyebilirim —. Yüksek trafikte genelde dışarıdan bir pooler istiyorsunuz (bizzat test ettim). Kurumsal tarafta ise observability, failover davranışı ve routing gibi ihtiyaçlar devreye girince built-in seçeneklerin sınırı hemen belli oluyor.
En büyük hata çoğu zaman yanlış araç seçmek değil; aracın hangi modda çalışacağını hiç düşünmemek oluyor. Aynı pooler ayarı küçük startup’ta uçurabilirken enterprise ortamda sessizce sorun çıkarabiliyor. Kullanım modelini bilmeden yapılan kurulumlar genelde ilk trafik dalgasında çatlıyor. Dur bir saniye — aslında önce ihtiyacı ölçmek lazım. Trafik deseni yoksa çözüm de havada kalır. Evet. Bu kadar net.
Neyi ölçmezseniz orada kaybolursunuz
- Açık toplam bağlantılar kaç tane?
- Bunların kaçı idle ya da idle in transaction durumda?
- Trafik piklerinde oran nasıl değişiyor?
“`sql
SELECT
state,
usename AS username,
application_name,
count(*) AS connection_count,
count(*) FILTER (WHERE state = ‘idle’) AS idle_connections,
count(*) FILTER (WHERE state = ‘idle in transaction’) AS idle_in_transaction
FROM pg_stat_activity
WHERE backend_type = ‘client backend’
GROUP BY state, usename, application_name
ORDER BY connection_count DESC;
“`
Analizde beni en çok şaşırtan şeylerden biri genelde idle in transaction kayıtları oluyor — valla güzel iş çıkarmışlar —. Çünkü bunlar görünürde hiçbir şey yapmıyormuş gibi duruyor ama kilit tutuyor, snapshot tutuyor, pool slot yutuyor. Bir süre sonra performans probleminin kökü SQL’den çok uygulama davranışı çıkıyor (ki bu çoğu kişinin gözünden kaçıyor)
Bir başka kontrol de max_connections kullanım oranı. Ben %70 üstünü gördüğümde kulağımı dikerim; %80 civarıysa artık alarm çalmaya başlar — çünkü deployment anında veya kısa süreli burst’te işler çığrından çıkabiliyor. Daha fazla bilgi için Butterfly CSS: 2026’da Dikkat Çeken Hafif Bir Seçenek yazımıza bakabilirsiniz.
“`sql
SELECT
count(*) AS active_connections,
current_setting(‘max_connections’)::int AS max_connections,
round(100.0 * count(*) / current_setting(‘max_connections’)::int, 1) AS utilization_percentage
FROM pg_stat_activity
WHERE backend_type = ‘client backend’;
“`
İşin garibi, Hele idle in transaction beş dakikayı geçmişse orada ciddi hata kokusu vardır. Uygulamanın transaction açıp unuttuğu yerleri yakalamak çoğu zaman performans kazancından daha değerlidir.
Neyse uzatmayayım: ölçüm olmadan pooling seçimi biraz göz kapalı araba sürmeye benziyor.
Küçük startup ile enterprise arasında fark nerede?
Kulağa basit geliyor, ama pratikte olay değişiyor. Küçük startup’ta öncelik hızlı kurulum ve düşük operasyon yükü olur. Orada PgBouncer çoğu zaman yeterlidir; hatta fazla bile gelebilir. Bir servisiniz varsa basit konfigürasyonla yol alırsınız, özel routing gerekmiyorsa işi çözer gidersiniz.
- Küçük takım mı? Önce basit tutun.
- Büyüyorsanız observability şart.
- Eğer serverless kullanıyorsanız pooling neredeyse zorunlu hale gelir.
- Kilitlenme sorunları varsa kodu da inceleyin.
Enterprise ortamında ise replica sayısı, failover davranışı ve compliance detayları önem kazanır (evet, doğru duydunuz). Bu bölümde kişisel kanaatim net: enterprise dünyasında pooling yalnızca performans optimizasyonu değildir, aynı zamanda operasyon güvenliği aracıdır. Yedeklilikle birlikte düşünülmelidir.
Sahada gördüğüm iki tip hata açıklaması nasıl okunmalı?
Birincisi klasik “too many connections”. Bu mesaj size doğrudan şunu söyler: limit doldu. Bu kadar basit görünen mesajın arkasında aslında kötü connection lifecycle yönetimi yatabilir.
İkincisi ise RAM baskısıyla gelen sessiz bozulmadır. Sistem tamamen çökmeyebilir, fakat shared_buffers küçülür, cache hit oranları oynar, sorgular tuhaf şekilde yavaşlar. Kullanıcı tarafında hissedilen şey genelde “bugün niye ağır?” olur, halbuki kök neden gün boyu büyüyen oturum sayısıdır.
Ben olsam ilk yapılacaklar listesini şöyle kurarım:
pg_stat_activityüzerinden state dağılımını çıkar.idle in transactionkalan client’ları bul.- Aynı servisin kaç farklı replica ile bağlandığını kontrol et.
max_connectionsartırmadan önce gerçekten ihtiyaç olup olmadığına bak.- Zaten büyüyorsa pooler konumlandırmasını planla — geciktirmeye gerek yok.
Sıkça Sorulan Sorular
PostgreSQL connection pooling nedir?
Ne yalan söyleyeyim, Connection pooling, uygulamaların veritabanına doğrudan sınırsız şekilde bağlanması yerine sınırlı sayıdaki gerçek PostgreSQL bağlantısını paylaşmasını sağlar. Böylece bellek kullanımı düşer ve “too many connections” hataları azalır. Peki bunu neden söylüyorum? Burada, en çok da yoğun trafikli sistemlerde ciddi fark yaratır.
PgBouncer neden bu kadar popüler?
PgBouncer hafif olduğu için seviliyor. Kurulumu kolay, işletmesi görece sakin ve çoğu senaryoda yeterince güçlü. Hele bir de küçük ve orta ölçekli projelerde hızlı sonuç verdiği için tercih ediliyor.
Supavisor hangi durumlarda mantıklı?
Peki, bir şey dikkatimi çekti: Supabase odaklı çalışan ekiplerde veya modern cloud yapılarında mantıklı olabilir. Yönetim deneyimi hoş, özellikle platform uyumu açısından iyi hissettiriyor. Ama mevcut stack’iniz farklıysa entegrasyon maliyetini ayrıca düşünmek gerekir.
max_connections artırmak çözüm mü?
Kısmen çözüm gibi görünür ama çoğu zaman geçici pansuman olur. Daha fazla bağlantı demek daha fazla bellek tüketimi demektir. Sorunun kök nedeni connection lifecycle ise limit artırmak sadece problemi erteler.
Kaynaklar ve İleri Okuma
PostgreSQL Resmi Dokümantasyonu — Bağlantılar ve Ayarlar
PgBouncer Konfigürasyon Dokümantasyonu
Supabase Poolers Rehberi (Supavisor)
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



