Node.js uygulamalarında PostgreSQL ile güvenli ve hızlı veri erişimi için bağlantı havuzu, transaction yönetimi, prepared statements ve sorgu optimizasyonu gibi pratik yaklaşımları örneklerle anlatıyoruz.
Node.js ve PostgreSQL: Bağlantı Havuzu, Transaction Yönetimi ve Performans İpuçları
Node.js uygulamalarında veritabanı erişimi, doğru yapılandırılmadığında uygulamanın en zayıf halkası haline gelir. Bu yazıda PostgreSQL ile Node.js entegrasyonunda sıklıkla göz ardı edilen ancak performans, güvenlik ve kararlılık açısından kritik olan bağlantı havuzu (connection pooling), transaction yönetimi, prepared statements ve sorgu optimizasyonu konularını ele alacağım. Hem ham pg modülü hem de popüler araçlarla (ör. knex, typeorm) uygulanabilecek pratik örnekler içerecek.
Neden bağlantı havuzu (connection pool) kullanmalısınız?
Her yeni veritabanı bağlantısı kurmak maliyetlidir: TCP el sıkışması, kimlik doğrulama ve oturum oluşturma süreleri gibi gecikmeler vardır. Özellikle yüksek trafikli Node.js sunucularında her istekte yeni bağlantı açmak veritabanı ve uygulama üzerinde ciddi yük yaratır.
Bağlantı havuzu; belirli sayıda bağlantıyı baştan açar ve istekler arası bu bağlantıları yeniden kullanır. Böylece bağlantı oluşturma maliyeti ortadan kalkar, eşzamanlılık daha iyi yönetilir ve veritabanı üzerindeki toplam bağlantı sayısı kontrol altında tutulur.
pg ile doğru pool kullanımı
PostgreSQL için en yaygın kullanılan Node.js kütüphanesi pg'dir. Basit bir pool örneği ve dikkat edilmesi gereken noktalar aşağıdadır:
<!-- JavaScript kodu, HTML'ye uygun şekilde kaçışlı -->
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // maksimum eşzamanlı bağlantı
idleTimeoutMillis: 30000, // boşta kalan bağlantıların kapanma süresi
connectionTimeoutMillis: 2000 // yeni bağlantı için zaman aşımı
});
// Sorgu örneği
async function getUsers() {
const { rows } = await pool.query('SELECT id, name FROM users WHERE active = $1', [true]);
return rows;
}
Püf noktaları: pool'u modül dışı tek bir örnek olarak tutun (singleton). Her istekte yeni Pool yaratmayın; aksi halde havuz avantajını kaybedersiniz ve veritabanına aşırı bağlantı açılır.
Transaction yönetimi: ACID ve hata kurtarma
Birden fazla ilgili sorguyu tek bir birimde yürütmeniz gerektiğinde transaction kullanmalısınız. Node.js'de transactionları yönetirken dikkat edilmesi gereken en önemli konu, hata durumunda rollback yapıp bağlantıyı doğru şekilde serbest bırakmaktır.
<!-- JavaScript kodu -->
async function transferFunds(fromId, toId, amount) {
const client = await pool.connect();
try {
await client.query('BEGIN');
const res1 = await client.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [amount, fromId]);
const res2 = await client.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [amount, toId]);
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw err; // üst katmana hatayı ilet
} finally {
client.release();
}
}
Dikkat: uzun süren transactionlar veritabanı kilitlerine sebep olabilir. Mümkün olduğunca transaction içindeki işlemleri minimal tutun ve dışarıdaki IO (ör. HTTP çağrıları) gibi bloklayıcı işleri transaction dışında yapın.
Prepared statements ve SQL injection'a karşı pratik yöntemler
Hazırlanmış ifadeler (prepared statements) veritabanı tarafında sorgu planlarını yeniden kullanır ve parametre bağlama yoluyla SQL injection riskini azaltır. pg kütüphanesi parametre bağlamayı destekler ve her sorguda $1, $2 gibi yer tutucular kullanabilirsiniz.
<!-- JavaScript kodu -->
// Güvenli parametre bağlama
const text = 'SELECT * FROM products WHERE category = $1 AND price < $2';
const values = ['book', 30];
const res = await pool.query(text, values);
Ayrıca sorgu tekrar eden bir yapıdaysa named prepared statements kullanarak performans kazanabilirsiniz; ancak çoğu uygulama için parametreli sorgular yeterlidir. ORM'ler (TypeORM, Prisma) veya query builder'lar (Knex) de ORM katmanında güvenli parametre bağlama sağlar.
ORM vs Query Builder vs Ham SQL: hangi durumda ne tercih etmeli?
Seçim projenin ihtiyaçlarına göre değişir. ORM'ler (ör. TypeORM, Prisma) veri modeli, migration yönetimi ve ilişkiler için kolaylık sağlar. Ancak karmaşık SQL optimizasyonlarında ORM sizi sınırlayabilir.
Query builder (Knex) hem SQL üzerinde kontrol hem de güvenli parametre bağlama sağlar; migration ve seed desteği de sunar. Ham SQL ise maksimum kontrol ve performans gerektiğinde tercih edilir. Genelde pragmatik yaklaşım: CRUD için ORM veya query builder, karmaşık rapor/analitik sorgular için ham SQL kullanmaktır.
Sorgu performansını artırmak için pratik ipuçları
- İndeks kullanımı: WHERE, JOIN ve ORDER BY kullandığınız sütunları analiz edip uygun indeksi ekleyin. Ancak gereksiz indeks yazma; yazma maliyeti artar.
- Limit ve sayfalama: Büyük result setleri client tarafına taşımayın. OFFSET << büyük sayılarda yavaş olabilir; cursor veya keyset pagination düşünün.
- Batched işlemler: Toplu yazma/güncelleme işlemlerini tek sorguda batchleyin veya COPY komutunu kullanın.
- Explain Analyze: Karmaşık sorgular için EXPLAIN ANALYZE kullanarak planı inceleyin ve darboğazları tespit edin.
Sık Sorulan Sorular
1. Pool'un max değerini nasıl belirlemeliyim?
Bu değer uygulamanızın eşzamanlı istek sayısına, veritabanı sunucusunun kapasitesine ve diğer uygulamaların aynı veritabanını kullanıp kullanmadığına bağlıdır. Genelde 10-50 arası başlangıç noktası uygundur; load test ile en iyi değeri bulun.
2. Uzun süren sorguların yarattığı problemleri nasıl azaltırım?
Karmaşık sorguları parçalara ayırın, doğru indeksler ekleyin ve gerektiğinde sorguyu yeniden yazın. Büyük veri için arşivleme stratejisi uygulamayı düşünün. Sorgu izleme ve EXPLAIN ANALYZE en büyük yardımcınız olacaktır.
3. ORM kullanırken performans sorunları yaşarsam ne yapmalıyım?
Önce problemi profil ile tespit edin: hangi sorgular yavaş? ORM'nin ürettiği SQL'i inceleyin ve gerekirse o kısmı ham SQL ile optimize edin. Ayrıca lazy/eager yüklemeleri kontrol edin; gereksiz ilişki yüklemelerini kapatın.
Sonuç
Node.js ile PostgreSQL entegrasyonunda bağlantı havuzu, transaction yönetimi, prepared statements ve sorgu optimizasyonu uygulamanın performansını ve güvenilirliğini doğrudan etkiler. Pool'u tek örnek olarak kullanmak, transactionları düzgün yönetmek, parametreli sorgularla güvenliği sağlamak ve gerektiğinde ORM ile ham SQL arasındaki pragmatik dengeyi kurmak gerçek dünyada sorunsuz üretim ortamları için anahtardır. Bunları adım adım uygulayıp, load test ve izleme araçlarıyla desteklediğinizde veri katmanınız hem hızlı hem de dayanıklı olur.
