İçeriğe geç
academia.sh

Ders 05 / 15

Zaman Aşımı ve Yeniden Deneme Bütçesi

Uçtan uca gecikme eşiğinin zincirdeki adımlara bölüştürülmesi: zaman aşımı ile deneme sayısının çarpımının en kötü durumda bütçeyi kaç katına çıkardığının hesaplanması, son tarih aktarımının eşik aşımını erken başarısızlığa çevirmesi, kısa zaman aşımının geçici yavaşlığı soğurması ve deneme bütçesi konmadığında yaygın yavaşlamada iç istek hızının kaça katlandığının modelle ölçülmesi.

İçindekiler

Dört ders sonunda çağrı yapılabilir durumda: servisler durumsuz, adresleri çalışma zamanında bulunuyor, konuşma biçimi seçilmiş ve sözleşmeleri kırılmadan evrilebiliyor. Bir soru hâlâ açık ve o soru bu konunun ilk ölçüsüne geri döner. Ağ geçidi iki servisi çağırdığında birinden yanıt gelmezse ne kadar bekleyecek, kaç kez deneyecek ve bu bekleme uçtan uca yanıta ne ekleyecek.

Yeniden deneme kararı, üstel geri çekilme, sarsıntı ve devre kesici Uygulama Mimarisi kursunda kuruldu; algoritmalar burada yeniden anlatılmaz. Bu dersin ele aldığı şey bir bütçedir. Bütçenin kaynağı K01’dir: takip okumasının sıkı eşiği 200 milisaniye ve o eşiğin kaynağı alıcının takip sayfasından beklediği yanıt süresidir. Zincirdeki adım sayısı da ölçülmüştü — üçüncü ders bir dış istek için iki iç çağrı saydı ve bunun protokolden değil alan bölünmesinden geldiğini gösterdi.

Bütçenin Bölüştürülmesi

Zincir şudur: ağ geçidi iki servisi paralel çağırır, her servis kendi deposunu çağırır. Derinlik 2, kol 2, toplam dört adım çağrısı. Ağ geçidinin kendi işi de bir pay ister; bu bu kursun varsayımıdır.

U3 — ağ geçidinin kendi payı 20 ms. Gerekçe: doğrulama, kapı denetimleri ve iki yanıtın birleştirilmesi K02’de kenara taşınmıştı ve bu işler bütçeden yer tutar. Duyarlılığı aşağıda hesaplandı; K01’in tablosuna eklenmez.

// zincir/butce.mjs — uctan uca butcenin zincire bolusturulmesi; belirlenimli aritmetik
const UCTAN_UCA = 200;      // K01 Islevsel Olmayan Gereksinimler: okuma esigi (siki) 200 ms
const GECIT_PAYI = 20;      // varsayim U3: ag gecidinin kendi isine ayirdigi pay (ms)
const ADIM_ORTANCA = 8;     // model parametresi: bir adim cagrisinin olagan suresi (ms)
const kalan = UCTAN_UCA - GECIT_PAYI;

console.log(`uctan uca ${UCTAN_UCA} ms, gecidin payi ${GECIT_PAYI} ms, adimlara kalan ${kalan} ms\n`);
console.log(`${"derinlik".padStart(9)}${"deneme".padStart(8)}${"butcesiz en kotu(ms)".padStart(22)}` +
  `${"butceye oran".padStart(14)}${"bolusturulmus zaman asimi(ms)".padStart(31)}${"ortancaya oran".padStart(16)}`);
for (const k of [1, 2, 3, 4]) {
  for (const d of [1, 3]) {
    const butcesiz = k * d * UCTAN_UCA + GECIT_PAYI;
    const t = kalan / (k * d);
    console.log(`${String(k).padStart(9)}${String(d).padStart(8)}${String(butcesiz).padStart(22)}` +
      `${`x${(butcesiz / UCTAN_UCA).toFixed(1)}`.padStart(14)}${t.toFixed(1).padStart(31)}` +
      `${`x${(t / ADIM_ORTANCA).toFixed(2)}`.padStart(16)}`);
  }
}
const enDerin = Math.floor(kalan / (3 * ADIM_ORTANCA));
console.log(`\ndeneme 3 ve adim ortancasi ${ADIM_ORTANCA} ms iken zaman asimi ortancanin altina ` +
  `dusmeden en cok ${enDerin} adim derinlige inilebilir`);
console.log(`duyarlilik: gecit payi ${GECIT_PAYI} -> ${GECIT_PAYI * 2} ms olsaydi derinlik 2, deneme 3 icin ` +
  `zaman asimi ${(kalan / 6).toFixed(1)} -> ${((UCTAN_UCA - 2 * GECIT_PAYI) / 6).toFixed(1)} ms`);
uctan uca 200 ms, gecidin payi 20 ms, adimlara kalan 180 ms

 derinlik  deneme  butcesiz en kotu(ms)  butceye oran  bolusturulmus zaman asimi(ms)  ortancaya oran
        1       1                   220          x1.1                          180.0          x22.50
        1       3                   620          x3.1                           60.0           x7.50
        2       1                   420          x2.1                           90.0          x11.25
        2       3                  1220          x6.1                           30.0           x3.75
        3       1                   620          x3.1                           60.0           x7.50
        3       3                  1820          x9.1                           20.0           x2.50
        4       1                   820          x4.1                           45.0           x5.63
        4       3                  2420         x12.1                           15.0           x1.88

deneme 3 ve adim ortancasi 8 ms iken zaman asimi ortancanin altina dusmeden en cok 7 adim derinlige inilebilir
duyarlilik: gecit payi 20 -> 40 ms olsaydi derinlik 2, deneme 3 icin zaman asimi 30.0 -> 26.7 ms

Bu sayılar hesap sınıfındadır. Üçüncü kolon en sık yapılan hatayı gösteriyor: her adıma uçtan uca eşiğin kendisi zaman aşımı olarak verilirse, derinlik 2 ve deneme 3’te en kötü süre 1220 milisaniyedir — bütçenin 6,1 katı. Zaman aşımını eşiğe eşitlemek bütçeyi korumaz; her adım tek başına bütçenin tamamını harcayabilir hâle gelir.

Beşinci kolon doğru bölüştürmeyi veriyor ve bir sınır getiriyor. Adıma düşen zaman aşımı, derinlik ve deneme sayısının çarpımına bölünür: derinlik 2 ve deneme 3’te 30 milisaniye, derinlik 4’te 15 milisaniye. Son kolon bunun neden bir sınır olduğunu söylüyor — zaman aşımı adımın olağan süresine yaklaştıkça olağan istekler de zaman aşımına uğramaya başlar. Sekiz milisaniyelik bir adım ortancasında ve üç denemede bütçe en çok yedi adım derinliğe dayanır.

Bu, servis bölünmesi ile gecikme eşiği arasındaki bağdır: zincire eklenen her adım bütçeyi böler, sonsuza kadar bölünemez.

Model

Aşağıdaki koşum bir modeldir: mantıksal saat kullanır, adım süreleri bir döngüden okunur ve yavaşlama parametreyle tetiklenir. İki rejimde çalışır — olağan rejimde adımların çoğu hızlıdır ve arada bir yanıt vermeyen bir adım geçer; yaygın yavaşlama rejiminde adımların çoğu yanıt vermez. Gerçek bir servis kümesi ya da ağ kurulmaz.

// zincir/kosum.mjs — zincirin MODELI: mantiksal saat, adim sureleri parametre, iki rejim.
// Zincir: ag gecidi -> (teslimat-op, ucretlendirme) paralel -> her biri kendi deposu. Derinlik 2, kol 2.
const N = 40, ESIK = 200, GECIT_PAYI = 20, DERINLIK = 2, KOL = 2, ADIM = DERINLIK * KOL;
const REJIM = {                                   // model parametresi: adim cagrisinin suresi (ms)
  olagan: [6, 7, 6, 8, 6, 7, 6, 45, 6, 7, 6, 8, 6, 7, 6, 1000],
  "yaygin yavaslama": [6, 1000, 1000, 1000, 6, 1000, 1000, 1000],
};

function kosum({ zamanAsimi, deneme, sonTarih, denemeButcesi }, rejim) {
  let s = 0, birincil = 0, ek = 0;
  const sayim = { yanit: 0, esikDisi: 0, dusen: 0 }, uctanUca = [];
  const sure = () => REJIM[rejim][s++ % REJIM[rejim].length];
  const izin = () => denemeButcesi === null || ek < denemeButcesi * birincil;

  const adim = (kalan) => {                       // tek adim: zaman asimi ve yeniden deneme
    let gecen = 0;
    for (let i = 0; i < deneme; i += 1) {
      const zt = sonTarih ? Math.min(zamanAsimi, kalan - gecen) : zamanAsimi;
      if (zt <= 0) return { ok: false, gecen };
      if (i === 0) birincil += 1; else ek += 1;
      const su = sure();
      if (su <= zt) return { ok: true, gecen: gecen + su };
      gecen += zt;
      if (i + 1 < deneme && izin() === false) return { ok: false, gecen };
    }
    return { ok: false, gecen };
  };
  const kol = () => {                             // bir kol: DERINLIK adet ardisik adim
    let gecen = 0, ok = true;
    for (let d = 0; d < DERINLIK && ok; d += 1) {
      const r = adim(ESIK - GECIT_PAYI - gecen);
      gecen += r.gecen; ok = r.ok;
    }
    return { ok, gecen };
  };

  for (let i = 0; i < N; i += 1) {
    const kollar = Array.from({ length: KOL }, kol);   // paralel: sure kollarin en buyugudur
    const gecen = GECIT_PAYI + Math.max(...kollar.map((k) => k.gecen));
    uctanUca.push(gecen);
    if (kollar.every((k) => k.ok) === false) sayim.dusen += 1;
    else if (gecen > ESIK) sayim.esikDisi += 1;
    else sayim.yanit += 1;
  }
  const sirali = [...uctanUca].sort((a, b) => a - b);
  return { ...sayim, ortanca: sirali[N >> 1], enKotu: sirali[N - 1],
    carpan: (birincil + ek) / (N * ADIM) };
}

const YAPILANDIRMA = [
  ["butce yok (200/3)", { zamanAsimi: 200, deneme: 3, sonTarih: false, denemeButcesi: null }],
  ["+ son tarih aktarimi", { zamanAsimi: 200, deneme: 3, sonTarih: true, denemeButcesi: null }],
  ["+ bolusturulmus (30/3)", { zamanAsimi: 30, deneme: 3, sonTarih: true, denemeButcesi: null }],
  ["+ deneme butcesi 0.30", { zamanAsimi: 30, deneme: 3, sonTarih: true, denemeButcesi: 0.3 }],
];
const OKUMA_TEPE = 416.67, IC_TABAN = OKUMA_TEPE * ADIM;   // K01: tepe okuma; 03. ders: adim sayisi

console.log(`${N} dis istek, derinlik ${DERINLIK}, kol ${KOL}, adim ${ADIM}; ` +
  `uctan uca esik ${ESIK} ms (K01), gecit payi ${GECIT_PAYI} ms (U3)`);
console.log(`ic istek/s tabani = ${OKUMA_TEPE} x ${ADIM} = ${IC_TABAN.toFixed(2)}`);
for (const rejim of Object.keys(REJIM)) {
  console.log(`\n-- ${rejim}: ${REJIM[rejim].filter((v, i, a) => a.indexOf(v) === i).join("/")} ms dongusu --`);
  console.log(`${"yapilandirma".padEnd(24)}${"esik ici".padStart(9)}${"esik disi".padStart(10)}` +
    `${"dusen".padStart(7)}${"ortanca".padStart(9)}${"en kotu".padStart(9)}${"carpan".padStart(8)}` +
    `${"ic istek/s".padStart(12)}`);
  for (const [ad, y] of YAPILANDIRMA) {
    const r = kosum(y, rejim);
    console.log(`${ad.padEnd(24)}${String(r.yanit).padStart(9)}${String(r.esikDisi).padStart(10)}` +
      `${String(r.dusen).padStart(7)}${String(r.ortanca).padStart(9)}${String(r.enKotu).padStart(9)}` +
      `${`x${r.carpan.toFixed(2)}`.padStart(8)}${(IC_TABAN * r.carpan).toFixed(2).padStart(12)}`);
  }
}
console.log(`\nsinirsiz denemenin ust siniri: her adim 3 deneme -> ${(IC_TABAN * 3).toFixed(2)} istek/s`);
40 dis istek, derinlik 2, kol 2, adim 4; uctan uca esik 200 ms (K01), gecit payi 20 ms (U3)
ic istek/s tabani = 416.67 x 4 = 1666.68

