Ders 04 / 30
Uzlaşma Döngüsü
İstenen durum ile gerçek durumun sürekli karşılaştırılması küme ölçeğinde çalışan bir döngüyle yazılır ve rapor gecikmesi taranır: sistem kaç gereksiz kopya açıyor, kaç kopyayı boşuna siliyor, salınımın genliği ne. Döngü periyodu ile rapor gecikmesi arasındaki bağıntı elli altı hücrede sınanır ve eksik bilgi kaleminin sistemin kendi geçmişi olduğu gösterilir.
İçindekiler
Bu konunun ilk üç dersi iki tarafı ayrı ayrı ölçtü. Şimdi ikisini birbirine bağlayan şey yazılır: istenen durum ile gerçek durumun sürekli karşılaştırılıp farkın kapatılması, yani uzlaşma döngüsü. Döngünün kendisi yeni değildir — daha önce tek makinede çalışan bir uzlaştırıcı görülmüştü: istenen durumu dosyadan, gerçek durumu diskten okuyor, yalnız farklı kaleme dokunuyor ve ikinci koşumda sıfır iş yapıyordu. O ölçüm burada girdi olarak alınır ve tekrarlanmaz.
Küme ölçeğinde tek bir şey değişir. Gerçek durum artık diskten okunmaz; düğümlerden gelen ve bir önceki dersin ölçtüğü gecikmeyi taşıyan raporlardan gelir. Bu dersin tamamı o tek farkın bedelini sayar: gecikmeli bir görüntüye bakarak verilen doğru kararlar, kaç yanlış düzeltme üretiyor.
Ölçüm yine bölgesel ölçüm ağı üzerindedir ve kurgudur; koşan şey node ile kurulmuş bir
modeldir, gerçek bir küme değil.
KM19. İstenen durum tek kalemdir: toplayıcı servisinin kopya sayısı altıdır ve insanın yazdığı bildirimden gelir. KM20. Gerçek durum düğüm raporlarından gelir; defter gerçek dünyanın G saniye gerisindedir. KM21. Yaratılan bir kopya B saniye sonra çalışır hâle gelir, çalıştığı bilgisi G saniye sonra deftere düşer; silinen bir kopya da defterden G saniye sonra kaybolur. KM22. Döngü periyodu U’dur; her turda defter okunur, fark hesaplanır, düzeltme yazılır. KM23. Olaylar yazılıdır, rastgelelik yoktur: 300. saniyede iki kopya ölür; koşum 900 saniyedir. KM24. İki denetleyici karşılaştırılır — yalnız deftere bakan, ve kendi bekleyen eylemlerini de sayan. KM25. Silme komutu defterde görünene verilir; zaten ölmüş bir kopyaya verilen komut iş olarak sayılır ama canlı kopya sayısını değiştirmez.
Küme Ölçeğinde Uzlaşma Döngüsü
// uzlas.mjs — kume olceginde uzlasma dongusu. Istenen durum bildirimden, gercek durum // gecikmeli raporlardan gelir. Model; gercek kume kosturulmaz. Rastgelelik yok, olaylar yazili. export const OLUM = [[300, 2]]; // yazili olay: 300. saniyede 2 kopya olur export function kos({ istenen = 6, U = 15, G = 0, B = 5, sure = 900, niyet = false }) { const gercek = []; // {dogum, hazir, olum} let yaratildi = 0, komut = 0, silindi = 0, is = 0, tur = 0, asim = 0, sonSapma = 0; const canli = (t) => gercek.filter((r) => r.hazir <= t && (r.olum === null || r.olum > t)); const gorunen = (t) => gercek.filter((r) => // defter G saniye geride r.hazir + G <= t && (r.olum === null || r.olum + G > t)); for (let t = 0; t <= sure; t++) { for (const [an, kac] of OLUM) // yazili olum: en eski kopyalar duser if (t === an) canli(t).slice(0, kac).forEach((r) => { r.olum = t; }); if (t % U === 0) { tur++; const g = gorunen(t); const bekleyen = niyet ? gercek.filter((r) => r.olum === null && r.hazir + G > t).length : 0; const fark = istenen - g.length - bekleyen; // niyet: kendi bekleyen eylemlerini say if (fark > 0) { for (let i = 0; i < fark; i++) gercek.push({ dogum: t, hazir: t + B, olum: null }); yaratildi += fark; is += fark; } else if (fark < 0) { komut += -fark; is += -fark; // silme komutu: defterde gorunene verilir g.slice(0, -fark).forEach((r) => { if (r.olum === null) { r.olum = t; silindi++; } }); } } const c = canli(t).length; asim = Math.max(asim, c - istenen); if (c !== istenen) sonSapma = t; } const gerekli = istenen + OLUM.reduce((s, k) => s + k[1], 0); return { tur, isTur: is / tur, yaratildi, gereksiz: yaratildi - gerekli, komut, silindi, asim, kararli: sonSapma === sure ? null : sonSapma + 1 }; } const satir = (ilk, r) => `${ilk}${r.isTur.toFixed(2).padStart(8)}${String(r.yaratildi).padStart(11)}` + `${String(r.gereksiz).padStart(10)}${String(r.komut).padStart(14)}${String(r.silindi).padStart(9)}` + `${String(r.asim).padStart(6)}${(r.kararli === null ? "yok" : r.kararli).toString().padStart(13)}`; if (import.meta.url === `file://${process.argv[1]}`) { console.log("U=15 sn dongu, B=5 sn baslama, istenen 6 kopya, 300. sn'de 2 olum, 900 sn kosum"); for (const niyet of [false, true]) { console.log(`\ndenetleyici: ${niyet ? "defter + kendi bekleyen eylemleri" : "yalniz defter"}`); console.log("G(sn) is/tur yaratilan gereksiz silme komutu silinen asim kararli(sn)"); for (const G of [0, 5, 10, 20, 30, 60]) console.log(satir(String(G).padStart(5), kos({ G, niyet }))); } }
U=15 sn dongu, B=5 sn baslama, istenen 6 kopya, 300. sn'de 2 olum, 900 sn kosum
denetleyici: yalniz defter
G(sn) is/tur yaratilan gereksiz silme komutu silinen asim kararli(sn)
0 0.13 8 0 0 0 0 305
5 0.13 8 0 0 0 0 320
10 0.13 8 0 0 0 0 320
20 0.52 16 8 16 8 6 375
30 0.92 24 16 32 16 12 405
60 2.75 40 32 128 32 24 495
denetleyici: defter + kendi bekleyen eylemleri
G(sn) is/tur yaratilan gereksiz silme komutu silinen asim kararli(sn)
0 0.13 8 0 0 0 0 305
5 0.13 8 0 0 0 0 320
10 0.13 8 0 0 0 0 320
20 0.13 8 0 0 0 0 335
30 0.13 8 0 0 0 0 335
60 0.13 8 0 0 0 0 365
Üst tablonun ilk üç satırı tanıdıktır. Gecikme sıfır, beş ya da on saniyeyken döngü sekiz kopya yarattı — altısı ilk kurulum, ikisi ölenlerin yerine — hiçbirini boşuna silmedi, hiç aşım yapmadı. Tur başına iş 0,13’tür: kırk bir turun otuz sekizi sıfır iş yaptı. Tek makinede ölçülen etkisizlik küme ölçeğinde de duruyor.
Dördüncü satırda döngü davranış değiştirir. Yirmi saniyelik rapor gecikmesinde döngü on altı kopya yarattı; sekizi gereksizdi. Sekiz kopya boşuna silindi ve canlı kopya sayısı altı yerine on ikiye çıktı. Altmış saniyelik gecikmede tablo iyice bozulur: kırk kopya yaratıldı, otuz ikisi gereksiz; yüz yirmi sekiz silme komutu verildi; aşım yirmi dört, yani bir ara otuz kopya çalıştı.
Bu satırlarda dikkat edilecek yer denetleyicinin hiçbir hata yapmamış olmasıdır. Her turda defteri okudu, farkı doğru hesapladı ve tam da o farkı kapatacak düzeltmeyi yazdı. Kural doğru, aritmetik doğru, girdi eski. Kopyalar yaratıldı ama henüz raporlanmadıkları için denetleyici bir sonraki turda onları göremedi ve aynı eksiği yeniden gördü. Yanlış düzeltmenin kaynağı karar kuralı değil, kararın dayandığı görüntünün yaşıdır.
Silme komutu ile gerçekten silinen kopya arasındaki fark aynı körlüğün ikinci yüzüdür. Altmış saniyelik gecikmede 128 silme komutu verildi, bunların yalnız 32’si canlı bir kopyaya denk geldi; kalan 96 komut zaten ölmüş kopyalara gitti. Sistem yalnız kendi yarattığını değil, kendi sildiğini de gecikmeyle görür.
Ölçüm ağında bunun karşılığı somuttur: altı kopyalık toplayıcı servisi bir ara otuz kopyayla çalıştı, arkasındaki veritabanına beş kat bağlantı açıldı ve aynı sayaç okuma partileri birden çok kopya tarafından çekildi. Salınım bir gösterge sorunu değil, gerçek bir yük. Üstelik bu yükün hiçbir kaydı, denetleyicinin “altı kopya istendi, altı kopya var” diyen son satırında görünmez; salınım kapandıktan sonra geriye yalnız doğru bir son durum kalır.
Döngü Periyodu ile Rapor Gecikmesinin İlişkisi
// oran.mjs — dongu periyodu U ile rapor gecikmesi G arasindaki iliski sayiyla. import { kos } from "./uzlas.mjs"; const B = 5, ISTENEN = 6; const ongoru = (G, U) => ISTENEN * (Math.ceil((G + B) / U) - 1); // sinanacak baginti console.log("G=30 sn sabit, B=5 sn; dongu periyodu taraniyor"); console.log("U(sn) ceil((G+B)/U) ongorulen asim olculen asim gereksiz silinen is/tur"); for (const U of [5, 10, 15, 20, 30, 35, 60]) { const r = kos({ U, G: 30, B }); console.log(`${String(U).padStart(5)}${String(Math.ceil(35 / U)).padStart(15)}` + `${String(ongoru(30, U)).padStart(16)}${String(r.asim).padStart(14)}` + `${String(r.gereksiz).padStart(10)}${String(r.silindi).padStart(9)}` + `${r.isTur.toFixed(2).padStart(8)}`); } console.log("\nasim tablosu (satir: rapor gecikmesi G, sutun: dongu periyodu U)"); const U_LER = [5, 10, 15, 20, 30, 45, 60, 90]; console.log("G\\U " + U_LER.map((u) => String(u).padStart(5)).join("")); let sapma = 0, hucre = 0; for (const G of [0, 10, 20, 30, 45, 60, 90]) { const s = U_LER.map((U) => { const a = kos({ U, G, B }).asim; hucre++; if (a !== ongoru(G, U)) sapma++; return String(a).padStart(5); }); console.log(String(G).padStart(3) + " " + s.join("")); } console.log(`\nbaginti asim = istenen x (ceil((G+B)/U) - 1); ${hucre} hucrenin ${sapma} tanesi sapiyor`);
G=30 sn sabit, B=5 sn; dongu periyodu taraniyor
U(sn) ceil((G+B)/U) ongorulen asim olculen asim gereksiz silinen is/tur
5 7 36 36 48 48 1.90
10 4 18 18 24 24 1.14
15 3 12 12 16 16 0.92
20 2 6 6 8 8 0.70
30 2 6 6 8 8 0.77
35 1 0 0 0 0 0.31
60 1 0 0 0 0 0.50
asim tablosu (satir: rapor gecikmesi G, sutun: dongu periyodu U)
G\U 5 10 15 20 30 45 60 90
0 0 0 0 0 0 0 0 0
10 12 6 0 0 0 0 0 0
20 24 12 6 6 0 0 0 0
30 36 18 12 6 6 0 0 0
45 54 24 18 12 6 6 0 0
60 72 36 24 18 12 6 6 0
90 108 54 36 24 18 12 6 6
baginti asim = istenen x (ceil((G+B)/U) - 1); 56 hucrenin 0 tanesi sapiyor
Üst tablo tek bir gecikmeyi (30 saniye) sabit tutup döngü periyodunu tarıyor ve beklenmedik yönü gösteriyor: döngüyü hızlandırmak zararı büyütür. Beş saniyelik periyotta aşım otuz altı, gereksiz kopya kırk sekizdir; otuz beş saniyelik periyotta ikisi de sıfırdır. Sık uzlaşan bir döngü, gecikme sabitken aynı eski görüntüyü daha çok kez okur ve her okumada aynı eksiği yeniden kapatmaya çalışır.
Alt tablo bağıntıyı verir. Aşım, ölçülen bütün hücrelerde şu değerdedir:
aşım = istenen kopya × (⌈(G + B) / U⌉ − 1)
Elli altı hücrenin hiçbirinde sapma yok. Bağıntının okunuşu basittir: (G + B) / U, bir kopyanın
yaratılmasından defterde görünmesine kadar geçen sürede kaç tur koştuğudur. O turların ilki
gerçek eksiği kapatır; kalanların her biri aynı eksiği bir kez daha kapatır. Kör tur sayısı bir
olduğunda — yani U ≥ G + B — aşım sıfırlanır ve tablonun sıfır bölgesi tam olarak bu köşegenin
üstünde kalır.
Bu, bir önceki dersin canlılık eşiği kuralının kardeşidir. Orada eşiğin en uzun geçici kesintiyi artı bir bildirim periyodunu geçmesi gerekiyordu; burada döngü periyodunun rapor gecikmesini artı başlama süresini geçmesi gerekiyor. İkisinde de kümenin ayarı, kümenin kendi ölçmediği bir süreye bağlıdır ve ikisinde de yanlış ayarın alarmı yoktur — salınım sessizce çalışır. Bağıntının kendisi de bu yüzden işletim değeri taşır: aşımı görmeden, yalnız üç süreyi bilerek hesaplamak mümkündür.
G burada serbest bir ayar değildir; bir önceki ders onu ölçmüştü. Dört aşamalı bildirim
zincirinin ortalaması 13,53, en kötüsü 26,36 saniyeydi. Beş saniyelik başlama süresiyle birlikte
G + B ortalamada 18,5, en kötü durumda 31,4 saniye eder. On beş saniyelik bir döngü periyodu bu
sayıların ikisinin de altındadır — yani ölçülen gecikmelerle çalışan bir küme, hiçbir şey
bozulmadan, varsayılan ayarında salınım bölgesindedir. Tablodaki U=15, G=20 satırı bir uç
durum değil, sıradan bir gündür.
Döngünün taban maliyeti de burada görünür. Tur başına iş 0,13’tür ve turların çoğu sıfır iş yapar, ama sıfır iş yapan tur bedava değildir: her tur defteri baştan okur. Bir önceki dersin doğrulama turu bu okumayı 26 kalem olarak saymıştı. Döngünün maliyeti yaptığı düzeltmelerle değil, kalem sayısı ile periyodun oranıyla büyür; periyodu küçültmek hem okuma yükünü hem — gecikme sabitse — aşımı büyütür.
Eksik Olan Kalem Sistemin Kendi Geçmişidir
Alt tablo ikinci denetleyiciyi gösteriyor ve sonuç keskindir: kendi bekleyen eylemlerini sayan denetleyici bütün gecikme değerlerinde sekiz kopya yarattı, sıfır gereksiz, sıfır silme, tur başına 0,13 iş. Aşım her satırda sıfırdır.
Eklenen bilgi kalemi tek bir satırdır ve dünyayla ilgili değildir: son turlarda ne yarattığımın kaydı. Denetleyici artık “defterde altı yerine dört kopya görüyorum” demiyor; “defterde dört kopya görüyorum ve iki tanesini iki tur önce ben yarattım, henüz görünmemiş olabilirler” diyor. Kursun ölçüsü buraya oturur: devredilen karar doğru verilebilsin diye eklenen kalem, çoğu zaman bir ölçüm değil, sistemin kendi eylemlerinin hafızasıdır — ve bu kalem bedavaya yakındır, çünkü kimseden bildirim istemez.
Bedavaya yakındır ama her şeyi çözmez. İki tablonun son sütunu karşılaştırıldığında görülür: niyet kaydı olan denetleyicide de kararlı hâle gelme anı gecikmeyle birlikte büyüyor — 305 saniyeden 365 saniyeye. Ölen iki kopyanın yerine yenisi, ölüm defterde görünene kadar açılamaz. Ayrım tek cümleye iner: gecikme fark etmeyi geciktirir, kayıtsız niyet ise işi çoğaltır. Bunlar ayrı iki sorundur; ikincisi bir satırla kapanır, birincisi kapanmaz — yalnız bildirim periyodu ve ağ gecikmesi küçültülerek pahalıya azaltılır — ve bir önceki dersin gösterdiği gibi, o periyotları küçültmenin faturası düğüm başına bildirim yükü olarak geri gelir.
Özet
- Rapor gecikmesi sıfır, 5 ve 10 saniyeyken döngü 8 kopya yarattı, hiç boşuna silme yapmadı ve 41 turun 38’i sıfır iş yaptı; tek makinede ölçülen etkisizlik küme ölçeğinde de duruyor.
- 20 saniyelik gecikmede 8 gereksiz kopya açıldı, 8 kopya boşuna silindi, canlı sayı 6 yerine 12’ye çıktı; 60 saniyede 32 gereksiz kopya, 128 silme komutu ve 30 canlı kopyalık bir salınım oluştu.
- 128 silme komutunun yalnız 32’si canlı bir kopyaya denk geldi; sistem kendi yarattığını da, kendi sildiğini de gecikmeyle görüyor.
- Aşım bütün taramalarda
istenen × (⌈(G+B)/U⌉ − 1)çıktı; 56 hücrede sıfır sapma. Aşımın sıfırlandığı sınırU ≥ G + B, yani döngü periyodunun rapor gecikmesi artı başlama süresini geçmesidir. - Döngüyü hızlandırmak zararı büyüttü: 30 saniyelik gecikmede periyot 5 saniyeyken aşım 36, periyot 35 saniyeyken 0.
- Kendi bekleyen eylemlerini sayan denetleyici bütün gecikmelerde 0 gereksiz kopya ve 0 boşuna silme yaptı; kararlı olma anı yine de 305’ten 365 saniyeye çıktı — gecikme fark etmeyi geciktirir, kayıtsız niyet işi çoğaltır.
Sonraki Adım
Bu ders döngünün kendisini ölçtü: neyi okuduğunu, ne sıklıkta koştuğunu, gecikmeli bir görüntüyle ne kadar yanlış iş yaptığını. Konunun son dersi bir işletim kararına döner — bu düzlemi kendin mi işleteceksin, yoksa işletimini başkasına mı devredeceksin. Ölçülecek şey iki seçeneğin aynı kalemlerde karşılaştırılmasıdır: devredilen işletim kalemi sayısı, elde kalan kalem, sürüm yükseltme penceresi, denetim düzlemine erişimin kaybı ve kararın geri alınabilirliği.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.