# VC-095 · Sunucusuz fonksiyon her istekte havuz açıyor ve bağlantıları tüketiyor

Her istek yeni bağlantı havuzu kuruyor veya aldığı bağlantıyı hata yolunda bırakmıyor. Trafik artınca veritabanı bağlantı sınırına ulaşıyor ve normal istekler de beklemeye başlıyor.

- Önem: ORTA. Etkisi orta. Trafik artınca ya da istek tekrarlanınca tetiklenir.
- Önem notu: Artan trafik altında bağlantıların tükenmesi temel alınır. Tek süreçli ve düşük hacimli uygulamada görülmeyebilir. Havuz boyutu her çalışan örnek için geçerli olduğundan toplam sınır ayrıca hesaplanır.
- Eksen ve kategori: Sağlamlık, 14 Performans ve ölçek
- Yığın: Node.js, Vercel, Supabase
- Yapay zekâ kodunda: ölçülmedi. Dayanak: uzman görüşü.
- Ne zaman bakılır: İlk yayından önce, Her ay
- CWE: CWE-404, CWE-770
- Checklist ifadesi: Sunucusuz fonksiyonlar veritabanı bağlantılarını havuzdan yeniden kullanıyor.
- Son inceleme: 4 Ekim 2026, Komünite editörlüğü
- Adres: https://vibecheck.komunite.com.tr/madde/sunucusuz-fonksiyon-baglanti-havuzunu-tuketiyor

## 60 saniyelik kontrol

Yalnız kendi uygulamanda ya da yazılı izin aldığın sistemde dene. Bu bir sızma testi değildir.

1. Pool oluşturma satırının istek işleyicisinin içinde olup olmadığını bul.
2. Alınan bağlantının başarı ve hata yollarında bırakıldığını kontrol et.
3. Uygulama havuzu ile sağlayıcı havuzunun sınırlarını ayrı kaydet.
4. Sunucusuz örnek sayısının toplam bağlantı bütçesine etkisini hesapla.

## Ne oluyor

Sunucusuz fonksiyon tek istekte sorunsuz çalışıyor. Veritabanına bağlanıyor, sorguyu yürütüyor ve yanıt dönüyor. Trafik arttığında aynı kodun çok sayıda çalışan örneği açılabileceğini düşünmeyince bağlantı bütçesi gözden kaçıyor. Her istek kendi havuzunu oluşturuyorsa havuz kullanmak bile toplam bağlantı sayısını sınırlamaya yetmiyor. Yeniden kullanım fırsatı kayboluyor.

Bir başka yol da alınan bağlantının hata sırasında bırakılmaması. Başarılı sorgudan sonra `release` çağrısı var, fakat sorgu hata verince o satıra ulaşılamıyor. Havuzun ayrılmış bağlantıları sonraki isteklere dönmüyor. Sağlıklı sorgular dahi bağlantı beklemeye başlıyor. Yeniden başlatma belirtileri geçici olarak silebilir, ancak aynı hata yolu yeniden çalışınca sorun döner.

Uygulama havuzu ve sağlayıcının bağlantı havuzu ayrı katmanlardır. Sıcak çalışan örnekte sonlu bir havuz kurmak yararlıdır. Bunun sınırı yalnız o örneğe aittir. Diğer fonksiyonlar, yönetim araçları ve çoğalan örnekler aynı veritabanı kapasitesini paylaşabilir. Bağlantı bütçesi dağıtımın tamamı üzerinden değerlendirilmelidir. Tek isteğin geçmesi kapasite kanıtı değildir.

## Gerçek olay

