İçeriğe geç
academia.sh

Ders 17 / 20

Dayanıklı Durum Nesneleri

Aynı anahtar için tek yazarlı bir nesne modeli koşturulur: eşzamanlı isteklerde kayıp güncelleme sıfıra iner. Karşılığında gelen iki kısıt sayılır — tek örneğin istek başına sıraya girmesi ve nesnenin coğrafi olarak sabitlenmesi — ve sayaç, oda, kilit senaryolarında hangisinin uyduğu yazılır.

İçindekiler

Önceki dersin deposu bir şeyi çözmeden bıraktı. Yayılma penceresi okumaları eski değere bağlıyordu; iki uç aynı anahtarı aynı anda değiştirmek isterse ne olur? Kurgudaki bölgesel ölçüm ağında bunun karşılığı somuttur: bir şubenin doğrulanmış okuma sayacı, aynı anda iki farklı uçtan gelen kayıtla artırılmak isteniyor.

“Oku, değiştir, yaz” üç ayrı adımdır ve aralarına başkası girebilir. Bu dersin ölçtüğü şey bu araya girmenin kaç güncelleme düşürdüğü, ve onu sıfıra indiren modelin karşılığında ne istediğidir. İki model aynı iş yüküyle koşar.

KE19. Paylaşılan depo modelinde okuma 20 ms, hesap 1 ms, yazma 64 ms sürer; üçü ayrı adımdır. KE20. Varışlar deterministiktir; rastgelelik yoktur. KE21. Tek yazarlı nesnede aynı anahtar tek bir örneğe yönlendirilir, istekler varış sırasına göre kuyruğa girer, durum örneğin belleğinde tutulur ve istek başına işleme 6 ms’dir; koşut yürütme yoktur.

Aynı Anahtara İki Model

İlk model tanıdıktır: değer uzak bir depoda durur, her istemci okur, artırır, geri yazar. İkinci modelde anahtar bir örneğe bağlanır — aynı anahtar için ağda tek bir örnek vardır ve bütün istekler oraya sıraya girer. Adım artık üç değil, birdir.

// olcum-agi/tekyazar.mjs — ayni anahtar iki modelde: paylasilan depo (oku-degistir-yaz)
// ve tek yazarli nesne (sirali isleme). MODEL: varislar ve sureler deterministiktir.

const ISTEMCI = 8, ADIM = 50, TOPLAM = ISTEMCI * ADIM;
const OKU_MS = 20, HESAP_MS = 1, YAZ_MS = 64;      // paylasilan depo yolu
const ISLEM_MS = 6;                                 // tek ornekte istek basina isleme

// --- 1. Paylasilan depo: her istemci sirayla oku-degistir-yaz yapar.
const islem = [];
for (let c = 0; c < ISTEMCI; c++) {
  let t = c * 3;
  for (let i = 0; i < ADIM; i++) {
    islem.push({ okuT: t, yazT: t + OKU_MS + HESAP_MS + YAZ_MS, okunan: 0 });
    t = t + OKU_MS + HESAP_MS + YAZ_MS;
  }
}
const olay = [];
for (const o of islem) { olay.push({ t: o.okuT, oku: true, o }); olay.push({ t: o.yazT, oku: false, o }); }
olay.sort((a, b) => a.t - b.t || (a.oku ? -1 : 1));
let deger = 0, ustuneYazilan = 0;
for (const e of olay) {
  if (e.oku) { e.o.okunan = deger; continue; }
  if (deger !== e.o.okunan) ustuneYazilan++;
  deger = e.o.okunan + 1;
}
console.log(`${ISTEMCI} istemci x ${ADIM} artirma = ${TOPLAM} istek, ayni anahtar (model)`);
console.log("paylasilan depo (oku-degistir-yaz)");
console.log(`  beklenen ${TOPLAM}   sayacin son degeri ${deger}   ` +
  `kayip guncelleme ${TOPLAM - deger}   %${(((TOPLAM - deger) / TOPLAM) * 100).toFixed(1)}`);
console.log(`  ustune yazilan yazma ${ustuneYazilan}   islem suresi ${OKU_MS + HESAP_MS + YAZ_MS} ms`);

