Ders 07 / 30
Kopya Kümeleri ve Sürüm Nesneleri
Bildirimsel sürüm yönetiminin iki ölçüsü: istenen kopya sayısı ile gerçek kopya sayısını uzlaştıran denetleyicinin tur başına işi, ve geçiş ayarlarının — kaç fazla, kaç eksik kopya — geçiş turu, kapasite altı tur ve geri alma penceresi üzerindeki etkisi.
İçindekiler
Önceki ders kapsülün sınırını çizdi ve altı kopya gerektiğini bir varsayım olarak aldı. O varsayım tek başına hiçbir şey yapmaz: bir kapsül düşerse yerine yenisi gelmez, bir düğüm kaybolursa üzerindeki kapsüller de kaybolur. Altı sayısının bir etkisi olması için bir yere bildirilmesi ve bir denetleyicinin o bildirimi sürekli gerçekle karşılaştırması gerekir. Bu bildirimi taşıyan nesne kopya kümesidir: tek bir kapsül şablonu ve tek bir sayı.
Sayı yeterli değildir. Şablonun kendisi de değişir — yeni bir sürüm çıkar ve altı kapsülün içeriği
değişmesi gerekir. Bunu yöneten nesne sürüm nesnesidir; iki kopya kümesi tutar ve istenen sayıyı
ikisi arasında adım adım kaydırır. Bu ders iki nesneyi de node ile yazar ve geçişi tur tur sayar.
Ölçülen şey bir modeldir; gerçek bir küme koşturulmaz.
TN7 — okuma toplayıcı için istenen kopya sayısı altıdır. TN8 — denetleyicinin bildiği tek şey istenen sayı ile gerçek sayıdır; kapsüllerin içi ona görünmez. TN9 — yeni oluşturulan kapsül iki tur sonunda hazır olur, silme aynı turda biter. TN10 — sürüm nesnesi iki kopya kümesi tutar ve istenen sayıyı ikisi arasında paylaştırır. TN11 — geçiş ayarı iki sayıdır: en çok kaç fazla kapsül, en çok kaç eksik hazır kopya. TN12 — yeni sürümün bozukluğu dördüncü turun sonunda anlaşılır; geri alma aynı ayarlarla ters yönde yürür.
İstenen Sayı ile Gerçek Sayı
Denetleyicinin yaptığı iş tek satırdır: farkı al, işareti neyse o yöne bir eylem üret. Aşağıdaki koşum bu tek satırı dört olayın üzerinden geçiriyor — soğuk başlangıç, iki kapsülün çökmesi, istenen sayının değişmesi ve dışarıdan elle iki kapsül eklenmesi.
// kopya.mjs — kopya kumesi denetleyicisi: istenen ile gercek kopya sayisini uzlastirir (MODEL). export const HAZIR_TUR = 2; // TN9: yeni kapsul 2 turda hazir olur export const kume = (ad, istenen) => ({ ad, istenen, hazir: 0, bekleyen: [] }); export const toplam = (k) => k.hazir + k.bekleyen.length; export function ilerle(k) { // bekleyen kapsuller hazir olur k.bekleyen = k.bekleyen.map((x) => x - 1); const olan = k.bekleyen.filter((x) => x <= 0).length; k.hazir += olan; k.bekleyen = k.bekleyen.filter((x) => x > 0); return olan; } export const olustur = (k, n) => { for (let i = 0; i < n; i++) k.bekleyen.push(HAZIR_TUR); }; export function sil(k, n) { // once bekleyen, sonra hazir silinir let kalan = n; while (kalan > 0 && k.bekleyen.length) { k.bekleyen.pop(); kalan--; } const d = Math.min(kalan, k.hazir); k.hazir -= d; return n - (kalan - d); } // Denetleyici: farki bir eyleme cevirir. Bildigi tek sey istenen ve gercek sayidir. TN8. export function uzlas(k) { const fark = k.istenen - toplam(k); if (fark > 0) { olustur(k, fark); return `+${fark} olustur`; } if (fark < 0) { sil(k, -fark); return `${fark} sil`; } return 'fark yok'; } if (import.meta.url === `file://${process.argv[1]}`) { const K = kume('okuma-toplayici', 6); // TN7 const OLAY = { 4: ['iki kapsul cokti', (k) => (k.hazir -= 2)], 8: ['istenen 8 oldu', (k) => (k.istenen = 8)], 11: ['elle iki kapsul eklendi', (k) => olustur(k, 2)] }; const s = (x, n) => String(x).padStart(n); let olusan = 0, silinen = 0, eksikTur = 0; console.log(`${'tur'.padEnd(4)}${'olay'.padEnd(25)}${s('istenen', 8)}${s('hazir', 6)}` + `${s('bekleyen', 9)} eylem`); for (let t = 1; t <= 14; t++) { const o = OLAY[t]; if (o) o[1](K); ilerle(K); const once = toplam(K); const eylem = uzlas(K); const d = toplam(K) - once; if (d > 0) olusan += d; else silinen += -d; if (K.hazir < K.istenen) eksikTur++; console.log(`${String(t).padEnd(4)}${(o ? o[0] : '').padEnd(25)}${s(K.istenen, 8)}` + `${s(K.hazir, 6)}${s(K.bekleyen.length, 9)} ${eylem}`); } console.log(`\n14 turda ${olusan} olusturma, ${silinen} silme, ${eksikTur} turda hazir sayisi` + ` istenenin altinda`); }
tur olay istenen hazir bekleyen eylem 1 6 0 6 +6 olustur 2 6 0 6 fark yok 3 6 6 0 fark yok 4 iki kapsul cokti 6 4 2 +2 olustur 5 6 4 2 fark yok 6 6 6 0 fark yok 7 6 6 0 fark yok 8 istenen 8 oldu 8 6 2 +2 olustur 9 8 6 2 fark yok 10 8 8 0 fark yok 11 elle iki kapsul eklendi 8 8 0 -2 sil 12 8 8 0 fark yok 13 8 8 0 fark yok 14 8 8 0 fark yok 14 turda 10 olusturma, 2 silme, 6 turda hazir sayisi istenenin altinda
Dört olay dört ayrı gerekçeden geliyor, denetleyicinin verdiği yanıt ise tek biçimdir. Soğuk başlangıçta altı, çöküşte iki, istenen sayı arttığında iki kapsül oluşturulur; hepsinde uygulanan kural aynıdır ve hiçbirinde neden düşüldüğü sorulmaz. Denetleyici çöküşle elle silmeyi ayırt etmez, çünkü TN8 gereği ikisi de aynı sayıya iner. Bu, devretmenin en somut kazancıdır: dört farklı senaryo için dört ayrı yordam yazılmaz.
On birinci tur bunun ters yüzüdür. Dışarıdan eklenen iki kapsül aynı turda silinir; bildirimin
dışında yapılan bir değişiklik, bir sonraki uzlaşma turundan uzun yaşamaz. Kümede elle yapılan iş
kalıcı değildir ve bunu gösteren tek kanıt tablodaki -2 sil satırıdır.
Sayıların bedeli ise ikinci ve beşinci turlarda görünüyor: fark yok yazan iki turda gerçekten hiç
iş yapılmaz, ama hazır kopya sayısı hâlâ istenenin altındadır. Toplam ölçü altı turdur — on dört
turun altısında küme eksik kapasiteyle çalıştı. Bunun tamamı TN9’dan, yani hazır olmanın iki
tur sürmesinden gelir; denetleyici farkı gördüğü turda kapatamaz, yalnız kapatma emrini verir.
Kapasite açığı, kararın verilmesiyle etkisinin görünmesi arasındaki gecikmedir.
Tablonun on turunda yazan fark yok da bir sonuçtur. Aynı kural her turda yeniden koşar ve fark
kapalıysa hiçbir şey yapmaz; bu, uzlaşma döngüsünün küme ölçeğinde de değişmeyen özelliğidir.
Denetleyici bir kez tetiklenip biten bir yordam değil, sürekli koşan ve çoğu turda hiçbir iş
üretmeyen bir karşılaştırmadır. Tur başına iş, on dört turun dördünde sıfırdan farklıdır.
Geçişin Ayarları
Kopya sayısı sabit kalırken şablon değişirse iş uzlaşma olmaktan çıkar, bir geçişe dönüşür. Sürüm nesnesi iki kopya kümesini aynı anda yönetir ve iki sayıya uyar: aynı anda en çok kaç fazla kapsül olabilir, aynı anda en çok kaç kopya eksik olabilir.
// gecis.mjs — surum nesnesi: iki kopya kumesi arasinda asamali gecis ve geri alma (MODEL). import { kume, toplam, ilerle, olustur, sil } from './kopya.mjs'; const N = 6; // TN7: istenen kopya sayisi // fazla: en cok kac fazla kapsul; eksik: en cok kac eksik hazir kopya. TN11. function gecis(fazla, eksik, bozukTur = null) { const eski = kume('surum-1', N), yeni = kume('surum-2', 0); eski.hazir = N; const olusan = { 'surum-1': 0, 'surum-2': 0 }; let t = 0, altTur = 0, tepe = N, silinen = 0, geriTur = null, durgun = 0, yon = 'ileri'; while (t < 80) { t++; ilerle(eski); ilerle(yeni); if (bozukTur && t === bozukTur && yon === 'ileri') { yon = 'geri'; geriTur = t - 1; } const hedef = yon === 'ileri' ? yeni : eski, kaynak = yon === 'ileri' ? eski : yeni; const yer = Math.min(N + fazla - (toplam(eski) + toplam(yeni)), N - toplam(hedef)); if (yer > 0) { olustur(hedef, yer); olusan[hedef.ad] += yer; } const dus = Math.min(eski.hazir + yeni.hazir - (N - eksik), toplam(kaynak)); if (dus > 0) { sil(kaynak, dus); silinen += dus; } tepe = Math.max(tepe, toplam(eski) + toplam(yeni)); if (eski.hazir + yeni.hazir < N) altTur++; durgun = yer <= 0 && dus <= 0 && !eski.bekleyen.length && !yeni.bekleyen.length ? durgun + 1 : 0; if (durgun >= 2) return { tur: null, altTur, tepe, olusan, silinen, geriTur }; if (toplam(kaynak) === 0 && hedef.hazir >= N) break; } return { tur: t, altTur, tepe, olusan, silinen, geriTur }; } const AYAR = [[0, 0], [0, 1], [1, 0], [1, 1], [2, 2], [6, 0]]; const s = (x, n) => String(x).padStart(n); console.log(`istenen kopya ${N}, hazir olma 2 tur`); console.log(`${s('fazla', 6)}${s('eksik', 6)}${s('tur', 11)}${s('kapasite alti', 15)}` + `${s('tepe kapsul', 13)}${s('olusan', 8)}${s('silinen', 9)}`); for (const [f, e] of AYAR) { const r = gecis(f, e), o = r.olusan['surum-1'] + r.olusan['surum-2']; console.log(`${s(f, 6)}${s(e, 6)}${s(r.tur ?? 'ilerlemez', 11)}${s(`${r.altTur} tur`, 15)}` + `${s(r.tepe, 13)}${s(o, 8)}${s(r.silinen, 9)}`); } console.log(`\nyeni surum 4. turun sonunda bozuk cikarsa (TN12):`); console.log(`${s('fazla', 6)}${s('eksik', 6)}${s('ileri tur', 11)}${s('geri tur', 10)}` + `${s('toplam', 8)}${s('kapasite alti', 15)}${s('bosa olusan', 13)}`); for (const [f, e] of AYAR.slice(1)) { const r = gecis(f, e, 5); if (r.geriTur === null) { console.log(`${s(f, 6)}${s(e, 6)} gecis 4. turdan once bitti: geri alinacak eski kopya yok`); continue; } console.log(`${s(f, 6)}${s(e, 6)}${s(r.geriTur, 11)}${s(r.tur - r.geriTur, 10)}` + `${s(r.tur, 8)}${s(`${r.altTur} tur`, 15)}${s(r.olusan['surum-2'], 13)}`); }
istenen kopya 6, hazir olma 2 tur
fazla eksik tur kapasite alti tepe kapsul olusan silinen
0 0 ilerlemez 0 tur 6 0 0
0 1 19 18 tur 6 6 6
1 0 18 0 tur 7 6 6
1 1 10 9 tur 7 6 6
2 2 6 5 tur 8 6 6
6 0 3 0 tur 12 6 6
yeni surum 4. turun sonunda bozuk cikarsa (TN12):
fazla eksik ileri tur geri tur toplam kapasite alti bosa olusan
0 1 4 6 10 9 tur 1
1 0 4 5 9 0 tur 2
1 1 4 6 10 9 tur 3
2 2 4 6 10 9 tur 6
6 0 gecis 4. turdan once bitti: geri alinacak eski kopya yok
İlk satır iki sayının anlamını en keskin biçimde veriyor: ikisi de sıfırsa geçiş başlamaz. Ne fazladan bir kapsüle yer vardır ne de bir kopyayı eksiltmeye izin; sürüm nesnesi kuralları ihlal etmeden hiçbir adım atamaz ve model durgunluğu görüp çıkar. Bildirimsel bir nesnenin ilerlemek için en az bir yönde esneklik istemesi, bu tablonun ilk sonucudur.
Sonraki iki satır aynı sayıda kapsül oluşturup silmelerine rağmen taban tabana zıt maliyet üretiyor. Bir eksiğe izin verip fazlaya izin vermemek geçişi on dokuz tura yayar ve bunların on sekizinde küme eksik kapasiteyle çalışır; tepe kapsül sayısı altıda kalır, yani hiç ek kaynak harcanmaz. Bir fazlaya izin verip eksiğe izin vermemek on sekiz tur sürer, kapasitenin altına hiç düşmez ve karşılığında yalnız bir kapsüllük ek kaynak ister. İki ayar arasındaki tur farkı birdir; kapasite farkı on sekiz turdur. Ek kaynak vermeye razı olmak, aynı süreyi kesintisiz geçirmenin bedelidir.
Alttaki iki satır hızın nereden geldiğini gösteriyor. Bir fazla ve bir eksik birlikte tanındığında tur sayısı on sekizden ona iner; iki ve iki tanındığında altıya. Hız her iki ayardan da beslenir, ama kapasite altı turlar da onunla birlikte gelir. Son satır bu eğrinin ucudur: altı fazla kapsüle izin verildiğinde geçiş üç turda biter, kapasite hiç düşmez — tepe kapsül sayısı on ikidir, yani kaynak bir süreliğine iki katına çıkar. Bu ayar, iki ortamlı geçişin sayısal karşılığıdır.
İkinci tablo o üç turluk hızın gizli bedelini ödetiyor. Dördüncü turun sonunda yeni sürümün bozuk olduğu anlaşıldığında, bir fazla ve sıfır eksik ayarında geri alma beş tur sürer, kapasite hiç düşmez ve boşa giden kapsül sayısı ikidir. İki-iki ayarında geri alma altı tur alır ve altı kapsül boşa gitmiştir — dört tur içinde yeni sürümün tamamı kurulmuştur. Altı fazlalı ayarda ise geri alma diye bir şey yoktur: geçiş dördüncü turdan önce bitmiş, eski kopya kümesi sıfırlanmıştır. Geri alma penceresi, geçişin süresiyle aynı şeydir. Geçişi hızlandıran her ayar bu pencereyi kısaltır ve bir noktadan sonra kapatır.
Devredilen Karar ve Bilmediği Şey
Bu derste devredilen karar iki tanedir: kaç kopya koşacağı ve yeni şablona hangi hızla geçileceği. Birincisi için sistemin bilmesi gereken tek bilgi kalemi istenen sayıdır — bu yüzden ucuzdur ve dört farklı olaya tek kuralla yanıt verir. İkincisi için iki kalem gerekir: fazla ve eksik. Bu iki sayı bir tercih değil, iki kaynağın karşılıklı fiyatıdır; biri geçici kapasite, öteki geçici kesinti.
İki kalemin maliyeti de eşit değildir. İstenen sayı bir kez yazılır ve nadiren değişir; ölçüm gerektirmez, eskimez, yanlış olduğunda sonucu doğrudan görünür. Geçiş ayarları ise her yayında yeniden değerlendirilmesi gereken bir tahmine dayanır: yeni sürümün ne kadar sürede ayağa kalkacağı ve arızalıysa ne kadar sürede belli olacağı. İkinci tahmin ölçülemez olduğu için ayarlar çoğu zaman bir kez yazılır ve her yayında aynı kalır — oysa tablonun gösterdiği gibi aynı ayar, hızlı ayağa kalkan bir sürümde geri alma penceresini tümüyle kapatabilir.
Sistemin bilmediği şey de aynı tabloda duruyor. Sürüm nesnesi yeni kopyaların hazır olduğunu görür, doğru çalıştığını görmez; TN8 gereği kapsülün içi ona kapalıdır. Bozukluk dördüncü turun sonunda bir yerden gelir ve modelde bu bir varsayımdır. Sistemin ölçemediği bu bilgi kalemi yüzünden iki-iki ayarında altı kapsül boşa kurulur, altı fazlalı ayarda ise eski sürüm hiç kalmadığı için geri dönülecek yer kalmaz. Yanlış karar burada bir hesap hatası değildir: hız, sistemin göremediği bir bilginin gelmesini beklemeden ilerlemektir.
Özet
- Kopya kümesi denetleyicisi tek kural uygular: fark artıysa oluştur, eksiyse sil. Soğuk başlangıç, çöküş, istenen sayının değişmesi ve elle ekleme aynı yordamla karşılanır.
- Bildirimin dışında elle eklenen iki kapsül aynı turda silindi; kümede elle yapılan iş bir sonraki uzlaşma turundan uzun yaşamaz.
- On dört turun altısında hazır kopya sayısı istenenin altında kaldı; bunun kaynağı hazır olmanın iki tur sürmesi, yani kararla etkisi arasındaki gecikmedir.
- Geçiş ayarının iki sayısı da sıfırsa geçiş hiç başlamaz; bir eksiğe izin vermek on dokuz turun on sekizini kapasite altında geçirtir, bir fazlaya izin vermek aynı işi on sekiz turda ve hiç kapasite düşürmeden yapar.
- İki fazla ve iki eksik geçişi altı tura, altı fazla üç tura indirir; ikincisinde tepe kapsül sayısı on ikiye, yani iki katına çıkar.
- Geri alma penceresi geçişin süresiyle aynıdır: dördüncü turda fark edilen bozukluk bir-sıfır ayarında iki kapsülü, iki-iki ayarında altı kapsülü boşa çıkarır; üç turda biten geçişte geri dönülecek eski kopya kalmaz.
Sonraki Adım
Bu ders altı kapsülü ayakta tutmayı ve içeriğini değiştirmeyi ölçtü. İki koşumda da kapsüller sürekli oluşturulup silindi: on dört turda on oluşturma iki silme, geçişte altı oluşturma altı silme. Her yeni kapsül yeni bir kapsüldür ve öncekinin adresi onunla gitmiştir. Bu durumda ona istek gönderen bir başka bileşen kime bağlanacaktır? Kopya kümesi altı kapsülü garanti eder ama hangi altı olduğunu garanti etmez. Sonraki ders bu boşluğu dolduran nesneyi ölçer: kopya kümesi altında değişirken üstte sabit kalan bir ad, o adın çözümlenmesinin isteğe eklediği tur ve hazır olmayan bir kopyaya giden istek sayısı.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.