# VC-083 · Başarısız çağrı beklemeden ve deneme sınırı olmadan tekrarlanıyor

Dış servis hata verince uygulama aynı çağrıyı hemen yeniden gönderiyor. Deneme sayısı ve bekleme bütçesi bulunmadığında toparlanması gereken servise daha fazla yük biniyor ve kullanıcı işi uzuyor.

- Önem: ORTA. Etkisi orta. Trafik artınca ya da istek tekrarlanınca tetiklenir.
- Önem notu: Yük altında geçici hata veren salt okunur bağımlılığın aralıksız çağrıldığı akış temel alınır. Ortak bağımlılık veya yüksek maliyetli çağrıda etki artar. Yazma tekrarının çift işlem oluşturması ayrıca değerlendirilir.
- Eksen ve kategori: Sağlamlık, 12 Hata yolları ve dış çağrılar
- Yığın: Node.js, Her yığın
- Yapay zekâ kodunda: ölçülmedi. Dayanak: uzman görüşü.
- Ne zaman bakılır: İlk yayından önce, Her ay
- CWE: CWE-400
- Checklist ifadesi: Başarısız çağrılar sınırlı sayıda ve artan beklemeyle tekrarlanıyor.
- Son inceleme: 4 Ekim 2026, Komünite editörlüğü
- Adres: https://vibecheck.komunite.com.tr/madde/tekrar-denemede-bekleme-ve-ust-sinir-yok

## 60 saniyelik kontrol

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

1. Retry döngülerinde ilk çağrı dahil toplam deneme sayısını bul.
2. Geçici hata sonrası sonraki çağrıdan önce gerçekten beklenip beklenmediğini kontrol et.
3. Kalıcı hata ve çağıran iptalinin yeniden denenmediğini doğrula.
4. SDK ve uygulamanın aynı işi ayrı ayrı tekrar edip etmediğini incele.
5. Yerel testte sürekli geçici hata üret ve deneme sınırında durduğunu ölç.

## Ne oluyor

Dış katalog servisi geçici hata döndürüyor. Uygulama kullanıcı işini tamamlamak için çağrıyı yeniden deniyor. İlk bakışta bu makul bir dayanıklılık davranışı. Ancak kod hata aldığı anda aynı çağrıyı tekrar gönderiyor ve ne zaman vazgeçeceğini bilmiyor. Servisin toparlanması gereken sırada uygulama ona kesintisiz yeni iş yolluyor.

Tek kullanıcıyla yapılan denemede servis sonraki çağrıda cevap verebilir. Aynı anda bekleyen çok sayıda iş olduğunda her biri aynı döngüye girer. Aralıkların aynı olması da çağrıların birlikte geri dönmesine yol açabilir. Bir hatayı düzeltmek için eklenen tekrar, hatanın sürdüğü süre boyunca daha fazla yük üreten bir mekanizmaya dönüşür.

Sorun yalnız sonsuz döngü değildir. Uygulama birkaç kez denerken alttaki SDK her çağrıyı kendi içinde tekrar edebilir. Kodda görünen sayı gerçek dış istek sayısını yansıtmaz. Kullanıcının beklediği toplam süre de her çağrının süresiyle aralardaki beklemelerin birleşimidir. Deneme sayısı, bekleme ve toplam süre aynı işin bütçesi içinde anlaşılmalıdır. Başarılı yanıt gelene kadar çalışmak açık bir bütçe değildir.

## Gerçek olay

Bu madde belirli bir kesintiyi bu döngüye bağlamıyor. [Microsoft Retry rehberi](https://learn.microsoft.com/en-us/azure/architecture/patterns/retry), geçici hatalar için uygun aralık ve sonlu deneme kullanmayı anlatır. Yoğun servise saldırgan biçimde tekrar göndermenin yükü artırabileceğini, iç içe tekrarların beklemeyi uzatabileceğini ve yazma işlemlerinde tekrar güvenliğinin değerlendirilmesi gerektiğini belirtir.

Yerel örnek bir okuma işini temsil eder. İlk çağrılar yapay geçici hata verir, sonraki çağrı sonuç döndürür. Test gerçek çağrı sayısını ve istenen bekleme aralıklarını kontrol eder. Bekleme fonksiyonu testte sanaldır. Duvar saati süresi, ağ kapasitesi veya üretim servisi ölçülmez. [Node zamanlayıcı API'si](https://nodejs.org/api/timers.html) iyi örneğin olağan çalışma yolundaki bekleme aracıdır.

## Yapay zekâ bunu neden üretiyor

**Geçici hata isteği genel döngüye dönüşür.** Ajan bir entegrasyonun bazen hata verdiğini öğrenince çağrıyı döngüye alabilir. Tekrarın hangi hata türünde işe yarayacağını belirlemek yerine her hatada yeniden çağırmak daha kısa kod üretir. Fakat yanlış girdi veya iptal gibi durumlarda aynı işlemi tekrarlamak çözüm getirmez.

**Deneme sayısı ile tekrar sayısı karışır.** Bir ayar tekrar sayısını, başka katman toplam çağrı sayısını ifade edebilir. Ajan bu farkı belirtmediğinde beklenen bütçeye ek bir çağrı girebilir. Örnekte ilk çağrı toplam sayıya dahildir. Bu küçük sözleşme testte açıkça yer alır ve sonraki düzenlemelerde korunur.

**SDK davranışı görünmez kalır.** Ajan yalnız uygulama fonksiyonunu okuyabilir. Kullanılan istemcinin kendi tekrar politikasını görmeden yeni bir sarmalayıcı eklerse aynı hata birkaç katmanda ele alınır. Her katman tek başına makul görünse de bütün akışın maliyeti farklıdır. İnceleme gerçek dış çağrıyı yapan katmana kadar sürmelidir.

**Hızlı test beklemeyi kaldırır.** Geliştirme denemesinin uzun sürmemesi için gecikme azaltılabilir veya tamamen kaldırılabilir. Bu geçici seçim örnekte kalınca canlı davranış da değişir. Testte zamanı sanallaştırmak ile üründe beklemeyi kaldırmak ayrı işlemlerdir. Bunlar olası üretim nedenleridir. AI araçlarının bu arızayı ne sıklıkta ürettiğine ilişkin ölçüm iddiası yoktur.

## Etki

Kullanıcı aynı iş için daha uzun bekleyebilir ve sonunda yine hata görebilir. Dış servise giden çağrı sayısı artarken uygulamanın açık işleri kaynak tutar. Maliyetli API'lerde başarısız işin deneme maliyeti de birikir. Ortak bağımlılık etkilenirse başka kullanıcıların normal işleri de gecikebilir. Gerçek büyüklük trafik ve sağlayıcı davranışına bağlıdır.

Yazma çağrısında ayrı bir tehlike vardır. Yanıt gelmemiş olsa bile uzaktaki işlem tamamlanmış olabilir. Aynı yazıyı yeniden göndermek ikinci kayıt veya ikinci yan etki oluşturabilir. Bu nedenle örnek yalnız salt okunur ve kendi süresi sınırlı bir işlem varsayar. Yazma tekrarının güvenliği bu küçük döngüyle kanıtlanmaz.

## Nasıl anlarsın

Retry yardımcılarını ve SDK ayarlarını birlikte oku. İlk çağrı dahil en fazla kaç dış istek oluşabilir? Hangi hata geçici sayılıyor? Bekleme fonksiyonu gerçekten bekleniyor mu, yoksa çağrılıp sonucu unutuluyor mu? Çağıran iptal ettiğinde yeni denemeler devam ediyor mu? Sağlayıcının bekleme şartı varsa hangi katman uyguluyor?

Yerel testte sürekli geçici hata üret. İş belirlenmiş deneme sayısında son hatayı dışarı vermelidir. Ardından birkaç hatadan sonra başarı döndüren yol dene. Bekleme aralıklarının uygulandığını kontrol et. Kalıcı hata ve iptalde tek çağrı yeterli olmalıdır. İlk denemede başarı durumunu da tutarak gereksiz bekleme eklenmediğini doğrula.

## Nasıl düzeltirsin

1. **Tekrarın sahibini seç.** SDK uygun ve belgelenmiş politika sağlıyorsa onu yapılandır. Üstüne bağımsız tekrar katmanı eklemeden bütün çağrı sayısını hesapla. Hata türünü tanımayan genel yardımcıya her işi verme.
2. **Deneme bütçesini açık yaz.** Örnekte toplam üç çağrı vardır. Son başarısız denemeden sonra yeniden beklenmez. Bu sayı evrensel öneri değildir, açıklama için seçilmiştir. Kullanıcı akışının kaldırabileceği süreye göre karar ver.
3. **Aralığı uygula.** İyi örnek artan beklemeye küçük rastgele sapma ekler. Test sabit rastgelelik kaynağıyla hesaplanan aralıkları denetler. Sağlayıcı `Retry-After` gerektiriyorsa bu örneği aynen kullanma, süre şartını ve toplam bütçeyi bağdaştırıcıda işle.
4. **Hata ve iptali koru.** Yalnız güvenilen bağdaştırıcının geçici saydığı hata tekrar edilir. Kalıcı hata ve iptal dışarı çıkar. Üretimde bekleme sırasında da çağıran iptalini taşı, her çağrının süre sınırını ve bütün işin toplam bütçesini uygula.

## Bir daha olmasın

Tekrar politikası değişince sürekli hata ve toparlanma deneylerini yeniden çalıştır. Gerçek dış çağrı sayısını uygulamadaki görünür döngü sayısından ayrı izle.

## Sınır

Bu madde tekrarın sayısını ve aralığını kapsar. Tek çağrının zaman aşımı, devre kesici, yazma tekilliği ve kapasite planı ayrı konulardır. Küçük örnek bekleme sırasında iptal veya sağlayıcıya özgü hata başlıklarını uygulamaz. İşin gerektirdiği, sonlu ve gerekçeli anlık tekrar tek başına açık sayılmaz.

## Düzeltme kodları

### Node.js: Sınırlı ve aralıklı okuma tekrarı

Önce:

```js
// servis/yeniden-oku.js, açıklama amaçlı. read süre sınırlı ve salt okunurdur.
import { setTimeout as sleep } from 'node:timers/promises';
export async function retryRead(read, { wait = sleep, random = Math.random } = {}) {
  // Servis düzelene kadar hemen yeniden dene.
  for (;;) {
    try {
      return await read();
    } catch (error) {
      if (error?.retryable !== true || error.name === 'AbortError') {
        throw error;
      }
      // Deneme bütçesi veya bekleme uygulanmıyor.
    }
  }
}
```

Sonra:

```js
// servis/yeniden-oku.js, açıklama amaçlı. read süre sınırlı ve salt okunurdur.
import { setTimeout as sleep } from 'node:timers/promises';
export async function retryRead(read, { wait = sleep, random = Math.random } = {}) {
  const maxAttempts = 3; // İlk çağrı bu sayıya dahildir.
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try { return await read(); }
    catch (error) {
      if (error?.retryable !== true || error.name === 'AbortError') throw error;
      if (attempt === maxAttempts) throw error;
      const jitter = random();
      if (!Number.isFinite(jitter) || jitter < 0 || jitter >= 1) {
        throw new Error('Geçersiz rastgelelik kaynağı');
      }
      const delay = 100 * 2 ** (attempt - 1) + Math.floor(jitter * 50);
      await wait(delay);
    }
  }
}
// retryable kararı güvenilen servis bağdaştırıcısından gelir.
// Sağlayıcının Retry-After şartı varsa bu küçük örnek genişletilmelidir.
// Yazma tekrarının güvenliği ve toplam iptal bütçesi ayrı kurulmalıdır.
```

Düzeltmeyi kanıtlayan test:

```js
// servis/yeniden-oku.test.mjs, açıklama amaçlı. Bekleme sanal, çağrı sayısı gerçek.
import test from 'node:test';
import assert from 'node:assert/strict';
const { retryRead } = await import(process.env.ORNEK_DOSYA);
const transient = () => Object.assign(new Error('Yapay geçici hata'), { retryable: true });
test('geçici hata aralıklı ve sınırlı denenir, kalıcı hata hemen çıkar', async () => {
  let calls = 0;
  const delays = [];
  const value = await retryRead(async () => {
    if (++calls < 3) throw transient();
    return 'Yapay sonuç';
  }, { wait: async ms => delays.push(ms), random: () => 0.5 });
  assert.equal(value, 'Yapay sonuç');
  assert.equal(calls, 3);
  assert.deepEqual(delays, [125, 225]);
  calls = 0;
  await assert.rejects(retryRead(async () => {
    if (++calls > 6) throw new Error('Test güvenlik sınırı');
    throw transient();
  }, { wait: async () => {}, random: () => 0 }), /Yapay geçici hata/);
  assert.equal(calls, 3);
  for (const error of [new Error('Kalıcı hata'), Object.assign(transient(), { name: 'AbortError' })]) {
    calls = 0;
    await assert.rejects(retryRead(async () => { calls++; throw error; }), e => e === error);
    assert.equal(calls, 1);
  }
  calls = 0;
  assert.equal(await retryRead(async () => { calls++; return 'İlk başarı'; }), 'İlk başarı');
  assert.equal(calls, 1);
});
```

## Ajan kuralı (AGENTS.md)

```md
## Yeniden deneme yükü artırıyor (vibecheck VC-083)
- Yalnız güvenle tekrarlanabilen işi yeniden dene.
- İlk çağrı dahil toplam deneme sayısını sınırla.
- Geçici hatalarda aralığı artır ve uygun rastgele sapma ekle.
- Kalıcı hata ve çağıran iptalini tekrar etme.
- SDK tekrarlarıyla uygulama tekrarlarının çarpılmasını engelle.
- Çağrı süresi, bekleme ve sağlayıcı bekleme şartını birlikte bütçele.
```

## Kaynaklar

1. [Retry pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/retry), Microsoft
2. [Node.js Timers Promises API](https://nodejs.org/api/timers.html), Node.js
3. [CWE-400: Uncontrolled Resource Consumption](https://cwe.mitre.org/data/definitions/400.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/tekrar-denemede-bekleme-ve-ust-sinir-yok
