Ders 18 / 20
Kenar Kuyrukları ve Zamanlanmış İşler
Aynı iş kümesi merkez ve kenar kuyruğunda işlenir; kuyruğa girme turu, işin koştuğu yer ve sonucun dönüş yolu ayrı sayılır. Kenar kuyruğunun dört kısıtı ile zamanlanmış işin tetiklendiği bölge ölçülür.
İçindekiler
Bir önceki ders kenarda eşgüdümlü durumu tutmayı başardı: tek yazarlı nesne kendi anahtarına gelen yazmaları sıraya koydu. Eşzamansız iş ise hâlâ merkezde koşuyor; doğrulama, fatura satırı ve saha bildirimi merkez bölgedeki bir kuyruğa yazılıyor. Bu ders aynı iş kümesini iki yerde işletip üç şeyi ayrı sayar: kuyruğa girme turu, işin koştuğu yer, sonucun dönüş yolu.
Ölçüm bölgesel ölçüm ağının altı şube ucu üzerindedir ve kurgudur; uçlar, iki kuyruk servisi
ve zamanlayıcı node ile kurulmuş bir modeldir. Gerçek bir ürün çağrılmaz.
KE25. Her uç için merkeze ve en yakın kenar konumuna tek yön gecikme, bir de gecelik okuma sayısı yazılıdır. KE26. Kuyruğa girme turu, tek yön gecikmenin iki katı artı kuyruğun sabit kabul süresidir. KE27. Merkez kuyruğu 250’lik toplu okuma, 10 deneme, 14 gün saklama ve anahtar başına sıralama sunar; kenar kuyruğu 25 ve 3 sunar, 4 gün saklar, sıralama vermez. KE28. Tüketici her toplu okumada bir kez başlar: merkezde 40 ms, kenarda 6 ms. Okuma başına işlem iki yolda da 12 ms’dir. KE29. Sonuç, işin koştuğu yerdeki depoya yazılır. Merkez deposu merkez turu kadar uzaktır; kenar anahtar–değer deposu yerel okunur, ama yazısının öteki uçlarda görünmesi 480 ms sürer. KE30. Zamanlanmış tetikleme yalnız temel saati bilir; kenar zamanlayıcı tetikleme konumunu kendi seçer ve seçim kayıt altında değildir.
Aynı İş Kümesi, İki Kuyruk
// ag.mjs — kurgu olcum agi modeli: uclar, gecikmeler, iki kuyrugun kisitlari. export const UCLAR = [ // tek yon gecikme ms, serit temel saatten sapma { ad: "uc-bati", merkez: 12, kenar: 5, serit: 0, okuma: 610 }, { ad: "uc-kuzey", merkez: 38, kenar: 7, serit: 0, okuma: 520 }, { ad: "uc-dogu", merkez: 74, kenar: 9, serit: 2, okuma: 430 }, { ad: "uc-guney", merkez: 51, kenar: 6, serit: 1, okuma: 350 }, { ad: "uc-yayla", merkez: 96, kenar: 11, serit: 3, okuma: 280 }, { ad: "uc-liman", merkez: 63, kenar: 8, serit: 2, okuma: 210 }, ]; export const TOPLAM = UCLAR.reduce((t, u) => t + u.okuma, 0); export const KUYRUK = { // kalem kalem kisitlar merkez: { kabul: 9, toplu: 250, deneme: 10, saklama_gn: 14, baslatma: 40 }, kenar: { kabul: 3, toplu: 25, deneme: 3, saklama_gn: 4, baslatma: 6 }, }; export const ISLEME = 12, YAYILMA = 480, DEPO_OKUMA = 5; // isleme / kenar yayilmasi / depo yaniti, ms export const tur = (yol, u) => 2 * u[yol] + KUYRUK[yol].kabul; export const sut = (...s) => console.log(s.map((x, i) => String(x).padEnd(i ? 13 : 11)).join("").trimEnd()); export function uretec(tohum) { // tohum gorunur; kosumdan kosuma ayni dizi let s = tohum >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; } export function isKumesi(tohum) { // okuma: sayac kimligi, artan sira, gereken deneme const r = uretec(tohum), sira = new Map(), kume = []; for (const u of UCLAR) for (let i = 0; i < u.okuma; i++) { const sayac = `s${String(Math.floor(r() * 900)).padStart(3, "0")}`; sira.set(sayac, (sira.get(sayac) ?? 0) + 1); kume.push({ uc: u.ad, sayac, sira: sira.get(sayac), deneme: 1 }); } for (const i of kume) while (r() < 0.28 && i.deneme < 12) i.deneme++; return kume; }
// tur.mjs — ayni is kumesi iki yerde: kuyruga girme turu, isin kostugu yer, sonucun donus yolu. import { UCLAR, TOPLAM, KUYRUK, ISLEME, YAYILMA, DEPO_OKUMA, tur, sut } from "./ag.mjs"; sut("uc", "okuma", "merkez turu", "kenar turu", "kazanc"); for (const u of UCLAR) sut(u.ad, u.okuma, `${tur("merkez", u)} ms`, `${tur("kenar", u)} ms`, `${(tur("merkez", u) / tur("kenar", u)).toFixed(1)}x`); const agirlik = (yol) => UCLAR.reduce((t, u) => t + u.okuma * tur(yol, u), 0) / TOPLAM; console.log(`${TOPLAM} okuma icin agirlikli kuyruga girme turu: ` + `merkez ${agirlik("merkez").toFixed(1)} ms, kenar ${agirlik("kenar").toFixed(1)} ms\n`); for (const yol of ["merkez", "kenar"]) { // tuketici: kac kez basliyor const k = KUYRUK[yol], cagri = Math.ceil(TOPLAM / k.toplu); const baslatma = cagri * k.baslatma, is = TOPLAM * ISLEME; console.log(`${yol.padEnd(7)}toplu okuma ${String(k.toplu).padStart(3)} -> ${String(cagri).padStart(3)} tuketici cagrisi; ` + `baslatma ${String(baslatma).padStart(4)} ms + isleme ${is} ms = ${baslatma + is} ms`); } console.log(`isin kostugu yer: merkez yolunda bilinen tek bolge; ` + `kenar yolunda platformun sectigi konum, secim kayitli degil\n`); // donus yolu: merkez deposu uzak, kenar deposu yerel ama once yayilmali const don = (yol) => UCLAR.reduce((t, u) => t + u.okuma * (2 * u[yol] + DEPO_OKUMA), 0) / TOPLAM; console.log(`agirlikli donus turu: merkez ${don("merkez").toFixed(1)} ms, kenar ${don("kenar").toFixed(1)} ms`); console.log(`sonuc hazir olana kadar: merkez ${don("merkez").toFixed(1)} ms, ` + `kenar ${(don("kenar") + YAYILMA).toFixed(1)} ms (yayilma ${YAYILMA} ms eklendi)`); console.log(`uctan uca toplam: merkez ${(agirlik("merkez") + don("merkez")).toFixed(1)} ms, ` + `kenar ${(agirlik("kenar") + don("kenar") + YAYILMA).toFixed(1)} ms`);
uc okuma merkez turu kenar turu kazanc uc-bati 610 33 ms 13 ms 2.5x uc-kuzey 520 85 ms 17 ms 5.0x uc-dogu 430 157 ms 21 ms 7.5x uc-guney 350 111 ms 15 ms 7.4x uc-yayla 280 201 ms 25 ms 8.0x uc-liman 210 135 ms 19 ms 7.1x 2400 okuma icin agirlikli kuyruga girme turu: merkez 106.4 ms, kenar 17.5 ms merkez toplu okuma 250 -> 10 tuketici cagrisi; baslatma 400 ms + isleme 28800 ms = 29200 ms kenar toplu okuma 25 -> 96 tuketici cagrisi; baslatma 576 ms + isleme 28800 ms = 29376 ms isin kostugu yer: merkez yolunda bilinen tek bolge; kenar yolunda platformun sectigi konum, secim kayitli degil agirlikli donus turu: merkez 102.4 ms, kenar 19.5 ms sonuc hazir olana kadar: merkez 102.4 ms, kenar 499.5 ms (yayilma 480 ms eklendi) uctan uca toplam: merkez 208.8 ms, kenar 517.0 ms
Birinci ölçüde kenar açık ara kazanır: ağırlıklı kuyruğa girme turu 106,4 milisaniyeden 17,5 milisaniyeye iner, en uzak uçta kazanç sekiz kata çıkar.
İkinci ölçüde tablo ters döner. Kenar tüketicisi altı milisaniyede başlar, merkez kırkta; ama kenarın toplu okuma boyutu yirmi beş olduğu için 2400 ileti doksan altı çağrıya bölünür, merkezde ona. Çağrı başına daha ucuz olan başlangıç, dokuz kat daha sık başlandığı için toplamda daha pahalıya gelir. İşin koştuğu yer de belirsizleşir: merkezde bilinen tek bir bölge, kenarda platformun seçtiği kayıtsız bir konum.
Üçüncü ölçü dönüş yoludur. Kenarın dönüş turu 19,5 milisaniyeyle kısadır, ama sonuç kenar deposuna yazıldığı için kullanıcının ucunda görünmesi 480 milisaniyelik yayılmayı bekler: uçtan uca toplam merkezde 208,8, kenarda 517,0 milisaniye. Kuyruğa girmede kazanılan 89 milisaniye, sonucun okunabilir olmasında kaybedilen 397 milisaniyeyi karşılamaz.
Kenar Kuyruğunun Dört Kısıtı
// kisit.mjs — kenar kuyrugunun dort kisiti ayni is kumesinde sayilir: deneme, saklama, sira, toplu. import { TOPLAM, KUYRUK, isKumesi, uretec } from "./ag.mjs"; const sut = (...s) => console.log(s.map((x, i) => String(x).padEnd(i ? 15 : 17)).join("").trimEnd()); const kume = isKumesi(20260731); // tohum gorunur sut("kisit", "merkez", "kenar", "olcum", "kenarin kaybi"); const olu = (k) => kume.filter((i) => i.deneme > k.deneme).length; // deneme hakkini asan ileti sut("yeniden deneme", KUYRUK.merkez.deneme, KUYRUK.kenar.deneme, `${TOPLAM} ileti`, `${olu(KUYRUK.kenar)} olu mektup (${(100 * olu(KUYRUK.kenar) / TOPLAM).toFixed(1)}%)`); const KESINTI_GN = 6, GUNLUK = TOPLAM; // tuketici alti gun durdu const kalan = (k) => Math.min(KESINTI_GN, k.saklama_gn) * GUNLUK; sut("saklama suresi", `${KUYRUK.merkez.saklama_gn} gn`, `${KUYRUK.kenar.saklama_gn} gn`, `${KESINTI_GN} gn kesinti`, `${KESINTI_GN * GUNLUK - kalan(KUYRUK.kenar)} ileti dustu`); const r = uretec(770419), karisik = kume.map((i) => [r(), i]).sort((a, b) => a[0] - b[0]).map((x) => x[1]); const uygula = (akis) => { const son = new Map(); for (const i of akis) son.set(i.sayac, i.sira); return son; }; const dogru = uygula([...kume].sort((a, b) => a.sira - b.sira)), bozuk = uygula(karisik); const yanlis = [...dogru].filter(([s, n]) => bozuk.get(s) !== n).length; sut("siralama", "anahtar basina", "yok", `${dogru.size} sayac`, `${yanlis} sayacta yanlis endeks (${(100 * yanlis / dogru.size).toFixed(1)}%)`); const cagri = (k) => Math.ceil(TOPLAM / k.toplu); sut("toplu okuma", KUYRUK.merkez.toplu, KUYRUK.kenar.toplu, `${TOPLAM} ileti`, `${cagri(KUYRUK.kenar) - cagri(KUYRUK.merkez)} fazla cagri`); const cok = [...dogru.values()].filter((n) => n > 1).length, b = KUYRUK.kenar; console.log("\ndolasmanin bedeli, kalem kalem:"); for (const s of [ `deneme: ${olu(b)} olu mektup elle geri konur -> ${olu(b)} ek yazma; saklama sayaci sifirlanir, sira bastan gelir`, `saklama: ${KESINTI_GN * GUNLUK - kalan(b)} ileti icin ikinci dayanikli kopya; ${KESINTI_GN * GUNLUK} iletinin tamami iki yere yazilir`, `siralama: ${cok} cok okumali sayac icin tampon; ${TOPLAM} ek durum yazmasi, ileti basina 3000 ms bekleme`, `toplu okuma: ${cagri(b)} x ${b.baslatma} ms = ${cagri(b) * b.baslatma} ms, merkezin ${cagri(KUYRUK.merkez) * KUYRUK.merkez.baslatma} ms'sinden fazla`, ]) console.log(" " + s);
kisit merkez kenar olcum kenarin kaybi yeniden deneme 10 3 2400 ileti 50 olu mektup (2.1%) saklama suresi 14 gn 4 gn 6 gn kesinti 4800 ileti dustu siralama anahtar basina yok 842 sayac 438 sayacta yanlis endeks (52.0%) toplu okuma 250 25 2400 ileti 86 fazla cagri dolasmanin bedeli, kalem kalem: deneme: 50 olu mektup elle geri konur -> 50 ek yazma; saklama sayaci sifirlanir, sira bastan gelir saklama: 4800 ileti icin ikinci dayanikli kopya; 14400 iletinin tamami iki yere yazilir siralama: 667 cok okumali sayac icin tampon; 2400 ek durum yazmasi, ileti basina 3000 ms bekleme toplu okuma: 96 x 6 ms = 576 ms, merkezin 400 ms'sinden fazla
Dört kısıt dört ayrı sayı verir. Deneme hakkı ondan üçe inince aynı hata dağılımında 50 ileti ölü mektup kutusuna düşer; merkez yolunda hepsi geçer. Saklama on dörtten dört güne inince, altı günlük bir kesintide biriken 14400 iletinin 4800’ü kuyruktan düşer; merkez kuyruğu hiçbirini kaybetmez.
En pahalı kalem sıralamadır. Garanti olmadığında 842 sayacın 438’i yanlış endeksle biter: son uygulanan okuma en büyük sıra numarası olmayabilir, fatura eksik ya da fazla çıkar. Bu, ayarla düzeltilecek bir kalem değil, kuyruğun hiç sunmadığı bir garantidir.
Dolaşmanın bedeli son bloktadır. Ölü mektupları elle geri koymak 50 ek yazma demektir, ama geri konan ileti yenidir: saklama sayacı sıfırlanır, sıra numarası baştan gelir. Uzun saklama ancak ikinci bir dayanıklı kopyayla, sıralama da 667 sayaç adına tutulan tamponla sağlanır: 14400 iletinin tamamı iki yere yazılır, 2400 ek durum yazması ve ileti başına üç saniye bekleme çıkar. Üçü de kenarda kazanılan 89 milisaniyeden büyüktür.
Zamanlanmış İş Hangi Bölgede Tetiklenir
// zaman.mjs — zamanlanmis is: hangi bolgede tetiklendigi ve saat dilimi karari kimde kaldi. import { UCLAR, DEPO_OKUMA } from "./ag.mjs"; const TEMEL = 2, KAYMA_GUN = 217, KAYAN_SERIT = [2, 3]; // temel saat 02:00, mevsimlik kayma penceresi const saat = (h) => `${String(((h % 24) + 24) % 24).padStart(2, "0")}:00`; const gun = (h) => (h < 0 ? " (bir onceki gun)" : ""); const s1 = (...s) => console.log(s.map((x, i) => String(x).padEnd(i ? 14 : 11)).join("").trimEnd()); s1("uc", "serit", "tek tetikleme", "yerel saat", "kayma penceresi"); for (const u of UCLAR) s1(u.ad, `+${u.serit}`, saat(TEMEL), saat(TEMEL + u.serit), KAYAN_SERIT.includes(u.serit) ? `${KAYMA_GUN} gun 60 dk` : "yok"); const yerel = new Set(UCLAR.map((u) => TEMEL + u.serit)); const seritler = [...new Set(UCLAR.map((u) => u.serit))].sort(); console.log(`\ntek tetikleme -> ${yerel.size} ayri yerel saat; ` + `${UCLAR.filter((u) => u.serit === 0).length}/${UCLAR.length} uc istenen ${saat(TEMEL)} saatini goruyor`); console.log(`her ucta ${saat(TEMEL)} icin ${seritler.length} ayri tetikleme tanimi: ` + seritler.map((s) => `+${s} temel ${saat(TEMEL - s)}${gun(TEMEL - s)}`).join(", ")); const kayan = UCLAR.filter((u) => KAYAN_SERIT.includes(u.serit)); console.log(`mevsimlik kayma: ${kayan.length} uc x ${KAYMA_GUN} gun = ${kayan.length * KAYMA_GUN} uc-gun ` + `60 dk sapma; zamanlayici yalniz temel saati bilir, cevrimi islev kendi yapar\n`); const OKUMA = 2400, PAKET = 100, tur = Math.ceil(OKUMA / PAKET); const sure = (u) => tur * (2 * u.merkez + DEPO_OKUMA); const l = UCLAR.map(sure), en_az = Math.min(...l), en_cok = Math.max(...l); console.log(`gecelik is ${tur} tur x ${PAKET} okuma; tetikleme yeri secilemedigi icin sure ` + `${en_az}-${en_cok} ms araliginda, ortalama ${(l.reduce((a, b) => a + b) / l.length).toFixed(0)} ms ` + `(${(en_cok / en_az).toFixed(1)}x fark)`); console.log(`sabitlemenin bedeli: is merkez bolgeye pinlenirse sure ${en_az} ms'de sabitlenir, ` + `ama kenarda kosma ozelligi bu is icin tumden birakilir`);
uc serit tek tetikleme yerel saat kayma penceresi uc-bati +0 02:00 02:00 yok uc-kuzey +0 02:00 02:00 yok uc-dogu +2 02:00 04:00 217 gun 60 dk uc-guney +1 02:00 03:00 yok uc-yayla +3 02:00 05:00 217 gun 60 dk uc-liman +2 02:00 04:00 217 gun 60 dk tek tetikleme -> 4 ayri yerel saat; 2/6 uc istenen 02:00 saatini goruyor her ucta 02:00 icin 4 ayri tetikleme tanimi: +0 temel 02:00, +1 temel 01:00, +2 temel 00:00, +3 temel 23:00 (bir onceki gun) mevsimlik kayma: 3 uc x 217 gun = 651 uc-gun 60 dk sapma; zamanlayici yalniz temel saati bilir, cevrimi islev kendi yapar gecelik is 24 tur x 100 okuma; tetikleme yeri secilemedigi icin sure 696-4728 ms araliginda, ortalama 2792 ms (6.8x fark) sabitlemenin bedeli: is merkez bolgeye pinlenirse sure 696 ms'de sabitlenir, ama kenarda kosma ozelligi bu is icin tumden birakilir
Gecelik işin “her şubede 02:00’de koşsun” isteği tek tetikleme tanımıyla karşılanamaz. Temel saate kurulan tetikleme dört ayrı yerel saate düşer, altı uçtan yalnız ikisi istenen saati görür. Her uçta 02:00 için dört tanım gerekir ve şerit +3’ünki temel saat 23:00’e, yani bir önceki güne düşer. İki şerit ayrıca yılın 217 gününde altmış dakika kayar: 651 uç-gün sapma. Zamanlayıcı yalnız temel saati bilir; yerel saate çevirme kararı devredilmez, işlevin kodunda kalır.
Son iki satır tetiklemenin nerede koştuğunu sayar. Gecelik iş merkez deposunu yirmi dört turda okur; koştuğu konum seçilemediği için aynı iş 696 ile 4728 milisaniye arasında, ortalama 2792 milisaniyede biter — 6,8 katlık bir aralık ve hangi ucuna düşeceği bizim kararımız değil. Süreyi sabitlemenin tek yolu işi merkeze sabitlemektir; o zaman iş kenarda koşmaz.
Özet
- Kuyruğa girme turu kenarda 106,4 milisaniyeden 17,5 milisaniyeye indi, en uzak uçta kazanç 8 kata çıktı; ama toplu okuma 250’den 25’e indiği için 2400 ileti 10 yerine 96 çağrıya bölündü ve başlatma toplamı 400 yerine 576 milisaniye oldu.
- Sonuç kenar deposuna yazıldığı için 480 milisaniye yayılma bekledi; uçtan uca toplam merkezde 208,8, kenarda 517,0 milisaniye çıktı. Kabul hızlandı, teslim yavaşladı.
- Dört kısıt dört sayı verdi: 3 deneme hakkı 50 ölü mektup, 4 gün saklama 6 günlük kesintide 4800 ileti kaybı, sıralama garantisinin yokluğu 842 sayacın 438’inde yanlış endeks, 25’lik toplu okuma 86 fazla çağrı. Dolaşmanın bedeli 50 ek yazma, 14400 iletiyi iki yere yazma, 2400 ek durum yazması ve 3000 milisaniye bekleme oldu.
- Tek tetikleme 4 ayrı yerel saate düştü, 6 uçtan 2’si istenen saati gördü; 651 uç-gün mevsimlik sapma çıktı ve gecelik iş tetiklendiği yere göre 696-4728 milisaniye sürdü.
Sonraki Adım
Kenar çalışma zamanı bir kuyruğa ve bir zamanlayıcıya erişti, ama erişimin nasıl kurulduğu sorulmadı. Kaynak adresi ve kimlik bilgisi çalışma zamanında istemci kurularak da verilebilir, dağıtım anında bildirimsel bağlamayla da. Bir sonraki ders aynı kaynağa iki yoldan erişip dördünü sayar: kod satırı, açılış işi, kimlik bilgisinin yeri, ve yanlış yapılandırmanın fark edilme anı.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.