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.