// --- 2. Tek yazarli nesne: ayni anahtar tek ornege gider, istekler sirayla islenir.
function kuyruk(istemci, araMs) {
  const varis = [];
  for (let c = 0; c < istemci; c++)
    for (let i = 0; i < ADIM; i++) varis.push(c * 3 + i * araMs);
  varis.sort((a, b) => a - b);
  let bos = 0, sayac = 0, bekleme = [];
  for (const v of varis) {
    const basla = Math.max(v, bos);
    bekleme.push(basla - v);
    bos = basla + ISLEM_MS;
    sayac++;                                        // durum bellekte, adim atomik
  }
  bekleme.sort((a, b) => a - b);
  const p95 = bekleme[Math.ceil(0.95 * bekleme.length) - 1];
  const ort = bekleme.reduce((t, v) => t + v, 0) / bekleme.length;
  return { istek: varis.length, sayac, hiz: (istemci * 1000) / araMs, ort, p95,
           sure: (bos - varis[0]) / 1000 };
}
const TAVAN = 1000 / ISLEM_MS;
console.log(`\ntek yazarli nesne (istek basina ${ISLEM_MS} ms, tavan ${TAVAN.toFixed(1)} istek/sn)`);
console.log("yuk".padEnd(22) + "varis hizi".padEnd(14) + "kayip".padEnd(9) +
  "ort. bekleme".padEnd(15) + "p95 bekleme".padEnd(14) + "kuyruk");
for (const [istemci, ara] of [[4, 40], [6, 40], [8, 40]]) {
  const r = kuyruk(istemci, ara);
  console.log(`${istemci} istemci / ${ara} ms`.padEnd(22) +
    `${r.hiz.toFixed(0)}/sn`.padEnd(14) + String(r.istek - r.sayac).padEnd(9) +
    `${r.ort.toFixed(1)} ms`.padEnd(15) + `${r.p95} ms`.padEnd(14) +
    (r.hiz > TAVAN ? "doygun" : "akiyor"));
}
8 istemci x 50 artirma = 400 istek, ayni anahtar (model)
paylasilan depo (oku-degistir-yaz)
  beklenen 400   sayacin son degeri 45   kayip guncelleme 355   %88.8
  ustune yazilan yazma 355   islem suresi 85 ms

tek yazarli nesne (istek basina 6 ms, tavan 166.7 istek/sn)
yuk                   varis hizi    kayip    ort. bekleme   p95 bekleme   kuyruk
4 istemci / 40 ms     100/sn        0        4.5 ms         9 ms          akiyor
6 istemci / 40 ms     150/sn        0        7.5 ms         15 ms         akiyor
8 istemci / 40 ms     200/sn        0        206.5 ms       386 ms        doygun

Paylaşılan depoda 400 artırma isteği sayacı 45‘e taşır. Kayıp güncelleme 355, yani %88,8. Sayı bu kadar büyüktür çünkü işlem süresi 85 ms’dir ve sekiz istemci o 85 ms boyunca aynı eski değeri okur; sekizinin yazması da aynı sonucu üretir, yedisi boşa gider. Bu, deponun bir kusuru değildir — depo yazılan şeyi doğru yazmıştır. Kaybolan şey araya girmenin görülmemiş olmasıdır.

Tek yazarlı nesnede aynı iş yükünde kayıp güncelleme üç yük düzeyinde de 0’dır. Nedeni modelin kendisidir: okuma, hesap ve yazma tek bir örneğin belleğinde art arda olur ve o örnek aynı anda başka bir isteği işlemez. Sıfırın bir garanti olarak yazılabilmesi, adımın bölünemez hâle gelmesinden gelir.

Karşılığında Gelen İki Kısıt

Sıfır bedava değildir. İlk bedel kuyruğa girmedir ve tabloda üç düzeyde okunur. Örnek başına tavan 166,7 istek/sn’dir. 100/sn varışta ortalama bekleme 4,5 ms, p95 9 ms. 150/sn varışta 7,5 ms ve 15 ms. 200/sn varışta ise ortalama bekleme 206,5 ms, p95 386 ms’dir ve kuyruk doygundur — varış tavanı aştığı için bekleme koşum boyunca büyümeye devam eder.

