İçeriğe geç
academia.sh

Ders 08 / 14

Bilet ve Stok Sistemi

Sayılan şeyin geri getirilemez olduğu vaka: kontenjanın tepe hızda 0,179 saniyede tükenmesi, korumasız oku–karar ver–yaz akışının 16 eşzamanlı işçi altında kontenjanın 5,59 katını satması, iyimser ve kötümser kilitlemenin fazla satış ile reddedilen geçerli istek cinsinden ölçülmesi ve rezervasyon penceresinin boşa tutulan kalem-saniyeye etkisi.

İçindekiler

Önceki vakada bir sayaç bir eşiği koruyordu ve eşik aşıldığında kaybedilen şey bir istekti; istemci yeniden dener. Bu vaka aynı sayma sorununu, sayılan şeyin geri getirilemez olduğu yerde sorar. Sınırlı sayıda koltuk vardır ve sayacın iki fazla saymasının karşılığı reddedilmiş bir istek değil, satılmış ama var olmayan bir kalemdir.

İki fark buradan çıkar. Birincisi, aşımın birimi değişir: hız sınırlayıcıda 1,1 kat kabul edilebilirdi, burada bir tanenin bile karşılığı yoktur. İkincisi, kaynağı tutmak ile satmak ayrı işlemlerdir ve aralarında bir pencere vardır.

Kısıtlar ve Kapsam

İşlevsel gereksinimler: kalan kontenjanı bildirmek, bir rezervasyon açmak, rezervasyonu satışa çevirmek, süresi dolan rezervasyonu serbest bırakmak, iptali işlemek.

Tasarlanmayan işler: ödeme yürütümü (sonraki vakanın konusu), koltuk yerleşiminin gösterimi, fiyatlandırma ve ikincil satış.

İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: fazla satış sıfırdır (kaynak: satılan her kalemin karşılığının bulunması yükümlülüğü), stok varken reddedilen geçerli istek sıfırdır (kaynak: kontenjan bitmeden alıcının eli boş dönmemesi), rezervasyon kararı 100 milisaniyeyi geçmez (kaynak: açılış anında sayfanın yanıtsız kalmaması).

Varsayımlar ve Ölçek

Kod Varsayım Değer Gerekçe
BS1 kontenjan 5.000 kalem tek bir satışa açılan sınırlı kaynak
BS2 açılıştaki istek 200.000 / 10 saniye açılış anına yığılan talep
BS3 istek başına adet 1,4 tek ve çift alımların ortalaması
BS4 rezervasyon penceresi 120 saniye ödeme adımına bırakılan süre
BS5 pencereye göre satışa dönme 0,45 / 0,65 / 0,72 30, 120 ve 300 saniyelik pencerede tamamlanan ödeme payı
BS6 eşzamanlı yazma işçisi 16 kontenjan satırına aynı anda dokunan iş sayısı
BS7 stok kaydı 64 bayt kalem kimliği, durum, rezervasyon damgası
BS8 iyimser çakışmada deneme bütçesi 3 yeniden denemenin üst sınırı
// bilet/olcek.mjs — BS varsayim tablosundan cikan kabaca buyukluk hesabi
const BS = { kontenjan: 5_000, acilisIstek: 200_000, acilisSaniye: 10, adet: 1.4,
  pencere: 120, donme: { 30: 0.45, 120: 0.65, 300: 0.72 }, isci: 16, kayitBayt: 64 };

const tepe = BS.acilisIstek / BS.acilisSaniye;
const talep = BS.acilisIstek * BS.adet;
const tukenme = BS.kontenjan / (tepe * BS.adet);
console.log(`tepe istek/s                ${tepe}`);
console.log(`acilistaki toplam talep     ${talep} kalem`);
console.log(`talep / kontenjan           ${(talep / BS.kontenjan).toFixed(1)} kat`);
console.log(`kontenjanin tukenme suresi  ${tukenme.toFixed(3)} s (tepe hizda)`);
console.log(`tukenene kadar gelen istek  ${Math.round(tepe * tukenme)}`);
console.log(`stok tablosu                ${((BS.kontenjan * BS.kayitBayt) / 1e3).toFixed(1)} KB`);
console.log(`rezervasyon sayisi          ${(BS.kontenjan / BS.adet).toFixed(0)} (kontenjan / adet)`);

console.log(`\n${"pencere".padStart(9)}${"donme orani".padStart(13)}${"ilk turda satilan".padStart(19)}` +
  `${"geri donen kalem".padStart(18)}${"bosa kalem-saniye".padStart(19)}`);
for (const [p, d] of Object.entries(BS.donme)) {
  const geri = BS.kontenjan * (1 - d);
  console.log(`${`${p} s`.padStart(9)}${d.toFixed(2).padStart(13)}${(BS.kontenjan * d).toFixed(0).padStart(19)}` +
    `${geri.toFixed(0).padStart(18)}${(geri * Number(p)).toFixed(0).padStart(19)}`);
}
const d120 = BS.donme[BS.pencere], d30 = BS.donme[30];
console.log(`\npencere 30 s -> 120 s: ilk turda satilan ${(BS.kontenjan * d30).toFixed(0)} -> ` +
  `${(BS.kontenjan * d120).toFixed(0)} (+${((d120 - d30) * BS.kontenjan).toFixed(0)} kalem)`);
console.log(`ayni degisim bosa kalem-saniyeyi ${(BS.kontenjan * (1 - d30) * 30).toFixed(0)} -> ` +
  `${(BS.kontenjan * (1 - d120) * BS.pencere).toFixed(0)} yapiyor (` +
  `${((BS.kontenjan * (1 - d120) * BS.pencere) / (BS.kontenjan * (1 - d30) * 30)).toFixed(2)} kat)`);
tepe istek/s                20000
acilistaki toplam talep     280000 kalem
talep / kontenjan           56.0 kat
kontenjanin tukenme suresi  0.179 s (tepe hizda)
tukenene kadar gelen istek  3571
stok tablosu                320.0 KB
rezervasyon sayisi          3571 (kontenjan / adet)

  pencere  donme orani  ilk turda satilan  geri donen kalem  bosa kalem-saniye
     30 s         0.45               2250              2750              82500
    120 s         0.65               3250              1750             210000
    300 s         0.72               3600              1400             420000

pencere 30 s -> 120 s: ilk turda satilan 2250 -> 3250 (+1000 kalem)
ayni degisim bosa kalem-saniyeyi 82500 -> 210000 yapiyor (2.55 kat)

Bu sayılar hesap sınıfındadır. Üçü tasarımı belirliyor. Birincisi, talep kontenjanın 56 katıdır ve kontenjan tepe hızda 0,179 saniyede tükenir; tasarımın çözdüğü sorun kapasite değil, 0,179 saniyelik bir aralıkta doğru sayabilmektir. İkincisi, bu aralıkta yalnız 3571 istek gelir; geri kalan 196.429 isteğin işi reddedilmektir ve bu iş kontenjan satırına hiç dokunmadan yapılmalıdır. Üçüncüsü, rezervasyon penceresi iki yönlü bir maliyet taşır: 30 saniyeden 120 saniyeye çıkarmak ilk turda satılanı 1000 kalem artırıyor ama boşa tutulan kalem-saniyeyi 2,55 katına çıkarıyor.

Fazla Satış Ölçülüyor

Ölçüm süreç içi bir modeldir: gerçek iş parçacığı, veritabanı ya da kilit yoktur; 16 işçi tohumlu bir zamanlayıcı tarafından adım adım ilerletilir ve çekişme, bir işçinin okuması ile yazması arasına başka işçilerin girmesinden doğar. Üç düzen karşılaştırılır: korumasız oku–karar ver–yaz akışı, sürüm sütununa bakan iyimser kilitleme ve kararı kilit altında veren kötümser kilitleme. Mekanikleri Veri Erişim Katmanı kursunun İyimser ve Kötümser Kilitleme dersinde kurulmuştu; burada ölçülen şey bu vakadaki fazla satış sayısıdır.

// bilet/cekisme.mjs — sinirli kaynakta cekismenin surec ici modeli. Gercek is parcacigi,
// veritabani ya da kilit yoktur: 16 isci bir zamanlayici tarafindan adim adim ilerletilir.
const KONTENJAN = 5_000, ISTEK = 20_000, DENEME = 3;   // BS1, BS8

function uretec(tohum) {                     // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

export function kos({ duzen, tohum, ISCI = 16 }) {   // BS6: eszamanli yazma iscisi
  const rnd = uretec(tohum);
  const ortak = { stok: KONTENJAN, surum: 0, kilitli: false };
  const kuyruk = Array.from({ length: ISTEK }, () => (rnd() < 0.6 ? 1 : 2));  // BS3: ortalama 1,4
  const isci = Array.from({ length: ISCI }, () => ({ faz: "bos" }));
  const s = { satilan: 0, red: 0, redGecerli: 0, cakisma: 0, bekleme: 0, adim: 0 };
  let sira = 0;

  while (sira < ISTEK || isci.some((w) => w.faz !== "bos")) {
    const w = isci[Math.floor(rnd() * ISCI)];
    s.adim += 1;
    if (w.faz === "bos") {
      if (sira >= ISTEK) continue;
      w.adet = kuyruk[sira]; sira += 1; w.deneme = 0; w.faz = "oku";
    }
    if (w.faz === "oku") {
      if (duzen === "kotumser") {
        if (ortak.kilitli) { s.bekleme += 1; continue; }
        ortak.kilitli = true;
      }
      w.okunanStok = ortak.stok; w.okunanSurum = ortak.surum; w.faz = "yaz";
      continue;
    }
    if (w.okunanStok < w.adet) {                       // okunan stok yetmiyor
      s.red += 1;
      if (ortak.stok >= w.adet) s.redGecerli += 1;
      if (duzen === "kotumser") ortak.kilitli = false;
      w.faz = "bos"; continue;
    }
    if (duzen === "iyimser" && ortak.surum !== w.okunanSurum) {   // surum degismis
      s.cakisma += 1; w.deneme += 1;
      if (w.deneme >= DENEME) {
        s.red += 1;
        if (ortak.stok >= w.adet) s.redGecerli += 1;
        w.faz = "bos";
      } else w.faz = "oku";
      continue;
    }
    ortak.stok = w.okunanStok - w.adet;
    ortak.surum += 1;
    s.satilan += w.adet;
    if (duzen === "kotumser") ortak.kilitli = false;
    w.faz = "bos";
  }
  s.fazlaSatis = Math.max(0, s.satilan - KONTENJAN);
  s.kalanStok = ortak.stok;
  return s;
}

console.log(`model: kontenjan ${KONTENJAN}, 16 isci, ${ISTEK} istek, deneme butcesi ${DENEME}, tohum 20260730`);
console.log(`\n${"duzen".padEnd(12)}${"satilan".padStart(9)}${"fazla satis".padStart(13)}` +
  `${"kalan stok".padStart(12)}${"cakisma".padStart(9)}${"red (gecerli)".padStart(15)}` +
  `${"bekleme adimi".padStart(15)}${"toplam adim".padStart(13)}`);
const R = {};
for (const d of ["korumasiz", "iyimser", "kotumser"]) {
  const r = (R[d] = kos({ duzen: d, tohum: 20260730 }));
  console.log(`${d.padEnd(12)}${String(r.satilan).padStart(9)}${String(r.fazlaSatis).padStart(13)}` +
    `${String(r.kalanStok).padStart(12)}${String(r.cakisma).padStart(9)}` +
    `${`${r.red} (${r.redGecerli})`.padStart(15)}${String(r.bekleme).padStart(15)}${String(r.adim).padStart(13)}`);
}

const k = R.korumasiz, i = R.iyimser, p = R.kotumser;
console.log(`\nkorumasiz duzen kontenjani ${(k.satilan / KONTENJAN).toFixed(3)} kat satti; ` +
  `fazla satis / kontenjan = %${((100 * k.fazlaSatis) / KONTENJAN).toFixed(2)}`);
console.log(`iyimser: fazla satis ${i.fazlaSatis}, ${i.cakisma} cakisma, ` +
  `${i.redGecerli} gecerli istek deneme butcesi bitince reddedildi`);
console.log(`kotumser: fazla satis ${p.fazlaSatis}, gecerli ret ${p.redGecerli}, ` +
  `adim sayisi iyimserin ${(p.adim / i.adim).toFixed(2)} kati`);

console.log(`\n${"eszamanli isci".padStart(15)}${"iyimser cakisma".padStart(17)}${"gecerli ret".padStart(13)}` +
  `${"kotumser bekleme".padStart(18)}`);
for (const n of [2, 4, 16]) {
  const a = kos({ duzen: "iyimser", tohum: 20260730, ISCI: n });
  const b = kos({ duzen: "kotumser", tohum: 20260730, ISCI: n });
  console.log(`${String(n).padStart(15)}${String(a.cakisma).padStart(17)}${String(a.redGecerli).padStart(13)}` +
    `${String(b.bekleme).padStart(18)}`);
}
model: kontenjan 5000, 16 isci, 20000 istek, deneme butcesi 3, tohum 20260730

duzen         satilan  fazla satis  kalan stok  cakisma  red (gecerli)  bekleme adimi  toplam adim
korumasiz       27963        22963        1798        0          0 (0)              0        40047
iyimser          5000            0           0    10038   16397 (2409)              0        55267
kotumser         5000            0           0        0      16427 (0)         304276       344446

korumasiz duzen kontenjani 5.593 kat satti; fazla satis / kontenjan = %459.26
iyimser: fazla satis 0, 10038 cakisma, 2409 gecerli istek deneme butcesi bitince reddedildi
kotumser: fazla satis 0, gecerli ret 0, adim sayisi iyimserin 6.23 kati

 eszamanli isci  iyimser cakisma  gecerli ret  kotumser bekleme
              2             1519          130             20043
              4             3577          520             60109
             16            10038         2409            304276

Bu sayılar ölçüm sınıfındadır; tohum 20260730 ile yeniden üretilir.

Birinci satır korumasız düzenin ölçüsüdür ve iki sayı taşır. Kontenjanın 5,593 katı satıldı, fazla satış 22.963 kalem. İkinci sayı daha öğreticidir: kalan stok 1798 görünüyor. Sayaç yalnız yanlış değil, yanlış yönde yanlıştır — sistem hâlâ satacak yer olduğunu bildiriyor. Hiçbir istek reddedilmedi, çünkü hiçbir işçi stoku yetersiz görmedi; kaybedilen yazmalar sayacı geri yükseltti.

İkinci ve üçüncü satır iki korumanın da fazla satışı sıfırladığını gösteriyor. Ayrım öteki sütunlardadır. İyimser kilitleme 10.038 çakışma üretiyor ve deneme bütçesi biten 2409 geçerli isteği reddediyor: stok dururken eli boş dönen alıcı. Bu, ikinci işlevsel olmayan gereksinimin ihlalidir. Kötümser kilitleme geçerli ret üretmiyor, karşılığında 304.276 bekleme adımı ve iyimserin 6,23 katı toplam adım harcıyor.

Dördüncü tablo iki maliyetin de aynı değişkene bağlı olduğunu gösteriyor: eşzamanlı işçi sayısı. İşçi 2 iken iyimser kilitlemenin geçerli reddi 130’a, kötümserin bekleme adımı 20.043’e iniyor. Çekişme bir sabit değil, bir tasarım parametresidir.

Tasarım ve Elenen Alternatif

Kontenjan satırı kötümser kilitle korunur; seçim gerekçesi 2409’a karşı 0 geçerli rettir. Kilidin tutulma süresi bir kira olarak verilir ve Uygulama Katmanı ve Servis Etkileşimi kursunun Lider Seçimi dersindeki kira mekaniği burada kilit sahipliğine uygulanır; asılı kalan kilit kira dolunca düşer.

Rezervasyon penceresi 120 saniyedir ve süresi dolan rezervasyonu serbest bırakan denetçi aynı kursun Zamanlayıcı–Aracı–Denetçi dersindeki denetçidir. Açılış anındaki 20.000 istek/s’nin kontenjan satırına ulaşmaması için önde iki katman durur: önceki vakanın hız sınırlayıcısı ve aynı kursun Kuyruk Tabanlı Yük Dengeleme dersindeki kuyruk. Önceki vakada kuyruk bilerek kullanılmamıştı, çünkü orada sözleşme reddetmekti; burada sözleşme sıraya koymaktır ve aynı kalıp bu yüzden yer değiştirir.

Tekillik anahtarı kapsamı (alıcı, kontenjan, istemci istek kimliği) üçlüsüdür; alıcının yeniden denemesi ikinci bir rezervasyon açmaz.

Bilerek kullanılmayan kalıp. Kontenjan sayacının parçalara bölünmesi (Veri Katmanı Ölçekleme, Parçalama) kullanılmaz: bu, önceki vakada ölçülen bölünmüş kotanın aynısıdır ve başka parçada kalem dururken ret üretir — orada bu, sınırının tam üstündeki istemcinin yüzde 4,17’sini reddetmişti.

Elenen alternatif iyimser kilitlemedir ve eleme sayısı 2409 geçerli rettir. Kazandığı da yazılır: 6,23 kat daha az adım, yani kilit tutmayan bir yazma yolu. Hangi kısıt değişirse kazandığı da bellidir: eşzamanlı işçi 16 yerine 2 olsaydı geçerli ret 130’a, kontenjanın yüzde 2,6’sına inerdi ve deneme bütçesi 3’ten 6’ya çıkarılarak bu da düşürülebilirdi.

Arıza Davranışı ve Feda Edilen

Kilidi tutan işçi düşerse kontenjan satırı kira süresi boyunca kilitli kalır ve bu süre boyunca hiçbir satış yapılamaz; kira 0,179 saniyelik tükenme penceresinden uzunsa açılışın tamamı durur. Kuyruk tarafında ise Dayanıklılık ve Güvenilirlik kursunun Zarif Bozulma dersi işler: kontenjan tükendiğinde istekler kontenjan satırına hiç dokunmadan reddedilir ve bu, 196.429 isteğin normal yoludur.

Feda edilen tek cümlededir: bu tasarım fazla satışı sıfırlamak için verimi feda eder — 304.276 bekleme adımı ve 6,23 kat işlem, tek bir kalemin bile fazladan satılmaması karşılığında ödenir.

Özet

  • Talep kontenjanın 56 katı ve kontenjan tepe hızda 0,179 saniyede tükeniyor; bu aralıkta yalnız 3571 istek geliyor, kalan 196.429 isteğin işi reddedilmektir.
  • Korumasız düzen kontenjanın 5,593 katını sattı, fazla satış 22.963; üstelik sayaç sonunda 1798 kalan stok gösteriyor, yani yanlış yönde yanlış.
  • İyimser kilitleme fazla satışı sıfırladı ama 10.038 çakışma üretti ve deneme bütçesi biten 2409 geçerli isteği reddetti.
  • Kötümser kilitleme fazla satışı ve geçerli reddi birden sıfırladı; bedeli 304.276 bekleme adımı ve iyimserin 6,23 katı toplam adımdır.
  • İki maliyet de eşzamanlı işçi sayısına bağlı: 16 yerine 2 işçide geçerli ret 2409’dan 130’a, bekleme adımı 304.276’dan 20.043’e iniyor.
  • Rezervasyon penceresini 30 saniyeden 120 saniyeye çıkarmak ilk turda satılanı 1000 kalem artırıyor, boşa tutulan kalem-saniyeyi 2,55 katına çıkarıyor.

Sonraki Adım

Rezervasyon bir söz verdi ama sözü tutan adım bu vakanın dışında bırakıldı: ödemenin kendisi. O adım iki tarafı olan bir işlemdir ve iki taraf birbirinin kayıtlarını göremez. Sonraki vaka bu ayrımı ele alır. Sorulacak sayı iki kaydın birbirinden ne kadar ayrıştığıdır: eşleşmeyen kayıt sayısı, farkın nereden doğduğu ve geç gelen bir onayın bu farkı ne kadar sürede kapattığı.

İ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