Bu madde belirli bir AI uygulamasının kesinti hikâyesini kullanmıyor. node-postgres belgesi, sınırsız havuz oluşturmanın havuz kullanımının amacını bozduğunu ve alınan istemcinin hata halinde de geri verilmesi gerektiğini açıkça anlatıyor. Tek sorguda `pool.query` bağlantı yaşam döngüsünü yönetmek için önerilen yoldur. [Bağlantı havuzu](https://node-postgres.com/features/pooling)

Supabase güncel bağlantı rehberi, sunucusuz ve edge iş yükleri için transaction kipindeki ortak havuzu öneriyor. Aynı belge uygulama istemcisinin modül düzeyinde yeniden kullanılmasını ve örnek başına küçük havuzla başlanmasını anlatıyor. [Bağlantı yöntemi seçimi](https://supabase.com/docs/guides/database/connecting-to-postgres) Bu öneriler belirli bir planın kapasitesine ilişkin garanti değildir. Örneğimiz Supabase veya Vercel üzerinde yük üretmez. Yerel olarak havuz oluşturma ve bağlantı bırakma davranışını denetler.

## Yapay zekâ bunu neden üretiyor

**Model işleyiciyi kendi başına tamamlar.** Ajan tek bir fonksiyon örneği yazarken bağlantının kuruluşunu da o fonksiyonun içine koyabilir. Kod taşınabilir ve anlaşılır görünür. Fakat çalışma ortamının aynı modülü sonraki isteklerde kullanabilmesi dikkate alınmaz. Yeni havuz her çağrıda yeniden açılır ve paylaşılan kaynak yönetimi örnekten düşer. Kuruluş ile istek yürütme arasındaki sınır ayrıca tarif edilmelidir.

**Başarılı yol bırakmayı yeterli gösterir.** Sorgu sonucunun hemen ardından gelen `release` satırı gözle yapılan incelemede doğru görünebilir. Ajan hata veren sorguyla aynı yolu çalıştırmadığında bağlantının içeride kaldığını fark etmeyebilir. Hata testinde yalnız dönen mesajı kontrol etmek de yetmez. Sonraki isteğin kaynak alabildiği ve ayrılan bağlantının geri verildiği gözlenmelidir.

**Yerel süreç bulutun çoğalmasını göstermez.** Tek geliştirme sunucusunda ölçülen bağlantı sayısı düşük kalabilir. Model bu gözlemi çok sayıda sıcak örneğe genellerse toplam bütçe yanlış hesaplanır. Her örnekte küçük havuz kullanmak iyi bir başlangıçtır, ancak örnek sayısı ve aynı veritabanını kullanan başka işler de hesapta yer almalıdır. Varsayılan ayarı değiştirmek bu hesabın yerini tutmaz.

**SDK adları bağlantı yollarını örtebilir.** Tarayıcıdan Data API çağrısı yapmakla sunucuda PostgreSQL sürücüsü açmak farklı davranışlardır. Ajan bütün veri erişimlerini aynı bağlantı modeliyle açıklayabilir. İncelemede gerçek sürücünün ve bağlantı adresinin belirlenmesi gerekir. Bunlar üretim sürecine ilişkin olası açıklamalardır. Belirli bir modelde ölçülmüş hata oranı veya yaygınlık sonucu sunmuyoruz.

## Etki

Bağlantı bekleyen istekler yanıt veremez. Aynı veritabanını kullanan başka uçlar da etkilenebilir ve kullanıcı bunu genel bir hizmet arızası olarak yaşar. Bekleyen işlerin yeniden denenmesi mevcut kuyruğa yeni iş ekleyebilir.

Bağlantı sınırını yalnız artırmak, sorguların kaynak tutma süresini veya sızıntıyı düzeltmez. Gereksiz bağlantılar belleği de tüketir. Kaynağı geri vermeme deseni CWE-404 altında tanımlanır. [CWE-404](https://cwe.mitre.org/data/definitions/404.html) Etki değerlendirmesi normal trafik altında oluşan sağlamlık sorununa odaklanır, saldırganın hizmeti durdurabildiğini ayrıca kanıtlamaz.

## Nasıl anlarsın

`new Pool`, `connect` ve `release` çağrılarını aynı akış üzerinde izle. Havuz istek işleyicisinin içinde mi kuruluyor? Sorgu hata verirse bağlantı bırakılıyor mu? Sağlayıcı panelinde etkin bağlantılar ve bağlantı bekleme belirtilerini mevcut trafikle birlikte değerlendir. Canlı sistemi bilerek sınırına kadar doldurma.

Örnekte sürücü yerine gözlenebilir bir sahte havuz kullanılır. Test normal sorguyu, hata sonrası bağlantı sayısını, sonraki isteği ve tek havuz kullanımını doğrular. Bu yöntem yaşam döngüsü hatasını ayırır. Gerçek TCP bağlantısı, sürücü kuyruğu veya bulut kapasitesi ölçülmüş sayılmaz.

## Nasıl düzeltirsin

1. **Kuruluşu ayır.** Fabrikayı sıcak örneğin modül başlangıcında bir kez çağır. Dönen işleyiciyi isteklerde yeniden kullan.
2. **Kaynağı bırak.** Alınan istemciyi `finally` içinde geri ver. Tek sorguda sürücünün `pool.query` yolunu tercih edebilirsin.
3. **Bütçeyi belirle.** Havuz boyutu ve bağlantı alma bekleme süresi sonlu olsun. Bu süre sınırını sorgunun toplam çalışma süresiyle karıştırma.
4. **Bağlantı kipini doğrula.** Transaction havuzunun prepared statement ve oturum durumu kısıtlarını kullandığın sürücüyle kontrol et. TLS sertifika doğrulamasını kapatma. [Sürücü TLS ayarları](https://node-postgres.com/features/ssl)

## Bir daha olmasın

Hata testinde bağlantının bırakıldığını da doğrula. Dağıtım modeli değiştiğinde toplam bağlantı bütçesini yeniden hesapla.

## Sınır

Yerel havuzun doğru kullanılması bütün dağıtımın sınırsız ölçeklenmesini sağlamaz. Uzun transaction, yavaş sorgu, ağ arızası ve platformun süreç yaşam döngüsü ayrıca incelenmelidir. Örnekteki fabrika her istekte çağrılırsa düzeltmenin amacı kaybolur. Sağlayıcı kök sertifikası güvenilir yapılandırmadan alınmalıdır. Havuzu her isteğin sonunda kapatmak yeniden kullanım sağlamaz, kapanış işlemi süreç yaşam döngüsüne aittir.

## Düzeltme kodları

### Node.js: Paylaşılan bağlantı havuzu

Önce:

```js
// db/okuma.js, açıklama amaçlı. Pool üretimde pg paketinden sağlanır.
export function createReader(Pool, connectionString, ca, report) {
  return async function read() {
    // Her istek ayrı havuz açıyor. Hata yolu bağlantıyı bırakmıyor.
    const pool = new Pool({ connectionString, max: 1,
      connectionTimeoutMillis: 1000, idleTimeoutMillis: 10000,
      ssl: { ca, rejectUnauthorized: true } });
    pool.on('error', () => report('DB_IDLE_ERROR'));
    const client = await pool.connect();
    const result = await client.query('SELECT 1 AS value');
    client.release();
    return result.rows[0].value;
  };
}
// createReader başlangıçta bir kez çağrılsa da havuz istek içinde oluşuyor.
```

Sonra:

```js
// db/okuma.js, açıklama amaçlı. Pool üretimde pg paketinden sağlanır.
export function createReader(Pool, connectionString, ca, report) {
  // Bu fabrika sıcak örneğin modül başlangıcında yalnız bir kez çağrılır.
  const pool = new Pool({ connectionString, max: 1,
    connectionTimeoutMillis: 1000, idleTimeoutMillis: 10000,
    ssl: { ca, rejectUnauthorized: true } });
  pool.on('error', () => report('DB_IDLE_ERROR'));
  return async function read() {
    const client = await pool.connect();
    try {
      const result = await client.query('SELECT 1 AS value');
      return result.rows[0].value;
    } finally { client.release(); }
  };
}
// Tek sorguda pool.query da bağlantı bırakma işini yönetir.
// Sertifikayı doğrulanmış yapılandırmadan al. Havuzu istek sonunda kapatma.
```

Düzeltmeyi kanıtlayan test:

```js
// db/okuma.test.mjs, açıklama amaçlı. Sahte havuz yaşam döngüsünü gözler.
import test from 'node:test';
import assert from 'node:assert/strict';
const { createReader } = await import(process.env.ORNEK_DOSYA);
test('sıcak örnek havuzu paylaşır ve sorgu hatasında istemciyi bırakır', async () => {
  let pools = 0, active = 0, fail = false, idleError;
  const reports = [];
  class Pool {
    constructor(options) { pools++; assert.equal(options.max, 1); }
    on(event, handler) { assert.equal(event, 'error'); idleError = handler; }
    async connect() {
      active++;
      return {
        query: async () => { if (fail) throw new Error('DB_TEST'); return { rows: [{ value: 1 }] }; },
        release: () => { active--; },
      };
    }
  }
  const read = createReader(Pool, 'test-only', 'test-ca', code => reports.push(code));
  assert.equal(await read(), 1);
  assert.equal(active, 0);
  fail = true;
  await assert.rejects(read(), /DB_TEST/);
  assert.equal(active, 0);
  fail = false;
  assert.equal(await read(), 1);
  assert.equal(pools, 1);
  idleError(new Error('Gizli ayrıntı'));
  assert.deepEqual(reports, ['DB_IDLE_ERROR']);
});
```

## Ajan kuralı (AGENTS.md)

```md
## Bağlantı havuzu tükeniyor (vibecheck VC-095)
- Havuzu sıcak çalışan örnekte yeniden kullan.
- Uygulama havuzunu sonlu tut.
- Alınan bağlantıyı her hata yolunda bırak.
- Sağlayıcı bağlantı kipini iş yüküne göre seç.
- Toplam örnek sayısını bağlantı bütçesine dahil et.
- Boştaki havuz hata olayını güvenli biçimde kaydet.
```

## Kaynaklar

1. [Connection pooling](https://node-postgres.com/features/pooling), node-postgres
2. [SSL](https://node-postgres.com/features/ssl), node-postgres
3. [Connect to your database](https://supabase.com/docs/guides/database/connecting-to-postgres), Supabase
4. [CWE-404 Improper Resource Shutdown or Release](https://cwe.mitre.org/data/definitions/404.html), MITRE
5. [CWE-770 Allocation of Resources Without Limits or Throttling](https://cwe.mitre.org/data/definitions/770.html), MITRE

---

vibecheck · Komünite editörlüğü. Metin CC BY 4.0, prompt ve kural parçaları MIT-0. Kaynak: https://vibecheck.komunite.com.tr/madde/sunucusuz-fonksiyon-baglanti-havuzunu-tuketiyor
