Ders 08 / 20
Olay Veri Yolu
Yönlendirme kararının veri yoluna devredilmesi: aynı iki bin olaylık akışta kural kümesi genişledikçe hiçbir kurala uymayan ve birden çok hedefe giden olay sayısının taranması, süzgecin kuralda mı hedefte mi durduğunun taşınan olay sayısına etkisi ve yeni bir kuralın hiçbir sayıyı bozmadan trafik almasının ölçülmesi.
İçindekiler
Önceki ders işlevin sınırlarını saydı ve nerede kullanılamayacağını yazdı. Geriye tek bir soru kaldı: işlevler birbirine nasıl bağlanacak? Bir işlev kendi başına hiçbir şey yapmaz; bir şeyin onu uyandırması gerekir. Uyandıran taraf çağıran işlevse, çağıranın hedefi bilmesi gerekir — adresi, sürümü, kaç tane olduğu. Üretici sayısı arttıkça bu bilgi her üreticide ayrı ayrı çoğalır.
Olay veri yolu (event bus) bu bağı tersine çevirir. Üretici olayı yazar ve kime gideceğini bilmez; hedefi kural (rule) seçer. Karar üreticiden alınıp veri yoluna devredilmiştir. Bu ders devredilen kararın karşılığında ne geldiğini sayar.
Devredilen Karar: Kimin Uyanacağı
Doğrudan çağrıda hedef listesi kodun içindedir: kim kimi çağırıyor, çağrı yığınından okunur. Kural tabanlı yönlendirmede hedef listesi kodun dışındadır — kural bir veri kalemidir. Karşılığında gelen kısıt buradan doğar: yönlendirme artık kaynak deposundaki farkta görünmez, işlevin çağrı çizgesinde durmaz ve bir olayın nereye gittiği ancak kural kümesi okunarak bilinir.
Devredilen kararın ilk karşılığı bağ sayısındadır. Ölçüm ağında olay üreten altı bileşen ve olayla iş yapan dokuz hedef var. Doğrudan çağrıda her üretici ilgilendiği hedefleri bilmek zorundadır: en kötü durumda 54 bağ, her biri iki tarafta da tutulan bir adres ve bir sürüm. Veri yolunda üretici tek bir yere yazar, hedefler tek bir yerden okur — 6 + 9 = 15 kayıt ve aralarında bir kural kümesi. Bağ sayısı çarpımdan toplama iner. Kısıt de aynı yerden gelir: 54 bağın hepsi kodda görünürdü, kural kümesindeki hiçbir kalem kodda değildir.
İkinci kısıt eşleşmenin kendisindedir. Bir olay hiçbir kurala uymazsa veri yolunun öntanımlı davranışı onu düşürmektir; hata üretmez, kimseyi uyandırmaz, geriye iz bırakmaz. Üçüncüsü tersidir: bir olay birden çok kurala uyarsa her kural kendi hedefini uyandırır ve aynı olay birden çok kez işlenir. İkisi de kural kümesinin genişliğine bağlıdır, ikisi de bir sayıdır.
Ölçüm, ders boyunca sürecek kurgu bir düzenek üzerinde yapılır: bölgesel ölçüm ağı — sayaçların okumalarını gönderdiği, okumaların doğrulandığı, fatura üretilen ve saha ekiplerine iş atanan bir sistem. Ağ da, olay adları da kurgudur.
- OS1. Akış 2.000 olaydır; olay türleri dokuz kalemdir ve türlerin payı sabittir.
- OS2. Her olayın üç alanı vardır: tür, bölge (dört bölge), öncelik (1–3).
- OS3. Kural, nokta ile ayrılmış bir tür deseni ve isteğe bağlı alan koşullarıdır; desendeki
*o bölümden sonrasını serbest bırakır. - OS4. Bir olay aynı hedefe iki kuraldan uyarsa tek teslim sayılır; teslim hedef başınadır.
- OS5. Rastgelelik gerektiren yerlerde üreteç kendi yazılır ve tohum görünürdür.
- OS6. Model yalnız yönlendirme kararını kurar; taşıma gecikmesi ve sıra ölçülmez.
Kural Kümesi Genişledikçe
Aşağıdaki kod bir veri yolu değil, yönlendirme kararının sayılabilir bir modelidir. Aynı akış üç kural kümesinden geçirilir: dört kurallı dar, yedi kurallı orta, on kurallı ve bir yakala-tümü kuralı içeren geniş.
// kural-motoru.mjs — olay veri yolu modeli: kurallar deseni tutan olaylari hedeflere yonlendirir // Model: gercek bir veri yolu degil, yonlendirme kararinin sayilabilir bicimde yeniden kurulmasi. const TOHUM = 20260415; // uretec tohumu: kosum tekrarlanabilir let durum = TOHUM; const rastgele = () => (durum = (durum * 1103515245 + 12345) % 2147483648) / 2147483648; const TURLER = [["olcum.okuma.alindi", 46], ["olcum.okuma.eksik", 9], ["olcum.dogrulama.basarisiz", 7], ["fatura.taslak.olustu", 12], ["fatura.kesinlesti", 6], ["cihaz.pil.dusuk", 8], ["cihaz.baglanti.koptu", 5], ["saha.ekip.atandi", 4], ["saha.ariza.kapandi", 3]]; const BOLGELER = ["kuzey", "dogu", "bati", "merkez"]; function olaylar(n) { const liste = []; for (let i = 0; i < n; i++) { let p = rastgele() * 100, tur = TURLER[0][0]; for (const [ad, agirlik] of TURLER) { if ((p -= agirlik) < 0) { tur = ad; break; } } liste.push({ tur, bolge: BOLGELER[Math.floor(rastgele() * 4)], oncelik: 1 + Math.floor(rastgele() * 3) }); } return liste; } // Desen: nokta ile ayrilmis bolumler; "*" o bolumden sonrasini serbest birakir. function tutuyor(desen, tur) { const d = desen.split("."), t = tur.split("."); for (let i = 0; i < d.length; i++) { if (d[i] === "*") return true; if (d[i] !== t[i]) return false; } return d.length === t.length; } const uyuyor = (k, o) => tutuyor(k.desen, o.tur) && (k.bolge === undefined || k.bolge === o.bolge) && (k.enAz === undefined || o.oncelik >= k.enAz); const K = { dar: [ { ad: "K1", desen: "olcum.okuma.alindi", hedef: "dogrulama" }, { ad: "K2", desen: "fatura.*", hedef: "fatura" }, { ad: "K3", desen: "cihaz.pil.dusuk", hedef: "bakim" }, { ad: "K4", desen: "saha.*", hedef: "saha" }], }; K.orta = [...K.dar, { ad: "K5", desen: "olcum.*", hedef: "arsiv" }, { ad: "K6", desen: "cihaz.*", hedef: "izleme" }, { ad: "K7", desen: "olcum.dogrulama.basarisiz", bolge: "kuzey", hedef: "uyari" }]; K.genis = [...K.orta, { ad: "K8", desen: "*", hedef: "denetim" }, { ad: "K9", desen: "fatura.kesinlesti", hedef: "muhasebe" }, { ad: "K10", desen: "olcum.okuma.alindi", enAz: 3, hedef: "oncelikli" }]; const AKIS = olaylar(2000); function tara(kurallar) { let eslesmeyen = 0, tek = 0, cok = 0, teslim = 0; for (const o of AKIS) { const h = new Set(kurallar.filter((k) => uyuyor(k, o)).map((k) => k.hedef)); teslim += h.size; if (h.size === 0) eslesmeyen++; else if (h.size === 1) tek++; else cok++; } return { eslesmeyen, tek, cok, teslim }; } console.log("kume kural eslesmeyen tek hedef cok hedef toplam teslim"); for (const ad of ["dar", "orta", "genis"]) { const s = tara(K[ad]); console.log(ad.padEnd(8) + String(K[ad].length).padStart(5) + String(s.eslesmeyen).padStart(12) + String(s.tek).padStart(11) + String(s.cok).padStart(11) + String(s.teslim).padStart(15)); } console.log("olay sayisi: " + AKIS.length + ", tohum: " + TOHUM);
node kural-motoru.mjs
kume kural eslesmeyen tek hedef cok hedef toplam teslim dar 4 433 1567 0 1567 orta 7 0 881 1119 3119 genis 10 0 0 2000 5551 olay sayisi: 2000, tohum: 20260415
İlk satır sessiz düşmeyi gösteriyor. Dört kural iki bin olayın 1.567’sini bir hedefe verdi; 433 olay hiçbir kurala uymadı ve düşürüldü. Bu olaylar eksik okuma, doğrulama hatası ve kopan bağlantı bildirimleridir — yani en çok bakılması gereken üç türden ikisi. Düşme bir hata değildir; veri yolu için “eşleşme yok” olağan bir sonuçtur. Üretici tarafında da bir belirti yoktur: olay yazıldı, kabul edildi, iş bitti.
İkinci satır kısıtın öbür ucudur. Üç kural eklenince eşleşmeyen sıfıra indi, ama 1.119 olay birden çok hedefe gitti ve toplam teslim 1.567’den 3.119’a çıktı. Aynı okuma hem doğrulama hem arşiv hedefini uyandırıyor. İki hedef ayrı iş yapıyorsa bu doğrudur; ikisi de fatura kalemi yazıyorsa aynı kalem iki kez yazılır. Kural kümesi bu farkı bilmez.
Bu iki satır arasındaki geçiş, kural kümesi genişletilirken izlenen olağan yoldur: eşleşmeyen bir olay fark edilir, desen genişletilir, eşleşmeyen sayısı düşer. Düşen tek şey o değildir — her genişletme, daha önce tek hedefe giden olayları da yeni kurala uygun hale getirir. Dört kuralda hiçbir olay çok hedefli değilken, üç kural eklendikten sonra akışın yüzde 56’sı çok hedeflidir. Genişletmenin etkisi yalnız hedeflenen türle sınırlı kalmaz; desen, hedeflenmeyen türleri de kapsar.
Üçüncü satırda yakala-tümü kuralı eşleşmeyeni sıfırda tutuyor, ama artık her olay çok hedefli: 2.000 olay 5.551 teslime, olay başına 2,78 uyandırmaya dönüştü. Sessiz düşmeden kaçmanın bedeli üç kat teslimdir ve teslim, işlev tarafında çağrı demektir.
Süzgecin Yeri
Yönlendirme kararı devredilince ikinci bir karar da devredilebilir: süzme. Doğrulama hedefi gerçekte yalnız kuzey bölgesinin öncelikli okumalarıyla ilgilenir. Bu koşul ya kuralın içine yazılır ya da hedefin ilk satırlarına. İkisi de aynı işi yapar; taşınan olay sayısı aynı değildir.
// suzgec-yeri.mjs — ayni akis, suzgec kuralda ve hedefte; sonra gorunmeyen bir kural eklenir const TOHUM = 20260415; // ilk kosumla ayni tohum, ayni akis let durum = TOHUM; const rastgele = () => (durum = (durum * 1103515245 + 12345) % 2147483648) / 2147483648; const TURLER = [["olcum.okuma.alindi", 46], ["olcum.okuma.eksik", 9], ["olcum.dogrulama.basarisiz", 7], ["fatura.taslak.olustu", 12], ["fatura.kesinlesti", 6], ["cihaz.pil.dusuk", 8], ["cihaz.baglanti.koptu", 5], ["saha.ekip.atandi", 4], ["saha.ariza.kapandi", 3]]; const BOLGELER = ["kuzey", "dogu", "bati", "merkez"]; const AKIS = []; for (let i = 0; i < 2000; i++) { let p = rastgele() * 100, tur = TURLER[0][0]; for (const [ad, agirlik] of TURLER) { if ((p -= agirlik) < 0) { tur = ad; break; } } AKIS.push({ tur, bolge: BOLGELER[Math.floor(rastgele() * 4)], oncelik: 1 + Math.floor(rastgele() * 3) }); } const ilgili = (o) => o.bolge === "kuzey" && o.oncelik >= 2; // hedefin gercekten istedigi const okuma = AKIS.filter((o) => o.tur === "olcum.okuma.alindi"); const BIRIM = 1000; // kurgu birim: her 1000 teslim 1 tasima birimi console.log("suzgecin yeri".padEnd(16) + "tasinan".padStart(8) + "bosa uyandirma".padStart(16) + "tasima birimi".padStart(15)); for (const [yer, tasinan] of [["suzgec kuralda", okuma.filter(ilgili)], ["suzgec hedefte", okuma]]) { const bosa = tasinan.length - tasinan.filter(ilgili).length; console.log(yer.padEnd(16) + String(tasinan.length).padStart(8) + String(bosa).padStart(16) + (tasinan.length / BIRIM).toFixed(2).padStart(15)); } // Gorunmeyen kural: mevcut hedefin sayilarina dokunmadan ayni olaylari ikinci bir hedefe verir. const oncesi = okuma.filter(ilgili).length; const golge = AKIS.filter((o) => o.tur.startsWith("olcum.okuma.")).length; console.log("\nkural eklemeden once dogrulama hedefi: " + oncesi + ", toplam teslim: " + oncesi); console.log("K11 eklendikten sonra dogrulama hedefi: " + oncesi + ", toplam teslim: " + (oncesi + golge) + ", hata: 0");
node suzgec-yeri.mjs
suzgecin yeri tasinan bosa uyandirma tasima birimi suzgec kuralda 145 0 0.14 suzgec hedefte 926 781 0.93 kural eklemeden once dogrulama hedefi: 145, toplam teslim: 145 K11 eklendikten sonra dogrulama hedefi: 145, toplam teslim: 1257, hata: 0
Süzgeç kuralda durunca 145 olay taşındı ve hepsi işlendi. Hedefte durunca 926 olay taşındı, 781’i ilk satırda elendi: işlev uyandı, olayı okudu, ilgisiz olduğunu gördü, döndü. Aynı işin görünen maliyeti taşıma tarafında 0,14 birime karşı 0,93 birim — 6,4 kat. Birim kurgudur; oran kural kümesinden çıkar.
Boşa uyandırmanın bedeli yalnız taşıma değildir. 781 çağrı eşzamanlılık havuzundan pay alır, soğuk başlangıç ihtimalini taşır ve işlev tarafındaki her ölçüte gürültü olarak girer: çağrı sayısı, hata oranı, ortalama süre. Süzgeci kurala taşımanın bedeli ise koşulun kod incelemesinden çıkıp yapılandırmaya geçmesidir — bir sonraki bölümün konusu.
Kaynakta süzmenin bir sınırı var ve bu sınır ölçümün dışında kalan asıl kısıttır: süzgeç yalnız olayın kendi alanlarına bakabilir. Doğrulama hedefi “yalnız sözleşmesi askıya alınmış aboneliklerin okumaları” isteseydi, bu koşul olayda taşınan üç alandan hiçbiriyle yazılamazdı — aboneliğin durumu başka bir yerde tutulur. O durumda tek yol, koşulu ilgilendiren alanı olaya eklemektir; bedeli, üreticinin artık tüketicinin ihtiyacını bilmesi, yani devredilen bağın bir ucundan geri gelmesidir. Alan eklenemiyorsa 781 boşa uyandırma kaçınılmazdır ve seçim, süzgecin yeri değil, hangi maliyetin kabul edileceğidir.
Kuralın Görünmezliği Bir Yüzeydir
Çıktının son iki satırı tek bir şey gösteriyor. Kural kümesine, doğrulama hedefine dokunmayan
yeni bir kural eklendi: olcum.okuma.* desenli K11, olayları ikinci bir hedefe veriyor.
Doğrulama hedefinin teslim sayısı 145’te kaldı, hata sayısı sıfır kaldı, hiçbir ölçüt
kıpırdamadı.
Toplam teslim ise 145’ten 1.257’ye çıktı — akıştaki bütün okuma olayları artık ikinci bir yere
de gidiyor.
Bu, yönlendirmeyi devretmenin en pahalı kısıtıdır. Kural bir veri kalemi olduğu için:
- Kaynak deposundaki farkta görünmez; işlev kodunda tek satır değişmez.
- Var olan hedeflerin ölçütlerini bozmaz, dolayısıyla hiçbir uyarı eşiğini aşmaz.
- Yeni hedefe giden olayların içeriği üreticinin niyetinden bağımsızdır: üretici okumayı doğrulama için yazmıştı, kural onu başka bir yere de verdi.
Aynı görünmezlik ters yönde de çalışır. Bir kural silindiğinde ya da deseni daraltıldığında hedef trafik almayı bırakır; işlev hata vermez, çünkü hiç çağrılmaz. Çağrı sayısı sıfıra iner ve sıfır, bir arıza belirtisi gibi değil, “bu saatte iş yok” gibi okunur. Birinci ölçümdeki 433 sessiz düşme ile bu durum arasında hiçbir gözlemsel fark yoktur: ikisinde de olay yazılmış, kabul edilmiş ve hiçbir yere gitmemiştir. Aradaki farkı görebilmenin tek yolu, üretilen olay sayısı ile teslim edilen olay sayısını ayrı ayrı sayıp aralarındaki farkı izlemektir.
Kısıtın etrafından dolaşmanın üç yolu var ve üçü de sayılabilir bir bedel ister. Birincisi,
kural kümesini kaynak deposunda tutup değişiklikleri kod gibi incelemektir; bedeli, her kural
değişikliğinin yayın hattına girmesi ve dakikalar süren bir işlemin saatlere yayılmasıdır.
İkincisi hedef başına teslim sayacı tutmaktır; K11 eklendiğinde toplam teslimin 145’ten
1.257’ye çıkması ancak böyle görülür ve bedeli, on kurallı bir kümede on ayrı sayaç ile bunları
okuyan bir eşik kümesidir. Üçüncüsü desen genişliğini yasaklamaktır — yakala-tümü kuralı
kaldırılır, * yalnız son bölümde kullanılır. Bunun bedeli birinci ölçümdeki ilk satırdır:
desen daraldıkça eşleşmeyen olay sayısı yeniden yükselir ve 433 olay sessizce düşer.
Üçü arasında seçim yapılırken bakılacak sayı, düşen olayın maliyeti ile fazladan teslimin maliyetinin karşılaştırılmasıdır. Ölçüm ağında düşen bir doğrulama hatası faturaya yanlış tüketim olarak yansır; fazladan bir arşiv teslimi yalnız depolama yazar. Bu ağda kural kümesi geniş, süzgeç kuralda, sayaç hedef başınadır.
Özet
- Olay veri yolu yönlendirme kararını üreticiden alır: üretici hedefi bilmez, hedefi kural seçer. Karşılığında kural bir veri kalemi olur ve kod incelemesinin dışına çıkar.
- Aynı 2.000 olaylık akışta dört kurallı küme 433 olayı hiçbir hedefe vermedi ve bunu hata üretmeden yaptı; sessiz düşme veri yolunun öntanımlı davranışıdır.
- Kural sayısı yediye çıkınca eşleşmeyen sıfırlandı ama 1.119 olay çok hedefli oldu; on kurallı kümede toplam teslim 5.551’e, olay başına 2,78 uyandırmaya çıktı.
- Süzgeç kuralda 145 olay taşıdı, hedefte 926 olay taşıdı ve bunların 781’i işlevi boşuna uyandırdı; taşıma tarafındaki fark 0,14 birime karşı 0,93 kurgu birimdir.
- Yeni bir kural, var olan hedefin teslim sayısını 145’te bırakıp toplam teslimi 1.257’ye çıkardı ve hiçbir hata üretmedi; kuralın görünmezliği bu yüzden bir yüzeydir.
- Sessiz düşme ile silinmiş kural aynı belirtiyi verir: her ikisinde de çağrı sayısı sıfırdır. Aradaki farkı ayırt etmenin yolu, üretilen olay sayısı ile teslim sayısını ayrı saymaktır.
Sonraki Adım
Yönlendirme kararı verildi: hangi olayın kimi uyandıracağı artık kural kümesinde yazılı. Ama bu ölçümde bir şey hiç sorulmadı — hedef uyandığında ayakta değilse ne olur? Model her teslimi başarılı saydı; oysa işlev kısılabilir, hata verebilir, eşzamanlılık sınırına çarpabilir. Olayı tutan, yeniden deneyen ve tüketici hazır olana kadar bekleten bir katman gerekir. Sonraki ders bu katmanı hizmet olarak ölçer: kuyruk ve konu servisleri devralındığında hangi kararlar elden çıkar, görünürlük süresi hangi eşikte çift işleme, hangi eşikte gecikme üretir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.