İki yük düzeyi arasındaki fark burada dikkat çeker: 150/sn ile 200/sn arasında varış %33 artarken ortalama bekleme 7,5 ms’den 206,5 ms’ye, yani yaklaşık 28 kat çıkar. Tek örnek bir ölçekleme birimidir ve tavanı aştığında yanıt vermeyi kesmez, biriktirir. Devredilen karar eşzamanlılığın yönetimidir; karşılığında gelen kısıt tek bir sayıdır: istek başına 6 ms, saniyede 166,7 istek.

Kısıtın etrafından dolaşmanın yolu örneği çoğaltmak değildir — çoğaltmak tek yazarlığı, dolayısıyla sıfırı iptal eder. Yol anahtarı bölmektir: bir sayaç yerine şube başına bir sayaç tutmak, toplam yükü örnek sayısına böler. Bedeli, artık küresel bir toplamın tek bir yerde durmamasıdır; toplam istendiğinde bütün örnekler okunur ve okuma sayısı örnek sayısı kadar artar.

Nesne Nerede Durur

İkinci bedel coğrafyadır. Nesne bir yerde durur; oradan uzaktaki istemci ona uzaktan erişir. Uçta koşmanın bütün kazancı yakınlıktan geliyordu ve eşgüdümlü durum o yakınlığı tek bir noktaya indirger.

KE22. Uzaklıklar tam turdur ve model girdisidir. KE23. Nesne koşum boyunca taşınmaz; konum değişimi modellenmez. KE24. Senaryo hızları toplam istek hızıdır; örnek başına düşen hız, anahtarın kaç örneğe bölündüğüne göre hesaplanır.

// olcum-agi/senaryo.mjs — nesnenin cografi olarak sabitlenmesi ve senaryo uyumu
// MODEL: uzakliklar tam tur, model girdisidir; nesne kosum boyunca tasinmaz.

const ISLEM_MS = 6, TAVAN = 1000 / ISLEM_MS;
const UC = {
  "uc-1": { yuk: 2600, "en yogun ucta": 4,  "bolgede": 62, "sube basina": 4 },
  "uc-2": { yuk: 2100, "en yogun ucta": 40, "bolgede": 58, "sube basina": 4 },
  "uc-3": { yuk: 1800, "en yogun ucta": 55, "bolgede": 66, "sube basina": 4 },
  "uc-4": { yuk: 1500, "en yogun ucta": 70, "bolgede": 60, "sube basina": 4 },
  "uc-5": { yuk: 1200, "en yogun ucta": 88, "bolgede": 64, "sube basina": 4 },
  "uc-6": { yuk:  800, "en yogun ucta": 96, "bolgede": 70, "sube basina": 4 },
};
const YERLESIM = ["en yogun ucta", "bolgede", "sube basina"];
const TOPLAM = Object.values(UC).reduce((t, u) => t + u.yuk, 0);

console.log(`nesneye erisim turu, ${TOPLAM} istek uc uca dagilmis (model girdisi)`);
console.log("uc".padEnd(8) + "yuk".padEnd(9) + YERLESIM.map((y) => y.padEnd(16)).join(""));
for (const [ad, u] of Object.entries(UC))
  console.log(ad.padEnd(8) + String(u.yuk).padEnd(9) + YERLESIM.map((y) => `${u[y]} ms`.padEnd(16)).join(""));
console.log("agirlikli".padEnd(8) + "".padEnd(9) + YERLESIM.map((y) => {
  const ms = Object.values(UC).reduce((t, u) => t + u.yuk * u[y], 0) / TOPLAM;
  return `${ms.toFixed(1)} ms`.padEnd(16);
}).join(""));
console.log("en kotu uc".padEnd(8) + "".padEnd(9) +
  YERLESIM.map((y) => `${Math.max(...Object.values(UC).map((u) => u[y]))} ms`.padEnd(16)).join(""));
console.log("  esgudum istemeyen is ayni uctan 2 ms'de biter; fark sabitlenmenin bedelidir");

const SENARYO = {
  "sube sayaci":        { anahtar: "sube",    ornek: 6,   hiz: 480, uzunIsMs: 0 },
  "sube odasi":         { anahtar: "sube",    ornek: 6,   hiz: 600, uzunIsMs: 0 },
  "is emri kilidi":     { anahtar: "is emri", ornek: 120, hiz: 240, uzunIsMs: 0 },
  "kuresel sira sayaci":{ anahtar: "tek",     ornek: 1,   hiz: 900, uzunIsMs: 0 },
  "gecelik uzlastirma": { anahtar: "tek",     ornek: 1,   hiz: 5,   uzunIsMs: 40_000 },
};
console.log(`\nsenaryo uyumu (ornek basina tavan ${TAVAN.toFixed(1)} istek/sn)`);
console.log("senaryo".padEnd(22) + "anahtar".padEnd(10) + "ornek".padEnd(8) +
  "toplam hiz".padEnd(13) + "ornek basina".padEnd(14) + "sonuc");
for (const [ad, s] of Object.entries(SENARYO)) {
  const basina = s.hiz / s.ornek;
  const neden = s.uzunIsMs > 0
    ? `uymaz: ${s.uzunIsMs / 1000} sn'lik is ornegi kilitler, ` +
      `arkada ${Math.round((s.uzunIsMs / 1000) * s.hiz)} istek bekler`
    : basina > TAVAN
      ? `uymaz: ornek basina ${basina.toFixed(0)}/sn, tavanin ${(basina / TAVAN).toFixed(1)} kati`
      : `uyar: tavanin %${((basina / TAVAN) * 100).toFixed(0)}'i`;
  console.log(ad.padEnd(22) + s.anahtar.padEnd(10) + String(s.ornek).padEnd(8) +
    `${s.hiz}/sn`.padEnd(13) + `${basina.toFixed(0)}/sn`.padEnd(14) + neden);
}
const uyan = Object.values(SENARYO).filter((s) => s.uzunIsMs === 0 && s.hiz / s.ornek <= TAVAN).length;
console.log(`  ${uyan}/${Object.keys(SENARYO).length} senaryo tek yazarli nesneye uyar`);
nesneye erisim turu, 10000 istek uc uca dagilmis (model girdisi)
uc      yuk      en yogun ucta   bolgede         sube basina     
uc-1    2600     4 ms            62 ms           4 ms            
uc-2    2100     40 ms           58 ms           4 ms            
uc-3    1800     55 ms           66 ms           4 ms            
uc-4    1500     70 ms           60 ms           4 ms            
uc-5    1200     88 ms           64 ms           4 ms            
uc-6    800      96 ms           70 ms           4 ms            
agirlikli         48.1 ms         62.5 ms         4.0 ms          
en kotu uc         96 ms           70 ms           4 ms            
  esgudum istemeyen is ayni uctan 2 ms'de biter; fark sabitlenmenin bedelidir

senaryo uyumu (ornek basina tavan 166.7 istek/sn)
senaryo               anahtar   ornek   toplam hiz   ornek basina  sonuc
sube sayaci           sube      6       480/sn       80/sn         uyar: tavanin %48'i
sube odasi            sube      6       600/sn       100/sn        uyar: tavanin %60'i
is emri kilidi        is emri   120     240/sn       2/sn          uyar: tavanin %1'i
kuresel sira sayaci   tek       1       900/sn       900/sn        uymaz: ornek basina 900/sn, tavanin 5.4 kati
gecelik uzlastirma    tek       1       5/sn         5/sn          uymaz: 40 sn'lik is ornegi kilitler, arkada 200 istek bekler
  3/5 senaryo tek yazarli nesneye uyar

Nesne en yoğun ucun yanına konduğunda ağırlıklı erişim turu 48,1 ms, bölgeye konduğunda 62,5 ms‘dir. İlk seçenek ikinciyi ortalamada geçer ama en kötü ucu kötüleştirir: 96 ms’ye karşı 70 ms. Yani nesneyi en yoğun ucun yanına almak, yükün %26’sını taşıyan uca 4 ms verirken %8’ini taşıyan uca 96 ms yazar. Bölgeye koymak bu dağılımı düzleştirir; kimseye çok iyi, kimseye çok kötü davranmaz.

Üçüncü sütun asıl kararı gösterir. Anahtar şube başına bölündüğünde her nesne kendi ucunda durur ve ağırlıklı erişim turu 4,0 ms‘ye iner — en kötü uç da 4 ms’dir. Eşgüdüm istemeyen bir işin aynı uçtan 2 ms’de bitmesiyle karşılaştırıldığında fark yalnız 2 ms’dir. Coğrafi sabitlenme burada bir bedel olmaktan çıkar, çünkü nesne zaten kullanıldığı yerdedir. Sabitlenme, anahtar coğrafyayla örtüşmediğinde bedel olur.

