İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat