Ders 02 / 20
İşlev Çağırma Modelleri
Aynı doğrulama işi üç çağırma modelinde koşturulur ve dört şey ayrı ayrı sayılır: çağıranın gördüğü gecikme, hatayı kimin gördüğü, yeniden denemenin kimde olduğu ve yinelenen işleme oranı; ardından akış tabanlı modelde toplu okuma boyu birden iki bin beş yüze taranarak çağrı sayısı, fazladan işleme, ölü mektup kutusuna eş düşen sağlam kayıt ve süre sınırına sığmayan toplu iş çıkarılır.
İçindekiler
Önceki ders işlevi tek bir biçimde çağırdı: istek geldi, işlev çalıştı, sonuç döndü. Ama ağın işleri aynı ucun üzerinde durmaz: saha ekibine giden bildirimin yanıtını kimse beklemez; gece toplu işlemesi bir sıradan okunur; geçit üzerinden gelen doğrulama isteği bir istemcinin ucundadır.
Çağırma modeli dört ayrı sayının aynı anda değiştiği bir seçimdir: gecikme, hatayı kimin gördüğü, yeniden denemenin kimde olduğu ve aynı kaydın kaç kez işlendiği. Bu ders dördünü de sayar.
IH7. Bölgesel ölçüm ağının — kurgu bir yazılımın — gece toplu işlemesine giren 20.000 sayaç okuması ölçülür; kayıt sınıfları kendi yazılan bir üreteçle dağıtılır, tohum 20260803’tür. IH8. Bir doğrulama 400 ms, çağrı başına sabit yük 250 ms, eşzamansız kabul yanıtı 5 ms tutar. IH9. Geçici hata ikinci denemede geçer, zehirli kayıt hiç geçmez. IH10. Eşzamanlı modelde çağıran bir kez yeniden dener; eşzamansız ve akış tabanlı modelde yeniden denemeyi altyapı üstlenir, üç deneme ve 60 saniye ara ile. IH11. Akış tabanlı modelde toplu iş bölünmez: tek kayıt hata verirse toplu işin tamamı yeniden okunur, üçüncü denemeden sonra tamamı ölü mektup kutusuna gider; kayıtlar saniyede 250 varır. IH12. Fiyatlar kurgu birimle yazılır (çağrı başına 0,0005, işlem milisaniyesi başına 0,0000025) ve süre sınırı 900 saniyedir.
Aynı İş, Üç Çağırma Yolu
// olcum-agi/cagirma-modelleri.mjs — ayni dogrulama isi uc cagirma modelinde (model) // Kurgu sayilar: bir okuma dogrulamasi 400 ms, cagri basina sabit yuk 250 ms, kabul yaniti 5 ms. export const N = 20000, ISLEM = 400, SABIT = 250, KABUL = 5, BEKLE = 60000; export const CAGRI_B = 0.0005, MS_B = 0.0000025; // kurgu birim: cagri basina ve ms basina export const uretec = (t) => { let s = t >>> 0; return () => { s ^= s << 13; s >>>= 0; s ^= s >>> 17; s ^= s << 5; s >>>= 0; return s / 4294967296; }; }; // Gece toplu islemesine giren 20000 sayac okumasi: cogu temiz, kimi gecici hatali, kimi zehirli. export const kayitlar = (tohum = 20260803) => { const r = uretec(tohum); return Array.from({ length: N }, () => { const v = r(); return v < 0.001 ? "zehirli" : v < 0.021 ? "gecici" : "temiz"; }); }; const yuzde = (a, p) => [...a].sort((x, y) => x - y)[Math.floor(a.length * p)]; const ort = (a) => a.reduce((x, y) => x + y, 0) / a.length; // Eszamanli: cagiran yaniti bekler, hatayi gorur, yeniden denemeyi kendi yapar. export const eszamanli = (k) => { let cagri = 0, yeniden = 0; const bitis = []; for (const d of k) { const deneme = d === "temiz" ? 1 : 2; cagri += deneme; yeniden += deneme - 1; bitis.push(deneme * (SABIT + ISLEM) + (deneme - 1) * 200); } return { ad: "eszamanli", bitis, cagri, yeniden, islenen: cagri, sure: cagri * (SABIT + ISLEM), oluMektup: 0, esDusen: 0, hataGoren: "cagiran", denemeKimde: "cagiran" }; }; // Eszamansiz: cagiran kabul yanitini alip cikar; yeniden denemeyi altyapi yapar. export const eszamansiz = (k) => { let cagri = 0, yeniden = 0, olu = 0; const bitis = []; for (const d of k) { const deneme = d === "temiz" ? 1 : d === "gecici" ? 2 : 3; cagri += deneme; yeniden += deneme - 1; bitis.push(deneme * (SABIT + ISLEM) + (deneme - 1) * BEKLE); if (d === "zehirli") olu++; } return { ad: "eszamansiz", bek: KABUL, bitis, cagri, yeniden, islenen: cagri, sure: cagri * (SABIT + ISLEM), oluMektup: olu, esDusen: 0, hataGoren: "hic kimse", denemeKimde: "altyapi" }; }; // Akis tabanli: kayitlar sirali okunur, toplu is tek cagridir, tek hata butun toplu isi tekrarlatir. export const akis = (k, boy, varis = 250) => { let cagri = 0, islenen = 0, olu = 0, esDusen = 0; const bitis = []; for (let i = 0; i < k.length; i += boy) { const t = k.slice(i, i + boy); const zehirli = t.filter((d) => d === "zehirli").length; const deneme = zehirli ? 3 : t.some((d) => d === "gecici") ? 2 : 1; const sure = deneme * (SABIT + t.length * ISLEM) + (deneme - 1) * BEKLE; cagri += deneme; islenen += deneme * t.length; if (zehirli) { olu += t.length; esDusen += t.length - zehirli; } for (let j = 0; j < t.length; j++) bitis.push(t.length / varis * 1000 + sure); } return { ad: `akis tabanli (boy ${boy})`, bek: KABUL, bitis, cagri, islenen, yeniden: islenen - N, sure: cagri * SABIT + islenen * ISLEM, oluMektup: olu, esDusen, hataGoren: "hic kimse", denemeKimde: "altyapi" }; }; export const olc = (m) => ({ ...m, bekOrt: m.bek ?? ort(m.bitis), bitOrt: ort(m.bitis), bit99: yuzde(m.bitis, 0.99), maliyet: m.cagri * CAGRI_B + m.sure * MS_B }); if (import.meta.url === `file://${process.argv[1]}`) { const k = kayitlar(), say = (d) => k.filter((x) => x === d).length; console.log(`${N} kayit: ${say("temiz")} temiz, ${say("gecici")} gecici hatali, ${say("zehirli")} zehirli`); const m = [eszamanli(k), eszamansiz(k), akis(k, 100)].map(olc); console.log("\n" + "model".padEnd(24) + "cagiranin gordugu".padEnd(19) + "is biter (ort)".padEnd(16) + "is biter (99)".padEnd(15) + "islem cagrisi".padEnd(15) + "fazladan isleme".padEnd(17) + "olu mektup".padEnd(12) + "maliyet"); for (const x of m) console.log(x.ad.padEnd(24) + `${x.bekOrt.toFixed(0)} ms`.padEnd(19) + `${(x.bitOrt / 1000).toFixed(1)} sn`.padEnd(16) + `${(x.bit99 / 1000).toFixed(1)} sn`.padEnd(15) + String(x.cagri).padEnd(15) + String(x.yeniden).padEnd(17) + String(x.oluMektup).padEnd(12) + x.maliyet.toFixed(2)); console.log("\n" + "model".padEnd(24) + "hatayi goren".padEnd(15) + "yeniden deneme kimde".padEnd(22) + "100 kayda dusen fazladan isleme"); for (const x of m) console.log(x.ad.padEnd(24) + x.hataGoren.padEnd(15) + x.denemeKimde.padEnd(22) + (x.yeniden / N * 100).toFixed(1)); const a = m[2]; console.log(`\nolu mektup kutusuna dusen ${a.oluMektup} kaydin ${a.esDusen} tanesi saglamdi: ` + `zehirli kayit basina ${(a.esDusen / say("zehirli")).toFixed(1)} saglam kayit es dustu`); }
20000 kayit: 19551 temiz, 436 gecici hatali, 13 zehirli model cagiranin gordugu is biter (ort) is biter (99) islem cagrisi fazladan isleme olu mektup maliyet eszamanli 669 ms 0.7 sn 1.5 sn 20449 449 0 43.45 eszamansiz 5 ms 2.1 sn 61.3 sn 20462 462 13 43.48 akis tabanli (boy 100) 5 ms 130.9 sn 241.2 sn 380 18000 1300 38.43 model hatayi goren yeniden deneme kimde 100 kayda dusen fazladan isleme eszamanli cagiran cagiran 2.2 eszamansiz hic kimse altyapi 2.3 akis tabanli (boy 100) hic kimse altyapi 90.0 olu mektup kutusuna dusen 1300 kaydin 1287 tanesi saglamdi: zehirli kayit basina 99.0 saglam kayit es dustu
Birinci tablonun iki sütunu ters yönde hareket eder. Çağıranın gördüğü gecikme 669 ms’den 5 ms’ye iner: eşzamansız modelde çağıran işin bitmesini değil, kabul edilmesini bekler. Ama işin bittiği an 0,7 saniyeden 2,1 saniyeye, yüzde doksan dokuzuncu dilim 61,3 saniyeye çıkar. Kazanılan gecikme yok olmaz; çağıranın göremediği bir yere taşınır.
İkinci tablo asıl devri gösterir. Eşzamanlı modelde hatayı çağıran görür ve yeniden denemeye o karar verir; on üç zehirli kaydın on üçü de bildirilir. Öteki iki modelde hatayı hiç kimse görmez: altyapı üç kez dener, kaydı ölü mektup kutusuna bırakır, çağıran çoktan gitmiştir. Yeniden deneme sorumluluğunu devretmek onu ortadan kaldırmaz, hatanın görünürlüğünü kaldırır.
Son sütun devrin bedelini verir. İlk iki modelde her yüz kayda 2,2 fazladan işleme düşer; akış tabanlı modelde 90. Neden toplu işin bölünmemesidir: yüz kayıtlık bir toplu işte tek geçici hata yüz kaydın hepsini yeniden işletir. Aynı bölünmezlik ölü mektup kutusunda da geçerlidir — 1.300 kaydın 1.287’si sağlamdır ve tek suçları zehirli bir kayıtla aynı toplu işe düşmektir.
Toplu Okuma Boyutu
Toplu boy tek bir sayıdır ama üç şeyi birden belirler: çağrı sayısını, yeniden işleme miktarını ve bir kaydın sonucuna ne zaman ulaşıldığını.
// olcum-agi/toplu-tarama.mjs — akis tabanli modelde toplu okuma boyu taranir (model) import { N, ISLEM, SABIT, kayitlar, akis, olc } from "./cagirma-modelleri.mjs"; export const SINIR = 900; // kurgu sure siniri, saniye export const BOY = [1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500]; if (import.meta.url === `file://${process.argv[1]}`) { const k = kayitlar(), zehirli = k.filter((x) => x === "zehirli").length; console.log("boy".padEnd(7) + "cagri".padEnd(9) + "islenen kayit".padEnd(15) + "fazladan".padEnd(11) + "toplam sure".padEnd(14) + "maliyet".padEnd(10) + "kaydin gecikmesi".padEnd(18) + "olu mektup".padEnd(12) + "es dusen".padEnd(10) + "toplu is / sinir"); const satir = BOY.map((b) => olc(akis(k, b))); for (let i = 0; i < BOY.length; i++) { const x = satir[i], toplu = (SABIT + BOY[i] * ISLEM) / 1000; console.log(String(BOY[i]).padEnd(7) + String(x.cagri).padEnd(9) + String(x.islenen).padEnd(15) + `%${(x.yeniden / N * 100).toFixed(0)}`.padEnd(11) + `${(x.sure / 1000).toFixed(0)} sn`.padEnd(14) + x.maliyet.toFixed(2).padEnd(10) + `${(x.bitOrt / 1000).toFixed(1)} sn`.padEnd(18) + String(x.oluMektup).padEnd(12) + String(x.esDusen).padEnd(10) + `${toplu.toFixed(0)}/${SINIR} sn` + (toplu > SINIR ? " — sigmiyor" : "")); } const ucuz = satir.reduce((a, b) => (b.maliyet < a.maliyet ? b : a)); const hizli = satir.reduce((a, b) => (b.sure < a.sure ? b : a)); console.log(`\nen ucuz boy: ${ucuz.ad} — ${ucuz.maliyet.toFixed(2)} birim; ` + `en kisa toplam sure: ${hizli.ad} — ${(hizli.sure / 1000).toFixed(0)} sn`); const b1 = satir[0], b500 = satir[8]; console.log(`boy 1'den 500'e: cagri ${b1.cagri} -> ${b500.cagri} (${(b1.cagri / b500.cagri).toFixed(0)} kat az), ` + `islenen kayit ${b1.islenen} -> ${b500.islenen} (${(b500.islenen / b1.islenen).toFixed(1)} kat cok)`); console.log(`${zehirli} zehirli kayit boy 1'de ${b1.esDusen} saglam kaydi, boy 500'de ${b500.esDusen} ` + `saglam kaydi birlikte olu mektup kutusuna dusurdu`); }
boy cagri islenen kayit fazladan toplam sure maliyet kaydin gecikmesi olu mektup es dusen toplu is / sinir 1 20462 20462 %2 13300 sn 43.48 2.1 sn 13 0 1/900 sn 2 10456 20912 %5 10979 sn 32.68 3.8 sn 26 13 1/900 sn 5 4440 22200 %11 9990 sn 27.20 9.1 sn 65 52 2/900 sn 10 2420 24200 %21 10285 sn 26.92 17.8 sn 130 117 4/900 sn 25 1166 29150 %46 11952 sn 30.46 42.5 sn 325 312 10/900 sn 50 684 34200 %71 13851 sn 34.97 77.4 sn 650 637 20/900 sn 100 380 38000 %90 15295 sn 38.43 130.9 sn 1300 1287 40/900 sn 250 172 43000 %115 17243 sn 43.19 285.5 sn 3000 2987 100/900 sn 500 91 45500 %127 18223 sn 45.60 534.1 sn 5500 5487 200/900 sn 1000 49 49000 %145 19612 sn 49.06 1071.6 sn 9000 8987 400/900 sn 2500 23 57500 %188 23006 sn 57.53 2998.2 sn 17500 17487 1000/900 sn — sigmiyor en ucuz boy: akis tabanli (boy 10) — 26.92 birim; en kisa toplam sure: akis tabanli (boy 5) — 9990 sn boy 1'den 500'e: cagri 20462 -> 91 (225 kat az), islenen kayit 20462 -> 45500 (2.2 kat cok) 13 zehirli kayit boy 1'de 0 saglam kaydi, boy 500'de 5487 saglam kaydi birlikte olu mektup kutusuna dusurdu
Maliyet sütunu bir çukur çizer: boy 1’de 43,48, boy 10’da 26,92, boy 2500’de 57,53 birim. Sol yamaç çağrı başına sabit yükten gelir — her çağrı 250 ms kurulum ister ve bu yirmi bin kez ödenir. Sağ yamaç yeniden işlemeden gelir: boy büyüdükçe bir toplu işte hatalı kayıt bulunma olasılığı artar ve boy 2500’de kayıtların %188’i fazladan işlenir. En ucuz nokta en hızlı nokta değildir: en kısa süre boy 5’te, en düşük maliyet boy 10’dadır.
Üçüncü kuvvet gecikmedir ve tek yönde artar: bir kaydın sonucuna ulaşma süresi boy 1’de 2,1 saniye, boy 500’de 534 saniyedir; kayıt, kendisiyle ilgisi olmayan 499 kaydın işlenmesini bekler. Dördüncüsü bir eşiktir: boy 2500’de toplu işin kendisi 1.000 saniye tutar ve 900 saniyelik süre sınırına sığmaz. Satır yazılır ama seçilemez.
Eş düşen sütunu bölünmezliğin bedelini tek sayıya indirir: on üç zehirli kayıt boy 1’de hiçbir sağlam kaydı götürmez, boy 500’de 5.487 sağlam kaydı ölü mektup kutusuna düşürür. Toplu boyu büyütmek verimle birlikte hata yarıçapını da büyütür.
Yinelenen İşlemenin Bedeli
Fazladan işleme bir maliyet kalemi olarak sayıldı; asıl soru aynı kaydı iki kez işlemenin zararsız olup olmadığıdır. Doğrulama için zararsızdır, fatura satırı için değildir.
// olcum-agi/tekillestirme.mjs — yinelenen islemenin bedeli ve tekillestirmenin bedeli (model) import { N, MS_B, kayitlar, eszamanli, eszamansiz, akis, olc } from "./cagirma-modelleri.mjs"; export const OKU = 15, YAZ = 10, PENCERE = 24 * 3600; // ms, ms, saniye (kurgu) export const SURME_GECIKMESI = 3 * 24 * 3600; // olu mektup kutusu elle 3 gun sonra surulur if (import.meta.url === `file://${process.argv[1]}`) { const k = kayitlar(); const m = [eszamanli(k), eszamansiz(k), akis(k, 10)].map(olc); console.log("model".padEnd(23) + "fazladan isleme".padEnd(17) + "fazla fatura satiri".padEnd(21) + "tekillestirme eki".padEnd(19) + "ek maliyet".padEnd(12) + "pencere disi yineleme"); for (const x of m) { const ek = x.islenen * OKU + N * YAZ; console.log(x.ad.padEnd(23) + String(x.yeniden).padEnd(17) + String(x.yeniden).padEnd(21) + `${(ek / 1000).toFixed(0)} sn`.padEnd(19) + (ek * MS_B).toFixed(2).padEnd(12) + String(x.oluMektup)); } const a = m[2], oran = (a.islenen * OKU + N * YAZ) / a.sure; console.log(`\ntekillestirme penceresi ${PENCERE / 3600} saat; olu mektup kutusu ${SURME_GECIKMESI / 3600} saat ` + `sonra surulur — pencere kapali, ${a.oluMektup} kayit ikinci kez islenir`); console.log(`akis tabanli (boy 10) icin tekillestirme is suresinin %${(oran * 100).toFixed(1)}'i kadar ek yuk getirir; ` + `karsiliginda ${a.yeniden} fazla fatura satiri onlenir`); }
model fazladan isleme fazla fatura satiri tekillestirme eki ek maliyet pencere disi yineleme eszamanli 449 449 507 sn 1.27 0 eszamansiz 462 462 507 sn 1.27 13 akis tabanli (boy 10) 4200 4200 563 sn 1.41 130 tekillestirme penceresi 24 saat; olu mektup kutusu 72 saat sonra surulur — pencere kapali, 130 kayit ikinci kez islenir akis tabanli (boy 10) icin tekillestirme is suresinin %5.5'i kadar ek yuk getirir; karsiliginda 4200 fazla fatura satiri onlenir
Tekilleştirmenin bedeli küçüktür: iş süresinin %5,5’i ve 1,41 birim. Karşılığında 4.200 fazla fatura satırı önlenir; bu kıyas tekilleştirmeyi bir seçenek olmaktan çıkarır.
Son sütun tekilleştirmenin de bir kısıtla geldiğini gösterir. Anahtar sonsuza kadar saklanmaz, pencere 24 saattir; ölü mektup kutusu ise elle, 72 saat sonra sürülür. Sürülen 130 kaydın tekilleştirme kaydı çoktan silinmiştir ve hepsi ikinci kez işlenir. Önlemin kendi penceresi, önlemin kapsamadığı bir yol açar.
Özet
- Çağıranın gördüğü gecikme 669 ms’den 5 ms’ye iner ama işin bittiği an 0,7 saniyeden 2,1 saniyeye, yüzde doksan dokuzuncu dilim 61,3 saniyeye çıkar; gecikme yok olmaz, görünmez olur.
- Eşzamanlı modelde on üç zehirli kaydı çağıran görür; öteki ikisinde hatayı hiç kimse görmez — deneme sorumluluğunu devretmek onu kaldırmaz, görünürlüğü kaldırır.
- Toplu boy taramasında maliyet çukur çizer: boy 1’de 43,48, boy 10’da 26,92, boy 2500’de 57,53 birim. En kısa süre boy 5’te, en düşük maliyet boy 10’dadır ve boy 2500 süre sınırına sığmaz.
- Toplu iş bölünmediği için hata yarıçapı boyla büyür: on üç zehirli kayıt boy 1’de hiçbir sağlam kaydı, boy 500’de 5.487 sağlam kaydı ölü mektup kutusuna düşürür.
- Tekilleştirme iş süresine %5,5 ekleyip 4.200 fazla fatura satırını önler, ama 24 saatlik penceresi vardır; 72 saat sonra sürülen 130 kayıt pencerenin dışında kalır ve ikinci kez işlenir.
Sonraki Adım
Üç modelin gecikme sayıları da tek bir varsayımın üzerine kuruldu: çağrı geldiğinde işlevi çalıştıracak bir örnek hazırdır. Sabit yük olarak yazılan 250 ms o hazır durumun bedelidir.
Hazır örnek her zaman yoktur. Kapasite kararı devredildiği için örnekler istek geldikçe yaratılır, boşta kalınca geri alınır. Sonraki ders bir sıcak havuz modeli koşturur ve havuz ömrü ile trafik profili değiştikçe soğuk çağrı oranını, gecikmeye eklenen süreyi ve kuyruğun ne zaman oluştuğunu sayar; ardından üç azaltma yolunu ayrı ayrı ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.