Hangi Kullanım Uyar

Son tablo beş senaryoyu aynı iki ölçütle tartar: örnek başına düşen hız ve işin süresi. Şube sayacı 480/sn toplam yük taşır, altı örneğe bölünür, örnek başına 80/sn eder — tavanın %48’i. Şube odası 600/sn ile örnek başına 100/sn ve tavanın %60’ı. İş emri kilidi 120 örneğe bölündüğü için örnek başına 2/sn, tavanın %1’i; kilit senaryosu tek yazarlı nesneye en rahat oturan kullanımdır, çünkü anahtar doğal olarak çok sayıda ve her biri seyrektir.

İki senaryo uymaz ve nedenleri farklıdır. Küresel sıra sayacı bölünemez bir anahtar üzerinde 900/sn ister; bu, tavanın 5,4 katıdır ve tek örnek bunu karşılayamaz. Buradaki uyumsuzluk hızdandır. Gecelik uzlaştırma ise yalnız 5/sn ister ama işin kendisi 40 saniye sürer ve o süre boyunca örneği kilitler; arkasında 200 istek bekler. Buradaki uyumsuzluk süredendir. Aynı model iki ayrı eşikte reddeder: istek başına 6 ms’lik tavan ve isteğin bölünemez uzunluğu.

Beş senaryonun 3’ü uyar. Kural tek cümleyle yazılır: tek yazarlı nesne, anahtarı çok sayıda olan, her anahtarı seyrek dokunulan ve işlemleri kısa olan eşgüdüm işleri içindir. Üç koşuldan biri bozulduğunda model reddeder ve reddi bir sayıyla yazar.

Özet

  • Paylaşılan depoda 400 eşzamanlı artırma sayacı 45’e taşır: kayıp güncelleme 355, yani %88,8. Kaybolan şey deponun yanlış yazması değil, araya girmenin görülmemiş olmasıdır.
  • Tek yazarlı nesnede aynı iş yükünde kayıp güncelleme üç yük düzeyinde de 0’dır; okuma, hesap ve yazma tek örneğin belleğinde bölünemez biçimde olur.
  • Karşılığında gelen ilk kısıt tavandır: istek başına 6 ms, örnek başına 166,7 istek/sn. Varış 100, 150 ve 200/sn iken ortalama bekleme 4,5, 7,5 ve 206,5 ms; %33’lük bir varış artışı beklemeyi yaklaşık 28 kat büyütür.
  • İkinci kısıt coğrafi sabitlenmedir: ağırlıklı erişim turu en yoğun uçta 48,1 ms, bölgede 62,5 ms, şube başına bölündüğünde 4,0 ms’dir. Bedel, anahtarın coğrafyayla örtüşmediği durumda doğar.
  • Beş senaryonun 3’ü uyar: şube sayacı (tavanın %48’i), şube odası (%60), iş emri kilidi (%1). Küresel sıra sayacı hız yüzünden (tavanın 5,4 katı), gecelik uzlaştırma süre yüzünden (40 sn’de arkada 200 istek) uymaz.

Sonraki Adım

Bu dersin reddettiği iki senaryonun ortak yanı, isteğin yanıtı beklemek zorunda olmasıdır. Gecelik uzlaştırma 40 saniye sürüyordu ve arkasında iki yüz istek biriktiriyordu; oysa o işin sonucunu bekleyen kimse yoktu. Kurgudaki ağda uçtan gelen çoğu kayıt böyledir: sayaç okuması alınır, alındığı onaylanır ve işlenmesi sonraya bırakılabilir. Uçta bir işi hemen yapmak yerine bırakabilseydik, tek örneğin tavanı bir sınır olmaktan çıkar mıydı? Bırakılan iş nerede durur, kaç kez denenir ve uçtaki çalışma zamanının süre sınırı bu bekleyişi nasıl keser? Sonraki ders uçta işi sonraya bırakmanın yollarını ölçer: bırakılan işin nerede biriktiği, teslim garantisinin ne olduğu ve uçta zamanlanmış bir tetiklemenin bölgedekinden nerede ayrıldığı.

İ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