-- olagan: 6/7/8/45/1000 ms dongusu --
yapilandirma             esik ici esik disi  dusen  ortanca  en kotu  carpan  ic istek/s
butce yok (200/3)              30        10      0       71      233   x1.06     1770.85
+ son tarih aktarimi           30         0     10       71      200   x1.00     1666.68
+ bolusturulmus (30/3)         40         0      0       62       63   x1.14     1895.85
+ deneme butcesi 0.30          40         0      0       62       63   x1.14     1895.85

-- yaygin yavaslama: 6/1000 ms dongusu --
yapilandirma             esik ici esik disi  dusen  ortanca  en kotu  carpan  ic istek/s
butce yok (200/3)               0         0     40      626      626   x2.00     3333.36
+ son tarih aktarimi            0         0     40      200      200   x0.67     1114.59
+ bolusturulmus (30/3)          0         0     40      116      116   x2.00     3333.36
+ deneme butcesi 0.30           0         0     40       86       86   x0.97     1614.60

sinirsiz denemenin ust siniri: her adim 3 deneme -> 5000.04 istek/s

Dört Satırın Okunması

Son tarih aktarımı eşik aşımını erken başarısızlığa çevirir. Olağan rejimde bütçesiz düzen kırk isteğin otuzunu eşik içinde yanıtlıyor, onunu eşiğin dışında — en kötü 233 milisaniye. Çağıran kalan bütçeyi çağrıyla birlikte geçirdiğinde en kötü süre tam 200 milisaniyede kesiliyor ve o on istek eşik dışı olmaktan çıkıp düşen istek oluyor. Değiş tokuş açıktır: geç bir yanıt ile zamanında bir başarısızlık arasında seçim yapılıyor. Kullanıcının 233 milisaniyede aldığı yanıt zaten eşiği karşılamıyordu; onu üretmek için harcanan iş de karşılıksızdı.

Kısa zaman aşımı geçici yavaşlığı soğuruyor. Bölüştürülmüş düzende zaman aşımı 30 milisaniyeye indiğinde kırk isteğin kırkı eşik içinde yanıtlanıyor ve en kötü süre 63 milisaniye oluyor. Nedeni modelin varsayımıdır: yavaşlık geçicidir, aynı adımın ikinci çağrısı hızlı döner. Uzun zaman aşımı bu geçici yavaşlığı bekleyerek uçtan uca yanıta taşıyor, kısa zaman aşımı ise onu kesip yeniden deniyor. Karşılığı çağrı çarpanının 1,00’den 1,14’e çıkması: iç istek hızı 1666,68’den 1895,85 istek/s’ye yükseliyor.

Deneme bütçesi olağan rejimde bağlamıyor. Dördüncü satır üçüncüyle aynı. Gözlenen çarpan 1,14 ve bütçe 1,30 olduğu için sınır hiç devreye girmiyor. Bir bütçenin olağan koşulda görünmez olması doğru boyutlandırıldığının işaretidir.

Yaygın Yavaşlamada Ne Değişiyor

İkinci tablo aynı yapılandırmaları adımların çoğunun yanıt vermediği bir rejimde koşturuyor ve sıralama tersine dönüyor.

Bütçesiz düzende kırk isteğin kırkı düşüyor ve çağıran bunu öğrenmek için 626 milisaniye bekliyor. Son tarih aktarımı bu süreyi 200 milisaniyede kesiyor; çarpan 0,67’ye iniyor, çünkü bütçe tükendiğinde yapılmayan birincil çağrılar da var. İç istek hızı 3333,36’dan 1114,59 istek/s’ye düşüyor.

Üçüncü satır dersin en önemli sayısıdır. Zaman aşımı 30 milisaniyeye indirildiğinde uçtan uca süre 116 milisaniyeye iniyor — ama çarpan yeniden 2,00’ye çıkıyor ve iç istek hızı 3333,36 istek/s oluyor. Kısa zaman aşımı, aynı bütçeye daha çok deneme sığdırır; zaten yavaşlamış olan servis, kendisini yavaşlatan sistemden iki kat istek alır. Olağan rejimde iyi olan karar, arıza rejiminde zararlıdır.

Dördüncü satır bunu kesiyor. Deneme bütçesi ek denemeleri birincil çağrıların yüzde otuzuyla sınırladığında çarpan 0,97’ye, iç istek hızı 1614,60 istek/s’ye iniyor: iki kat yükseltme yerine taban hızın altında bir yük. Uçtan uca süre de 116’dan 86 milisaniyeye iniyor, çünkü boşa giden denemeler yapılmıyor. Sınırsız denemenin üst sınırı karşılaştırma noktasıdır — her adım üç kez denenirse 5000,04 istek/s, K01’in uçtaki tepe hızının neredeyse on katı bir iç yük.

Bütün bunlar yalnız yeniden denenebilir çağrılar için geçerlidir. Durum değiştiren bir çağrının yeniden denenebilmesi, aynı çağrının iki kez işlenmesinin tek kez işlenmesiyle aynı sonucu vermesine bağlıdır; bu özelliğe etkisiz (idempotent) denir ve HTTP yöntemlerinin etkisizlik özelliğiyle aynı kavramdır. Etkisiz olmayan bir çağrı ancak istemcinin ürettiği bir tekillik anahtarı sözleşmeye yazılmışsa yeniden denenebilir; ikisi de Web API Tasarımı kursunda kuruldu.

Özet

  • Uçtan uca eşik bir bütçedir ve zincire bölüştürülür; her adıma eşiğin tamamı zaman aşımı olarak verilirse derinlik 2 ve deneme 3’te en kötü süre bütçenin 6,1 katına çıkar.
  • Bölüştürülmüş zaman aşımı derinlik ile deneme sayısının çarpımına bölünür (derinlik 2, deneme 3’te 30 ms) ve adımın olağan süresine yaklaştığında sınıra dayanır: 8 ms ortanca ve 3 denemeyle bütçe en çok 7 adım derinliğe dayanır.
  • Son tarih aktarımı en kötü süreyi eşikte kesiyor: olağan rejimde 233 ms ve 10 eşik dışı yanıt, 200 ms ve 10 düşen isteğe dönüşüyor — geç yanıt yerine zamanında başarısızlık.
  • Kısa zaman aşımı geçici yavaşlığı soğuruyor (40/40 eşik içi, en kötü 63 ms) ve bedeli çağrı çarpanının 1,00’den 1,14’e, iç istek hızının 1666,68’den 1895,85 istek/s’ye çıkmasıdır.
  • Yaygın yavaşlamada aynı karar tersine dönüyor: kısa zaman aşımı çarpanı 2,00’ye ve iç istek hızını 3333,36 istek/s’ye çıkarıyor; deneme bütçesi 0,30 bunu 0,97 ve 1614,60 istek/s’ye indiriyor ve uçtan uca süreyi 116’dan 86 ms’ye düşürüyor.
  • Yeniden deneme yalnız etkisiz çağrılar için serbesttir; etkisiz olmayan bir çağrı ancak sözleşmede bir tekillik anahtarı varsa denenebilir.

Sonraki Adım

Bu konu eşzamanlı çağrıyı tasarladı: çağıran bekler, bütçe işler, sonunda ya yanıt gelir ya hata. Bütçe hesabı bir sınırı görünür kıldı — zincir uzadıkça ya bütçe parçalara bölünüp her adım daralır ya da uçtan uca süre büyür. İki seçenek de aynı varsayımın içindedir: çağıran işin sonucunu bekliyor. Bazı işler için üçüncü bir yol vardır ve o yol varsayımı kaldırır — çağıran hiç beklemez. Taşıyıcıdan gelen durum olayının deponun içine yazılması, isteği yanıtlamak için tamamlanmak zorunda değildir. Sonraki konu isteği bir kuyruğa bırakmanın ne değiştirdiğini ölçer ve bunu bir kapasite sorusu olarak sorar: eşzamanlı yolda kapasite anlık tepeye göre seçilmek zorundadır, kuyruk konduğunda ortalamaya yaklaştırılabilir; kazancın bekleyen iş ve bekleme süresi cinsinden ne ödettiği ve K01’in hangi sayısının değiştiği orada hesaplanır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat