İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat