İçeriğe geç
academia.sh

Ders 08 / 21

Tetikleyiciler

Olay, zaman ve elle tetikleme aynı iki haftalık kurgu takvimde koşturulur: koşum sayısı, hiçbir şey değişmemişken yapılan gereksiz koşum, değişiklik ile koşum arasındaki gecikme ve çakışan koşum sayılır, ardından yol tabanlı filtre eklenip gereksiz koşumun düşüşü ile kaçırılan gerçek değişiklik birlikte ölçülür.

İçindekiler

Hat tanımlı, doğrulanmış ve sürümlenmiş durumda. Önceki iki derste günde 10 ile 80 arasında koşum sayıları kullanıldı ve eşikler o sayılardan hesaplandı, ama sayının nereden geldiği hiç sorulmadı. Koşumu başlatan şey bir tetikleyicidir ve tetikleyici de bir kapıdır — tuttuğu şey koşumun kendisidir. Koşmayan bir hat hiçbir kusur yakalamaz; gereksiz koşan bir hat yalnız para ve sıra harcar.

Üç tür vardır: bir olaya bağlanan (depoya bir değişiklik geldiğinde), saate bağlanan (belirli aralıklarla) ve elle çekilen (biri düğmeye bastığında). Üçü aynı takvimde çok farklı sayılar üretir. Bu ders dördünü sayar: koşum sayısı, gereksiz koşum, gecikmiş geri bildirim ve çakışan koşum — sonra yol tabanlı bir filtre ekleyip aynı sayıları yeniden okur.

HT43. Takvim kurgu ölçüm ağının iki haftasıdır ve node ile üretilir; üreteç kendi yazılmıştır, tohum 9173. On iş günü vardır, hafta sonunda değişiklik yoktur. HT44. Yol grupları ve oranları modelde bildirilmiştir; belgeler/ hattın çıktısını etkilemez, ötekiler etkiler. HT45. Koşum süresi 203 saniyedir — önceki derslerde ölçülen kritik yol; çakışan koşum, önceki koşum bitmeden başlayan koşumdur. HT46. Gecikme, değişiklik anı ile onu kapsayan ilk koşumun başlangıcı arasındaki süredir; koşumun kendi süresi her tetikleyicide aynı olduğu için gecikmeye katılmaz. HT47. Elle tetikleme her iş günü 17:30’da modellenir ve dörtte bir olasılıkla atlanır. HT48. Yol filtresi elenen koşumda ilerlemez; kapsanmamış değişiklikler sonraki koşuma devreder.

Üç Tetikleyici, Aynı Takvim

// olcum-agi/takvim.mjs — iki haftalik kurgu degisiklik takvimi ve tetikleyiciler (model)
export const TOHUM = 9173;
export const uretec = (t) => () => ((t = (t * 48271) % 2147483647) / 2147483647);
export const GUN = 86400, KOSUM = 203; // 05'te olculen kritik yol suresi (saniye)

// Yol gruplari: etkili olan hattin ciktisini degistirir, etkisiz olan degistirmez.
export const YOLLAR = [["kaynak/", 45, 1], ["sinama/", 20, 1], ["belgeler/", 15, 0],
  ["yapilandirma/", 10, 1], ["ortak-kitaplik/", 10, 1]];
export const FILTRE = ["kaynak/", "sinama/", "yapilandirma/"]; // ortak-kitaplik/ listeye eklenmedi

export const takvim = () => {
  const rnd = uretec(TOHUM), d = [];
  for (let g = 0; g < 14; g++) {
    if (g % 7 >= 5) continue; // hafta sonu
    for (let i = 3 + Math.floor(rnd() * 6); i > 0; i--) {
      const p = rnd() * 100;
      let t = 0, yol = YOLLAR[YOLLAR.length - 1];
      for (const y of YOLLAR) { t += y[1]; if (p < t) { yol = y; break; } }
      d.push({ an: g * GUN + Math.round((9 + rnd() * 9) * 3600), yol: yol[0], etkili: yol[2] });
    }
  }
  return d.sort((a, b) => a.an - b.an);
};

export const TETIKLEYICI = {
  olay: (d) => d.map((x) => x.an),
  "zaman-4s": () => Array.from({ length: 84 }, (_, i) => Math.floor(i / 6) * GUN + (i % 6) * 4 * 3600),
  "zaman-gecelik": () => Array.from({ length: 14 }, (_, g) => g * GUN + 2 * 3600),
  elle: () => { const rnd = uretec(TOHUM + 1);
    return [...Array(14).keys()].filter((g) => g % 7 < 5 && rnd() > 0.25).map((g) => g * GUN + 17.5 * 3600); },
};

// Bos kosum: onceki kosumdan beri hic degisiklik yok. Ilgisiz kosum: kapsadigi degisikliklerin
// hicbiri hattin ciktisini etkilemiyor. Cakisan: onceki kosum bitmeden baslayan kosum.
export const olc = (kosum, degisiklik) => {
  let bos = 0, ilgisiz = 0, cakisan = 0, onceki = -Infinity;
  kosum.forEach((k, i) => {
    const kapsanan = degisiklik.filter((d) => d.an > onceki && d.an <= k);
    if (!kapsanan.length) bos++;
    else if (!kapsanan.some((d) => d.etkili)) ilgisiz++;
    if (i > 0 && k - kosum[i - 1] < KOSUM) cakisan++;
    onceki = k;
  });
  const gecikme = [];
  let kapsanmayan = 0;
  for (const d of degisiklik.filter((x) => x.etkili)) {
    const k = kosum.find((t) => t >= d.an);
    if (k === undefined) kapsanmayan++; else gecikme.push(k - d.an);
  }
  return { kosum: kosum.length, bos, ilgisiz, cakisan, kapsanmayan,
    ort: gecikme.reduce((a, b) => a + b, 0) / (gecikme.length || 1), enUzun: Math.max(0, ...gecikme) };
};

// Yol tabanli filtre: kosum, ancak kapsadigi degisikliklerden biri listeye uyarsa yapilir.
// Elenen kosumda "onceki" ilerlemez; kapsanmamis degisiklikler sonraki kosuma devreder.
export const filtrele = (kosum, degisiklik, liste = FILTRE) => {
  const tut = [];
  let onceki = -Infinity;
  for (const k of kosum)
    if (degisiklik.some((d) => d.an > onceki && d.an <= k && liste.includes(d.yol))) { tut.push(k); onceki = k; }
  return tut;
};
// olcum-agi/tetikleyici.mjs — uc tetikleyici turu ayni iki haftalik takvimde
import { takvim, TETIKLEYICI, olc, YOLLAR, TOHUM } from "./takvim.mjs";

const d = takvim();
const sa = (s) => (s / 3600).toFixed(1);
const dagilim = YOLLAR.map(([y]) => `${y}${d.filter((x) => x.yol === y).length}`).join("  ");
console.log(`tohum ${TOHUM}; iki haftada ${d.length} degisiklik, ${d.filter((x) => x.etkili).length} etkili`);
console.log(`yol dagilimi: ${dagilim}`);

console.log("\ntetikleyici      kosum  bos  ilgisiz  gereksiz  cakisan  ort gecikme  en uzun  kapsanmayan");
const sonuc = {};
for (const [ad, uret] of Object.entries(TETIKLEYICI)) {
  const s = (sonuc[ad] = olc(uret(d), d));
  console.log(ad.padEnd(16) + String(s.kosum).padStart(5) + String(s.bos).padStart(5) +
    String(s.ilgisiz).padStart(9) + String(s.bos + s.ilgisiz).padStart(10) + String(s.cakisan).padStart(9) +
    (sa(s.ort) + " sa").padStart(13) + (sa(s.enUzun) + " sa").padStart(9) + String(s.kapsanmayan).padStart(13));
}

// Olay tetikleyicisi zaten varken zaman tetikleyicisinin ek katkisi.
const olay = TETIKLEYICI.olay(d);
for (const ad of ["zaman-4s", "zaman-gecelik"]) {
  const birlikte = [...olay, ...TETIKLEYICI[ad]()].sort((a, b) => a - b);
  const s = olc(birlikte, d);
  console.log(`\nolay + ${ad}: ${s.kosum} kosum (olay tek basina ${sonuc.olay.kosum}), ` +
    `bos ${s.bos}, ort gecikme ${sa(s.ort)} sa — olay tetikleyicisinin gecikmesi ${sa(sonuc.olay.ort)} sa`);
  console.log(`  eklenen ${s.kosum - sonuc.olay.kosum} kosumun ${s.bos} tanesi bos; ` +
    `gecikmeye katkisi ${(sonuc.olay.ort - s.ort).toFixed(1)} saniye`);
}
tohum 9173; iki haftada 53 degisiklik, 46 etkili
yol dagilimi: kaynak/21  sinama/12  belgeler/7  yapilandirma/3  ortak-kitaplik/10

tetikleyici      kosum  bos  ilgisiz  gereksiz  cakisan  ort gecikme  en uzun  kapsanmayan
olay               53    0        7         7        2       0.0 sa   0.0 sa            0
zaman-4s           84   58        1        59        0       2.0 sa   3.9 sa            0
zaman-gecelik      14    4        0         4        0      12.4 sa  16.8 sa            0
elle                6    0        0         0        0      22.2 sa 103.1 sa            0

olay + zaman-4s: 137 kosum (olay tek basina 53), bos 84, ort gecikme 0.0 sa — olay tetikleyicisinin gecikmesi 0.0 sa
  eklenen 84 kosumun 84 tanesi bos; gecikmeye katkisi 0.0 saniye

olay + zaman-gecelik: 67 kosum (olay tek basina 53), bos 14, ort gecikme 0.0 sa — olay tetikleyicisinin gecikmesi 0.0 sa
  eklenen 14 kosumun 14 tanesi bos; gecikmeye katkisi 0.0 saniye

Dört Sayının Okunması

Olay tetikleyicisi 53 koşum yapar ve gecikmesi sıfırdır: her değişiklik kendi koşumunu doğurur. Gereksiz koşumu 7’dir ve hepsi aynı türdendir — yalnız belgeler/ altına dokunan değişiklikler, hattın çıktısını değiştirmediği hâlde tam bir koşum ödetir. Çakışan koşumu 2’dir: iki değişiklik 203 saniyeden yakın aralıkla gelmiş ve ikinci koşum, birincisi bitmeden başlamıştır. Çakışma yalnız yuva harcamaz; önce başlayan koşum bittiğinde ürettiği sonuç zaten eski ağaca aittir.

Dört saatlik zaman tetikleyicisi her iki eksende birden kötüdür. 84 koşum yapar — olay tetikleyicisinden yüzde 58 fazla — ve bunların 58’i tamamen boştur, yani önceki koşumdan beri hiçbir değişiklik gelmemiştir. Gereksiz koşum oranı 59/84, yani yüzde 70. Buna karşılık ortalama gecikme sıfır değil 2 saattir, en uzun bekleme 3,9 saat. Daha çok koşuyor ve daha geç haber veriyor.

Elle tetikleme tablonun en aldatıcı satırıdır. Gereksiz koşumu sıfırdır ve koşum sayısı yalnızca 6’dır; para sütununda açık ara kazanır. Kararı gecikme sütunu verir: ortalama 22,2 saat, en uzun 103,1 saat — dört günü aşkın. Gereksiz koşumunun sıfır olması da erdem değildir: koşumlar o kadar seyrektir ki her biri onlarca değişikliği birden kapsar, dolayısıyla hiçbiri boşa düşmez. Aynı seyreklik, bir koşum kırmızı yandığında hangi değişikliğin kırdığını belirsiz bırakır.

Kaldırılabilir Kapı

Tetikleyiciler birlikte de kullanılabilir ve uygulamada en sık görülen bileşim olay artı zamandır. Ölçüm bu bileşimin ne kattığını sorar. Olay tetikleyicisinin yanına dört saatlik zaman tetikleyicisi eklendiğinde koşum sayısı 53’ten 137’ye çıkar; eklenen 84 koşumun 84’ü de boştur, çünkü her değişiklik zaten kendi koşumunu tetiklemiştir. Gecikmeye katkısı 0,0 saniyedir.

Bu kursun ölçüsüne göre karar açıktır: bir kapı yalnız kendi yakaladığı kadar değerlidir, ve bu kapının yalnız kendisine ait yakalaması yoktur. Dört saatlik zaman tetikleyicisi, olay tetikleyicisi varken kaldırılması gereken kapıdır — 84 boş koşum, sıfır kazanç.

Gecelik koşum aynı ölçüde 14 koşum ekler ve o da bu takvimde sıfır katkı gösterir. Kaldırılıp kaldırılmayacağı burada ölçülemez ve ölçülemediği yazılır: bu takvim yalnız depoya gelen değişiklikleri modelliyor. Gecelik koşumun savunması depo dışındaki değişikliktir — kurulacak bağımlılığın içeriğinin liste dosyası değişmeden büyümesi gibi. Önceki derslerde önbellek anahtarının tam olarak bu türden bir değişikliği kaçırdığı ölçülmüştü. Gecelik koşum o körlüğün karşı ilacıdır ama bu modelde görünmez; kararı verecek ölçüm başka bir takvimdir.

Yol Filtresi

Olay tetikleyicisinin tek kusuru gereksiz koşumdu: 7 koşum belgeler/ değişikliği için harcandı. Bunun bilinen çaresi, koşumu yalnız belirli yolların değişmesine bağlayan bir yol filtresidir. Filtre listesi üç yol taşır; depoya sonradan eklenen ortak kitaplık dizini listeye yazılmamıştır.

// olcum-agi/filtre.mjs — yol tabanli filtre: gereksiz kosumun dususu ve kacirilan gercek degisiklik
import { takvim, TETIKLEYICI, olc, filtrele, FILTRE } from "./takvim.mjs";

const d = takvim(), sa = (s) => (s / 3600).toFixed(1);
const ok = (a, b) => `${a} -> ${b}`.padStart(12);
console.log(`filtre listesi: ${FILTRE.join(" ")}  (ortak-kitaplik/ listede yok)`);
console.log("\ntetikleyici        kosum     gereksiz  kapsanmayan   ort gecikme");
for (const [ad, uret] of Object.entries(TETIKLEYICI)) {
  const k = uret(d), o = olc(k, d), f = olc(filtrele(k, d), d);
  console.log(ad.padEnd(16) + ok(o.kosum, f.kosum) + ok(o.bos + o.ilgisiz, f.bos + f.ilgisiz) +
    ok(o.kapsanmayan, f.kapsanmayan) + `  ${sa(o.ort)} -> ${sa(f.ort)} sa`);
}

// Filtre listesinde olmayan yolun degisiklikleri: kendi kosumunu tetikleyemez, baskasina biner.
const suzulen = filtrele(TETIKLEYICI.olay(d), d);
const disarida = d.filter((x) => x.yol === "ortak-kitaplik/");
const bekleme = disarida.map((x) => { const k = suzulen.find((t) => t >= x.an); return k === undefined ? null : k - x.an; });
const binen = bekleme.filter((b) => b !== null);
console.log(`\nortak-kitaplik/ degisikligi: ${disarida.length} tane, hicbiri kendi kosumunu tetiklemedi`);
console.log(`  ${binen.length} tanesi baska bir degisikligin kosumuna bindi, ortalama ` +
  `${sa(binen.reduce((a, b) => a + b, 0) / binen.length)} sa gecikmeyle, en uzunu ${sa(Math.max(...binen))} sa`);
console.log(`  ${bekleme.length - binen.length} tanesi iki hafta boyunca hic kosulmadi`);

// Kapinin hesabi: tuttugu gereksiz kosum, yanlis tuttugu gercek degisiklik, ekledigi gecikme.
const olayHam = olc(TETIKLEYICI.olay(d), d), olayFiltreli = olc(suzulen, d);
const duzeltilmis = olc(filtrele(TETIKLEYICI.olay(d), d, [...FILTRE, "ortak-kitaplik/"]), d);
console.log(`\nkapi hesabi (olay tetikleyicisi + yol filtresi)`);
console.log(`  tuttugu    : ${olayHam.bos + olayHam.ilgisiz} gereksiz kosum (belgeler/ degisiklikleri)`);
console.log(`  yanlis tuttugu: ${disarida.length} gercek degisiklik, ${olayFiltreli.kapsanmayan} tanesi hic kosulmadi`);
console.log(`  ekledigi sure : ortalama gecikme ${sa(olayHam.ort)} -> ${sa(olayFiltreli.ort)} sa`);
console.log(`  liste duzeltilince: ${duzeltilmis.kosum} kosum, ${duzeltilmis.bos + duzeltilmis.ilgisiz} gereksiz, ` +
  `${duzeltilmis.kapsanmayan} kapsanmayan, gecikme ${sa(duzeltilmis.ort)} sa`);
filtre listesi: kaynak/ sinama/ yapilandirma/  (ortak-kitaplik/ listede yok)

tetikleyici        kosum     gereksiz  kapsanmayan   ort gecikme
olay                53 -> 36      7 -> 0      0 -> 1  0.0 -> 0.6 sa
zaman-4s            84 -> 23     59 -> 0      0 -> 0  2.0 -> 2.6 sa
zaman-gecelik       14 -> 10      4 -> 0      0 -> 0  12.4 -> 12.4 sa
elle                  6 -> 6      0 -> 0      0 -> 0  22.2 -> 22.2 sa

ortak-kitaplik/ degisikligi: 10 tane, hicbiri kendi kosumunu tetiklemedi
  9 tanesi baska bir degisikligin kosumuna bindi, ortalama 3.1 sa gecikmeyle, en uzunu 19.9 sa
  1 tanesi iki hafta boyunca hic kosulmadi

kapi hesabi (olay tetikleyicisi + yol filtresi)
  tuttugu    : 7 gereksiz kosum (belgeler/ degisiklikleri)
  yanlis tuttugu: 10 gercek degisiklik, 1 tanesi hic kosulmadi
  ekledigi sure : ortalama gecikme 0.0 -> 0.6 sa
  liste duzeltilince: 46 kosum, 0 gereksiz, 0 kapsanmayan, gecikme 0.0 sa

Filtrenin İki Yüzü

İlk satır tek başına okunursa filtre başarılıdır: olay tetikleyicisinde koşum 53’ten 36’ya, gereksiz koşum 7’den sıfıra iner. İkinci sayıya bakıldığında hesap bozulur. Düşen koşum 17’dir, oysa gereksiz olan yalnız 7’ydi. Aradaki 10 koşum, hattın çıktısını gerçekten etkileyen değişikliklerin koşumuydu.

O on değişikliğin hepsi ortak kitaplık dizinindedir ve hiçbiri kendi koşumunu tetiklememiştir. Dokuzu, sonradan gelen başka bir değişikliğin koşumuna binerek ortalama 3,1 saat, en uzunu 19,9 saat gecikmeyle hatta girmiştir. Biri iki hafta boyunca hiç koşulmamıştır. Ortalama gecikme 0,0 saatten 0,6 saate çıkmıştır — küçük görünen bir sayı, çünkü 46 etkili değişikliğin 36’sı hiç gecikmemiştir; ortalama, on değişikliğin başına geleni gizler.

Kapı hesabı üç satırda tamamlanır: filtre 7 gereksiz koşum tutar, 10 gerçek değişikliği yanlış tutar ve bunlardan biri hiç koşulmaz. Yanlış tuttuğu, doğru tuttuğundan fazladır. Bu hâliyle kapı zararlıdır ve kaldırılması ondan iyidir.

Kaldırmak yine de doğru çözüm değildir, çünkü sorun kapının kendisinde değil listesindedir. Liste düzeltilip ortak kitaplık yolu eklendiğinde ölçüm son satırı verir: 46 koşum, 0 gereksiz, 0 kapsanmayan, 0,0 saat gecikme. Ham olay tetikleyicisine göre 7 koşum az, hiçbir kayıp yok. Filtrenin doğru kurulduğunda değeri budur; yanlış kurulduğunda bedeli de yukarıdaki üç satırdır.

Zaman tetikleyicisinde filtrenin etkisi daha da büyüktür: 84 koşum 23’e, 59 gereksiz koşum sıfıra iner ve kapsanmayan değişiklik doğmaz, çünkü elenen koşumun kapsayacağı değişiklikler bir sonrakine devreder. Elle tetiklemede ise hiçbir şey değişmez: altı koşumun altısı da zaten filtreye uyan bir değişiklik kapsıyordur.

Bu ölçümün asıl bulgusu listede saklıdır. Hattın girdileri bu kursta iki ayrı yerde bildiriliyor: önbellek anahtarında ve tetikleyici filtresinde. İkisi de elle yazılıyor, depo büyüdükçe eskiyor ve eksik olduklarında aynı sonucu veriyor — bir değişiklik olduğu hâlde kapı koşmuyor. Önceki ölçümde bu, iki kusur kaçırmıştı; burada on değişikliği geciktirdi ve birini hiç koşturmadı.

Özet

  • Aynı iki haftalık takvimde: olay tetikleyicisi 53 koşum ve sıfır gecikme, dört saatlik zaman tetikleyicisi 84 koşum ve 2 saat gecikme, gecelik koşum 14 koşum ve 12,4 saat, elle tetikleme 6 koşum ve 22,2 saat (en uzun 103,1 saat) verdi.
  • Dört saatlik zaman tetikleyicisi her iki eksende birden kötüdür: olay tetikleyicisinden yüzde 58 fazla koşar (59/84’ü gereksiz) ve daha geç haber verir. Elle tetiklemenin sıfır gereksiz koşumu erdem değil seyreklik sonucudur; aynı seyreklik hangi değişikliğin kırdığını da belirsizleştirir.
  • Olay tetikleyicisi varken dört saatlik zaman tetikleyicisi kaldırılması gereken kapıdır: eklediği 84 koşumun 84’ü boş, gecikmeye katkısı 0,0 saniye. Gecelik koşumun savunması depo dışındaki değişikliktir ve bu takvim onu ölçmez.
  • Yol filtresi olay tetikleyicisinde koşumu 53’ten 36’ya indirdi, ama düşen 17 koşumun yalnız 7’si gereksizdi. Listede olmayan dizindeki 10 değişikliğin dokuzu ortalama 3,1 saat gecikmeyle başkasının koşumuna bindi, biri iki hafta hiç koşulmadı.
  • Kapı hesabı: 7 gereksiz koşum tutuldu, 10 gerçek değişiklik yanlış tutuldu. Liste düzeltilince 46 koşum, 0 gereksiz, 0 kapsanmayan ve 0,0 saat gecikme çıkıyor — kusur kapıda değil listededir. Hattın girdileri iki ayrı yerde elle bildiriliyor (önbellek anahtarı ve tetikleyici filtresi) ve iki liste de eksik olduğunda aynı sonucu veriyor: değişiklik var, kapı koşmuyor.

Sonraki Adım

Hat bu konunun sonunda tamamdır: kapıları sayıldı, hangi kapının yalnız kendisinin ne yakaladığı ölçüldü, süresi 203 saniyelik kritik yola indirildi, nerede koşacağı ve ne zaman koşacağı karara bağlandı. Bütün bu ölçümler sessiz bir varsayım üzerinde duruyordu: bir değişiklik yeşil koşumdan çıktığında üretime kadar gelmişti. Oysa yeşil bir koşum yalnız değişikliğin hatta hazır olduğunu söyler, üretimde olduğunu değil. Üretime nasıl gireceği ayrı bir karardır — hepsi birden mi, bir bölümüne mi önce, iki ortam arasında geçişle mi, yoksa kod içinde bir bayrağın arkasından mı? Bu kararların her biri kendi geri alma yolunu ve kendi gözlem ölçütünü getirir. Hattın son kapısı orada durur ve o kapının ne tuttuğu, neyi kaçırdığı ve ne kadar beklettiği aynı üç sayıyla ölçülür. Sonraki konu bu kararı ele alır.

İ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