Ders 02 / 30
Denetim Düzlemi Bileşenleri
Kararı üstlenen taraf dört parçaya ayrılır ve tek bir istek uçtan uca izlenir: kaç bileşenden geçtiği, her bileşenin hangi bilgiyi okuyup yazdığı, durum deposuna düşen okuma ve yazma sayılır. Sonra her bileşen tek tek düşürülür ve hangi kararların durup hangilerinin sürdüğü ölçülür.
İçindekiler
Bir önceki ders yerleştirme kararının altı bilgi kalemine dayandığını ve bu kalemlerin dördünün sürekli toplanması gerektiğini saydı. Toplayan, saklayan, karar üreten ve sonucu uygulayan taraf tek bir parça değildir. Bu ders o tarafı — denetim düzlemini — dört bileşene ayırır ve iki şey ölçer: bir isteğin uçtan uca izi, ve bir bileşen düştüğünde hangi kararların durduğu.
Bileşenler görevleriyle anılır: küme arayüzü (tek yazma kapısı ve doğrulama), durum deposu (bütün kalemlerin kalıcı yeri), zamanlayıcı (yerleştirme kararı), denetleyici yöneticisi (istenen ile gerçek arasındaki farkı kapatan iş). Düğüm tarafı burada tek bir toplu parça olarak durur; parçalarına bir sonraki ders ayırır.
Ölçüm yine bölgesel ölçüm ağı üzerindedir: su sayacı okumalarını toplayan ve doğrulayan
kurgu yazılım. Koşan şey gerçek bir küme değil, node ile kurulmuş bir modeldir.
KM7. Şema kendi şemamızdır: üç anahtar öneki — servis/<ad>, kopya/<ad>, dugum/<ad>.
KM8. Denetim düzlemi dört bileşendir; düğüm tarafı tek bir toplu parça olarak modellenir.
KM9. Küme arayüzü tek yazma kapısıdır ve kendi durumu yoktur; her yazma durum deposuna iner.
KM10. Okuma bir öneki tarayan tek işlemdir; taranan kalem sayısı okuma yükü olarak sayılır.
KM11. Başlangıç durumu iki servis (toplayıcı 3 kopya, doğrulayıcı 2 kopya) ve üç düğümdür;
düğüm kapasitesi dörttür.
KM12. Bir bileşen düştüğünde yalnız o bileşen durur; ağ ve düğümler ayaktadır. Bir kopyanın
öldüğü bilgisi de küme arayüzünden geçerek deftere girer.
Dört Bileşen, Tek Yazma Kapısı
// duzlem.mjs — denetim duzleminin dort bileseni ve durum deposu. Model; gercek kume degil. // Sema kendi semamizdir: servis/<ad>, kopya/<ad>, dugum/<ad>. export function kur(baslangic) { const veri = new Map(Object.entries(baslangic)); const iz = []; let rev = 0, aktif = "-"; const d = { veri, iz, get rev() { return rev; }, oku(on) { const c = [...veri].filter(([a]) => a.startsWith(on)); iz.push({ b: aktif, i: "oku", a: on + "*", n: c.length }); return c; }, yaz(a, v) { veri.set(a, v); rev++; iz.push({ b: aktif, i: "yaz", a, n: 1 }); }, sil(a) { veri.delete(a); rev++; iz.push({ b: aktif, i: "sil", a, n: 1 }); }, bilesen(ad) { aktif = ad; }, }; return d; } // 1) Kume arayuzu: tek yazma kapisi. Semayi dogrular, depoya yazar. Kendi durumu yoktur. export const arayuz = (d, istek) => { d.bilesen("kume arayuzu"); if (!istek.anahtar.match(/^(servis|kopya|dugum)\//)) return "sema disi"; istek.deger === null ? d.sil(istek.anahtar) : d.yaz(istek.anahtar, istek.deger); return "yazildi"; }; // 2) Denetleyici yoneticisi: istenen kopya sayisi ile gercek kopya sayisini karsilastirir. export const denetleyici = (d) => { d.bilesen("denetleyici yoneticisi"); let is = 0; const servisler = d.oku("servis/"), hepsi = d.oku("kopya/"); for (const [a, v] of hepsi) if (v.durum === "olu") { d.sil(a); is++; } // olu kaydi temizle for (const [a, s] of servisler) { const ad = a.slice(7), on = `kopya/${ad}-`; const k = hepsi.filter(([, v]) => v.servis === ad && v.durum !== "olu"); let n = Math.max(0, ...hepsi.filter(([b]) => b.startsWith(on)).map(([b]) => +b.slice(on.length))); for (let i = k.length; i < s.istenen; i++, is++) d.yaz(on + ++n, { servis: ad, dugum: null, durum: "bekliyor" }); for (let i = k.length; i > s.istenen; i--, is++) d.sil(k[i - 1][0]); } return is; }; // 3) Zamanlayici: dugumu olmayan kopyalara kapasitesi yeten en bos dugumu secer. export const zamanlayici = (d) => { d.bilesen("zamanlayici"); let is = 0; const dugumler = d.oku("dugum/").filter(([, v]) => v.saglik === "canli"); const kopyalar = d.oku("kopya/"); const yuk = new Map(dugumler.map(([a]) => [a, kopyalar.filter(([, v]) => v.dugum === a).length])); for (const [a, k] of kopyalar.filter(([, v]) => v.dugum === null && v.durum === "bekliyor")) { const uygun = dugumler.filter(([n, v]) => yuk.get(n) < v.kapasite); if (!uygun.length) continue; // cozumsuz: yer yok const s = uygun.reduce((x, y) => (yuk.get(x[0]) <= yuk.get(y[0]) ? x : y))[0]; yuk.set(s, yuk.get(s) + 1); d.yaz(a, { ...k, dugum: s }); is++; } return is; }; // 4) Araci sureclerin toplu karsiligi: atanmis kopyayi baslatir ve durumunu bildirir. export const dugumler = (d) => { d.bilesen("dugum araci surecleri"); let is = 0; for (const [a, k] of d.oku("kopya/")) if (k.dugum && k.durum === "bekliyor") { d.yaz(a, { ...k, durum: "calisiyor" }); is++; } return is; };
Dikkat edilecek yer bileşenlerin birbirini hiç çağırmamasıdır. Denetleyici yöneticisi zamanlayıcıya haber vermez; kopya nesnesini düğümsüz olarak deftere yazar ve orada bırakır. Zamanlayıcı da kimseye haber vermez; düğümsüz kopyaları okur, düğüm seçer, deftere yazar. Dört bileşenin ortak dili tek bir yerdir — durum deposu — ve bu seçim düşme davranışını da belirler. Deponun her yazmada artırdığı revizyon sayacı bu dilin zaman damgasıdır: bir bileşenin gördüğü defterin hangi anın defteri olduğunu ancak o sayı söyler.
Bir İsteğin Uçtan Uca İzi
// iz.mjs — tek bir istegin uctan uca izi: hangi bilesen neyi okuyor, neyi yaziyor. import { kur, arayuz, denetleyici, zamanlayici, dugumler } from "./duzlem.mjs"; export const baslangic = () => { const b = { "servis/toplayici": { istenen: 3 }, "servis/dogrulayici": { istenen: 2 } }; for (let i = 1; i <= 3; i++) b[`kopya/toplayici-${i}`] = { servis: "toplayici", dugum: `m${i}`, durum: "calisiyor" }; for (let i = 1; i <= 2; i++) b[`kopya/dogrulayici-${i}`] = { servis: "dogrulayici", dugum: `m${i}`, durum: "calisiyor" }; for (let i = 1; i <= 3; i++) b[`dugum/m${i}`] = { kapasite: 4, saglik: "canli" }; return b; }; if (import.meta.url === `file://${process.argv[1]}`) { // ucuncu blok bu dosyayi ice aktarir const d = kur(baslangic()); arayuz(d, { anahtar: "servis/toplayici", deger: { istenen: 5 } }); let tur = 0, is; do { is = denetleyici(d) + zamanlayici(d) + dugumler(d); tur++; } while (is > 0 && tur < 8); let oku = 0, yaz = 0; console.log("istek: toplayici servisinin istenen kopya sayisi 3 -> 5"); console.log("# bilesen islem anahtar kalem oku yaz"); d.iz.forEach((e, i) => { e.i === "oku" ? (oku += e.n) : yaz++; console.log(`${String(i + 1).padStart(2)} ${e.b.padEnd(23)} ${e.i.padEnd(6)} ` + `${e.a.padEnd(26)} ${String(e.n).padStart(5)} ${String(oku).padStart(4)} ${String(yaz).padStart(4)}`); }); const bilesenler = new Set(d.iz.map((e) => e.b)); console.log(`\ngecilen bilesen ${bilesenler.size} iz adimi ${d.iz.length} depo okuma ${oku}` + ` depo yazma ${yaz} revizyon 0 -> ${d.rev} tur ${tur}`); }
istek: toplayici servisinin istenen kopya sayisi 3 -> 5 # bilesen islem anahtar kalem oku yaz 1 kume arayuzu yaz servis/toplayici 1 0 1 2 denetleyici yoneticisi oku servis/* 2 2 1 3 denetleyici yoneticisi oku kopya/* 5 7 1 4 denetleyici yoneticisi yaz kopya/toplayici-4 1 7 2 5 denetleyici yoneticisi yaz kopya/toplayici-5 1 7 3 6 zamanlayici oku dugum/* 3 10 3 7 zamanlayici oku kopya/* 7 17 3 8 zamanlayici yaz kopya/toplayici-4 1 17 4 9 zamanlayici yaz kopya/toplayici-5 1 17 5 10 dugum araci surecleri oku kopya/* 7 24 5 11 dugum araci surecleri yaz kopya/toplayici-4 1 24 6 12 dugum araci surecleri yaz kopya/toplayici-5 1 24 7 13 denetleyici yoneticisi oku servis/* 2 26 7 14 denetleyici yoneticisi oku kopya/* 7 33 7 15 zamanlayici oku dugum/* 3 36 7 16 zamanlayici oku kopya/* 7 43 7 17 dugum araci surecleri oku kopya/* 7 50 7 gecilen bilesen 4 iz adimi 17 depo okuma 50 depo yazma 7 revizyon 0 -> 7 tur 2
Bir insan tek bir kalem yazdı: toplayıcı servisinin istenen kopya sayısı 3 yerine 5. Bunun karşılığı dört bileşen, on yedi iz adımı, 50 kalem okuma ve 7 yazma oldu. Yazma sayısı en öğretici olanıdır: insanın yazdığı 1 yazmaya karşılık düzlem 6 yazma daha üretti — iki kopya nesnesinin yaratılması, ikisinin düğüme atanması, ikisinin çalışır olarak bildirilmesi. Bir kalem üç kez yazılıyor, çünkü her bileşen aynı nesnenin ayrı bir alanını dolduruyor.
Kopya nesnesinin yolculuğu satırlarda görülüyor: 4. adımda düğümsüz ve bekliyor olarak doğuyor,
9. adımda bir düğüm alıyor, 12. adımda calisiyor oluyor. Üç bileşen aynı anahtara sırayla
dokunuyor ve hiçbiri ötekini beklemiyor; her biri yalnız deftere bakıp kendi işini yapıyor.
İkinci tur — 13’ten 17’ye kadar olan adımlar — bu dersin en pahalı sayısını taşır: 0 yazma, 26 kalem okuma. Fark kapandıktan sonra da her bileşen defteri baştan okudu ve yapacak iş bulamadı. Kümenin “hiçbir şey yapmamak” için ödediği bedel budur ve durmaz; ölçüm ağının sakin bir gecesinde bile denetleyici yöneticisi ile zamanlayıcı defteri taramayı sürdürür. Okuma yükü kalem sayısıyla büyür, yapılan işle değil.
Bir Bileşen Düştüğünde
// matris.mjs — bir bilesen dustugunde hangi kararlar duruyor, hangileri suruyor. import { kur, arayuz, denetleyici, zamanlayici, dugumler } from "./duzlem.mjs"; import { baslangic } from "./iz.mjs"; const OLAY = [["servis/toplayici", { istenen: 5 }], // olcek istegi ["kopya/toplayici-2", { servis: "toplayici", dugum: "m2", durum: "olu" }], // dugum bildirimi ["dugum/m4", { kapasite: 4, saglik: "canli" }]]; // yeni dugum function kos(dusen) { const d = kur(baslangic()); if (dusen === "durum deposu") { d.oku = () => []; d.yaz = () => {}; d.sil = () => {}; } for (const [a, v] of OLAY) if (dusen !== "kume arayuzu") arayuz(d, { anahtar: a, deger: v }); const bildirildi = d.veri.get("kopya/toplayici-2")?.durum === "olu"; // olum kaydedilebildi mi let tur = 0, is; do { is = (dusen === "denetleyici yoneticisi" ? 0 : denetleyici(d)) + (dusen === "zamanlayici" ? 0 : zamanlayici(d)) + dugumler(d); tur++; } while (is > 0 && tur < 8); const k = [...d.veri].filter(([a]) => a.startsWith("kopya/toplayici")); const say = (t) => k.filter(([, v]) => v.durum === t).length; const durum = (bitti, kismi) => (bitti ? "tamam" : kismi ? "yarim" : "durdu"); const sanilan = say("calisiyor"), gercek = sanilan - (bildirildi ? 0 : 1); // toplayici-2 olmustu return [dusen, durum(sanilan === 5, say("bekliyor") > 0), bildirildi ? durum(say("olu") === 0 && sanilan === 5, say("olu") === 0) : "bilmiyor", d.veri.has("dugum/m4") ? "tamam" : "durdu", dusen === "kume arayuzu" || dusen === "durum deposu" ? "durdu" : "tamam", "surer", `${sanilan}/${gercek}`, d.iz.filter((e) => e.i === "oku").reduce((t, e) => t + e.n, 0), d.iz.filter((e) => e.i !== "oku").length]; } console.log("dusen bilesen olcek ariza dugum okuma trafik sanilan/gercek oku yaz"); for (const b of ["-", "kume arayuzu", "durum deposu", "zamanlayici", "denetleyici yoneticisi"]) { const r = kos(b); console.log(`${r[0].padEnd(23)} ${r[1].padEnd(6)} ${r[2].padEnd(9)} ${r[3].padEnd(6)}` + ` ${r[4].padEnd(6)} ${r[5].padEnd(7)} ${r[6].padStart(11)} ${String(r[7]).padStart(5)}` + ` ${String(r[8]).padStart(3)}`); }
dusen bilesen olcek ariza dugum okuma trafik sanilan/gercek oku yaz - tamam tamam tamam tamam surer 5/5 52 13 kume arayuzu durdu bilmiyor durdu durdu surer 3/2 20 0 durum deposu durdu bilmiyor durdu durdu surer 3/2 0 0 zamanlayici yarim yarim tamam tamam surer 2/2 30 7 denetleyici yoneticisi durdu durdu tamam tamam surer 2/2 14 3
Trafik sütunu her satırda surer. Denetim düzleminin tamamı düşse bile çalışan kopyalar
çalışmaya devam eder; sayaç okumaları toplanır, doğrulama sürer, gecelik toplu iş başlar. Küme
“çalışmaz” hâle gelmiyor — karar veremez hâle geliyor. Bu ayrım kursun tamamında geçerlidir:
denetim düzlemi iş yapmaz, iş yapanlara ne yapacaklarını söyler.
Dört düşme senaryosu üç ayrı arıza türü üretiyor.
Küme arayüzü ya da durum deposu düştüğünde hiçbir yazma deftere inmez. Ölçek isteği kaybolur,
yeni düğüm kaydı kaybolur, okuma isteği yanıtsız kalır. En ağır satır sonuncusudur: bilmiyor.
Bir kopyanın öldüğü bilgisi de arayüzden geçtiği için bildirim hiç kaydedilmez ve defter o kopyayı
hâlâ çalışıyor sayar — sanılan 3, gerçek 2. Onarımdan sonra bile bu fark kendiliğinden kapanmaz;
kaybolan şey bir karar değil, bir olaydır, ve olayın kendisi geri gelmez. Yeniden öğrenmenin
tek yolu düğümün aynı bilgiyi yeniden bildirmesidir.
Zamanlayıcı düştüğünde karar üretilir ama yer bulunmaz. Denetleyici yöneticisi eksik kopyaları
görüp nesnelerini yaratır; nesneler düğümsüz olarak birikir. Ölçek isteği yarim kalır: defterde
beş kopya vardır, ikisi hiçbir yerde çalışmaz. Bu durumun onarımı ucuzdur — zamanlayıcı geri
geldiğinde biriken kopyaları okur ve dağıtır, çünkü iş defterde durmaktadır.
Denetleyici yöneticisi düştüğünde bilgi doğrudur, karşılığı üretilmez. Ölen kopyanın bildirimi deftere iner, defter onu ölü bilir, ama yerine yeni kopya yaratan kimse yoktur. Küme neyin yanlış olduğunu bilir ve düzeltmez. Bu da onarımla kapanır.
Ayrım tek cümleye iner: bilmemek ile bilip yapmamak ayrı arızalardır. İkincisi bileşen geri gelince kendiliğinden kapanır, çünkü fark hâlâ defterdedir. Birincisi kapanmaz, çünkü kapanması için gereken bilgi hiç yazılmamıştır.
Son iki sütun bunun maliyet tarafını veriyor. Hepsi ayaktayken defter 52 kalem okundu ve 13 yazma aldı; zamanlayıcı düşünce okuma 30’a, denetleyici yöneticisi düşünce 14’e indi. Depo yükünün büyük kısmı karar veren bileşenlerden gelir, isteklerden değil. Bir insanın tek isteği 1 yazma üretti; aynı süre içinde karar bileşenleri 52 kalem okudu.
Özet
- Tek bir insan yazması (istenen kopya 3 → 5) dört bileşenden geçti, 17 iz adımı, 50 kalem okuma ve 7 depo yazması üretti; kopya nesnesi üç ayrı bileşen tarafından üç kez yazıldı.
- Bileşenler birbirini çağırmıyor, yalnız durum deposuna bakıyor; fark kapandıktan sonraki doğrulama turu 0 yazma ve 26 kalem okuma tuttu — okuma yükü yapılan işle değil kalem sayısıyla büyüyor.
- Denetim düzleminin tamamı düşse de çalışan kopyalar hizmet vermeyi sürdürdü; kümenin kaybettiği yetenek çalışmak değil karar vermekti.
- Küme arayüzü ya da durum deposu düştüğünde ölüm bildirimi hiç kaydedilmedi ve defter kopyayı hâlâ çalışıyor saydı: sanılan 3, gerçek 2.
- Zamanlayıcı düşünce ölçek isteği yarım kaldı (defterde 5 kopya, 2’si düğümsüz); denetleyici yöneticisi düşünce bilgi doğru kalıp karşılığı üretilmedi — ikisi de onarımla kendiliğinden kapanır, bilinmeyen bildirim kapanmaz.
- Depo okuması hepsi ayaktayken 52, zamanlayıcı düşünce 30, denetleyici yöneticisi düşünce 14 oldu; yükün kaynağı istekler değil karar veren bileşenlerdi.
Sonraki Adım
Bu dersin bütün ölçümleri defterin bir tarafındaydı: kararın nasıl üretildiği ve nereye yazıldığı. Defterin öteki tarafında ise bir varsayım duruyordu — düğümlerin ne çalıştırdıklarını doğru bildirdiği. Son senaryoda o varsayımın kırıldığı bir an göründü: bildirim kaydedilemeyince küme eski bir gerçeği doğru sandı. Bir sonraki ders düğüm tarafını parçalarına ayırır — aracı süreç, çalışma zamanı, ağ vekili — ve bildirimin kendisini ölçer: ne bildiriliyor, ne sıklıkta, ne kadar gecikmeyle, ve bir bildirim kaybolduğunda küme ne kadar süre eski gerçeği doğru sanıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.