Ders 16 / 27
Durum Bölme
Tek durumda büyüyen altyapının plan süresi ve etki yarıçapı, aynı ölçümlerin bölünmüş hâli, bölmenin bedeli olan parçalar arası bağımlılıkların sayısı, çıktı okuma ile veri kaynağının farkı ve bir parça değiştiğinde yeniden uygulanan parça sayısı.
İçindekiler
Kilit doğruluğu kurtardı ama sırayı da kurdu: aynı durum dosyasına yazan herkes bekliyor. Kilidin kapsadığı alanı küçültmenin tek yolu durumu bölmektir. Bu ders bölmeden önceki ve sonraki sayıları yan yana koyar, sonra bölmenin faturasını çıkarır.
Tek Durumda Büyüyen Altyapı
Kurgu ölçüm ağı bir yıl içinde büyüdü. DM13. Model on üç parçaya ayrılabilecek bir yapı kuruyor: üç ortamın her birinde ağ, veri, hesaplama ve gözlem katmanları, bir de üç ortamın ortaklaşa kullandığı bölge omurgası.
// ag-uret.mjs — buyumus bolgesel olcum agi: on uc parca, kalemler ve bagimlilik kenarlari. // Cikti iki bicimde yazilir: tek durum dosyasi ve parca basina birer durum dosyasi. import { writeFileSync, mkdirSync, rmSync } from "node:fs"; const ortamlar = ["deneme", "hazirlik", "uretim"]; const katmanlar = ["ag", "veri", "hesaplama", "gozlem"]; const parcalar = [{ ad: "ortak", katman: "ortak", adet: 20 }]; for (const o of ortamlar) for (const k of katmanlar) parcalar.push({ ad: `${o}_${k}`, ortam: o, katman: k, adet: 50 }); // Katmanin baska katmanlardan kac kalem uzerinden bilgi aldigi. const disBagimlilik = { ag: [["ortak", 2]], veri: [["ag", 3]], hesaplama: [["ag", 6], ["veri", 4]], gozlem: [["hesaplama", 5], ["veri", 2]], }; const hedefParca = (p, katman) => (katman === "ortak" ? "ortak" : `${p.ortam}_${katman}`); const kalemler = [], icKenar = [], disKenar = []; for (const p of parcalar) { for (let i = 0; i < p.adet; i++) { kalemler.push({ ad: `${p.ad}/k${i}`, parca: p.ad }); if (i > 0) icKenar.push([`${p.ad}/k${i}`, `${p.ad}/k${i - 1}`]); } for (const [katman, adet] of disBagimlilik[p.katman] ?? []) for (let i = 0; i < adet; i++) disKenar.push([`${p.ad}/k${i}`, `${hedefParca(p, katman)}/k${i}`]); } rmSync("bolunmus", { recursive: true, force: true }); mkdirSync("bolunmus"); const kalem = (k) => ({ ...k, bagimli: [...icKenar, ...disKenar].filter((e) => e[0] === k.ad).map((e) => e[1]) }); writeFileSync("tek-durum.json", JSON.stringify({ seri: 1, kalemler: kalemler.map(kalem) })); for (const p of parcalar) writeFileSync(`bolunmus/${p.ad}.json`, JSON.stringify( { seri: 1, kalemler: kalemler.filter((k) => k.parca === p.ad).map(kalem) })); // Saglayicidaki gercek: kayittaki kalemler ve uretim hesaplama katmanindaki uc kayit disi kaynak. const saglayici = [...kalemler.map((k) => k.ad), "uretim_hesaplama/elle-1", "uretim_hesaplama/elle-2", "uretim_hesaplama/elle-3"]; writeFileSync("saglayici.json", JSON.stringify(saglayici)); console.log(`parca: ${parcalar.length}, kalem: ${kalemler.length}, ` + `parca ici kenar: ${icKenar.length}, parcalar arasi kenar: ${disKenar.length}`); console.log(`tek durum dosyasi: 1 x ${kalemler.length} kalem | bolunmus: ` + `${parcalar.length} dosya, en buyugu ${Math.max(...parcalar.map((p) => p.adet))} kalem`); console.log(`saglayicida ${saglayici.length} kaynak, kayitta ${kalemler.length} ` + `-> kayit disi ${saglayici.length - kalemler.length}`);
parca: 13, kalem: 620, parca ici kenar: 607, parcalar arasi kenar: 66 tek durum dosyasi: 1 x 620 kalem | bolunmus: 13 dosya, en buyugu 50 kalem saglayicida 623 kaynak, kayitta 620 -> kayit disi 3
Altı yüz yirmi kalem, altı yüz yetmiş üç kenar. Kenarların yalnız altmış altısı parça sınırını geçiyor — geri kalanı parçaların içinde kalıyor. Bu oran bölmenin neden mümkün olduğunu söylüyor: bağların yüzde doksanı zaten yereldir.
Son satır bu dersin boyunca sabit kalacak bir farkı kuruyor: sağlayıcıda altı yüz yirmi üç kaynak var, kayıtta altı yüz yirmi. Üç kaynak hiçbir parçanın durumunda değil. Bölme bu sayıyı değiştirmez — kaydın kapsamadığı altyapı, kayıt kaç dosyaya bölünürse bölünsün kapsam dışında kalır. Aşağıda göreceği tek etki, hangi bağlanma biçiminin o üç kalemi yanlışlıkla içeri aldığıdır.
Plan Süresi ve Etki Yarıçapı
Plan, kayıttaki her kalemi sağlayıcıdan tazeleyip tanımla karşılaştırır. DM14. Kalem başına bir tazeleme okuması bir milisaniye sürüyor; gerçek süre sağlayıcıya ve ağ gecikmesine bağlıdır, ama kalem sayısıyla doğrusal olduğu değişmez.
Ölçüm kuralı önceki dersteki ile aynı: ölçülen süre basılmaz, beklenenin 0,7 ile 2,0 katı arasında olup olmadığı basılır.
// plan.mjs — verilen kapsamin durumunu okur, her kalemi tazeler ve plan suresini olcer. // Basilan degerler kosumdan bagimsizdir: tazeleme sayisi ile ondan turetilen beklenen sure, // bir de olculen surenin bant denetimi. Ham milisaniye basilmaz. // kullanim: node plan.mjs tek | node plan.mjs parca <ad> import { readFileSync } from "node:fs"; const OKUMA_MS = 1, ALT = 0.7, UST = 2.0; // tazeleme okumasinin modellenen maliyeti ve bant const bekle = (ms) => { const b = Date.now() + ms; while (Date.now() < b); }; const [kapsam, ad] = [process.argv[2] ?? "tek", process.argv[3]]; const dosya = kapsam === "tek" ? "tek-durum.json" : `bolunmus/${ad}.json`; const t0 = Date.now(); const d = JSON.parse(readFileSync(dosya, "utf8")); for (const k of d.kalemler) bekle(OKUMA_MS); // kalem basina bir tazeleme okumasi const olculen = Date.now() - t0; const beklenen = d.kalemler.length * OKUMA_MS; console.log(`${(kapsam === "tek" ? "tek durum" : ad).padEnd(13)} | ` + `${String(d.kalemler.length).padStart(3)} tazeleme, beklenen plan ${String(beklenen).padStart(3)} ms | ` + `olculen bantta: ${olculen >= ALT * beklenen && olculen <= UST * beklenen ? "evet" : "hayir"} | ` + `kilitlenen kalem ${String(d.kalemler.length).padStart(3)}`);
İki kapsamda da yapılan iş aynıdır: gözlem katmanındaki tek bir kalemi değiştirmek.
node plan.mjs tek node plan.mjs parca uretim_gozlem
tek durum | 620 tazeleme, beklenen plan 620 ms | olculen bantta: evet | kilitlenen kalem 620 uretim_gozlem | 50 tazeleme, beklenen plan 50 ms | olculen bantta: evet | kilitlenen kalem 50
Ölçünün taşıyıcısı tazeleme sayısıdır; o sayı çalıştırmaya değil, kapsamın kalem sayısına bağlıdır. Tek durumda bir kalemi değiştirmek altı yüz yirmi tazeleme gerektiriyor, bölünmüş hâlde elli. Değişen kalem sayısı bire karşılık boşa giden okuma oranı yüzde doksan dokuzun üzerinde. Beklenen plan süresi bu sayıdan türüyor — altı yüz yirmi milisaniyeye karşı elli — ve ölçüm iki kapsamda da bandın içinde kaldı; süre doğrudan tazeleme sayısını izliyor.
Etki yarıçapı daha keskindir ve hiç zamana bağlı değildir: kilit altı yüz yirmi kalemin tamamını kapsıyor. Gözlem katmanına eşik ekleyen kişi, plan boyunca üç ortamın tamamını — ağını, veri deposunu, arşivini — kilitli tutuyor.
Bölünmüş hâlde aynı iş elli kalem tazeliyor ve elli kalem kilitliyor. Plan süresi on ikide bire, etki yarıçapı yüzde yüzden yüzde sekize iniyor. Öteki on iki parçaya dokunan herkes aynı anda çalışabilir; önceki dersteki bire inmiş eşzamanlılık tavanı on üçe çıkıyor.
Fark günlük çalışmada birikir. Ekip günde yirmi kez uygulama tetikliyorsa tek durumda toplam plan süresi on iki saniyeye çıkar ve bu sürenin tamamı kilit altında geçer; bölünmüş hâlde aynı yirmi uygulamanın çoğu farklı parçalara düştüğü için hem her biri kısalır hem de birbirini beklemez. Ölçünün asıl anlamı süre değil, bekleyen kişi sayısıdır: tek durumda her uygulama bütün ekibi bekletir, bölünmüş hâlde yalnız aynı parçaya dokunanları.
Bir başka etki plan çıktısının okunabilirliğidir. Altı yüz yirmi kalemin tazelendiği bir planda değişen tek kalemi bulmak, elli kalemlik bir planda bulmaktan zordur; sapma da aynı yığının içinde kaybolur. Kapsam küçüldükçe planın okunma olasılığı artar, okunan plan ise uygulanmadan önce durdurulabilen tek şeydir.
Bölmenin Bedeli
Bölme bağımlılıkları yok etmez, görünür kılar. Parça sınırını geçen altmış altı kenarın her biri artık iki ayrı durum dosyası arasında kurulmak zorundadır.
// bagimlilik.mjs — parcalar arasi kenarlari, cozum bicimlerini ve yeniden uygulama yayilimini sayar. import { readFileSync, readdirSync } from "node:fs"; const parcalar = readdirSync("bolunmus").map((f) => f.replace(".json", "")); const durum = {}, kalemParca = new Map(); for (const p of parcalar) { durum[p] = JSON.parse(readFileSync(`bolunmus/${p}.json`, "utf8")).kalemler; for (const k of durum[p]) kalemParca.set(k.ad, p); } // Cikti veren parcalar: degerlerini adiyla yayinlayanlar. Otekiler ancak sorgulanabilir. const ciktiVeren = new Set(parcalar.filter((p) => /(^ortak$|_ag$|_veri$)/.test(p))); const kenar = []; for (const p of parcalar) for (const k of durum[p]) for (const b of k.bagimli) { const h = kalemParca.get(b); if (h !== p) kenar.push([p, h]); } const ciktiIle = kenar.filter(([, h]) => ciktiVeren.has(h)).length; console.log(`parcalar arasi bagimlilik: ${kenar.length} kenar, ` + `${new Set(kenar.map((e) => e.join(">"))).size} ayri parca cifti`); console.log(` cikti okumayla cozulen: ${ciktiIle}, ` + `veri kaynagiyla cozulen: ${kenar.length - ciktiIle}`); const gelen = {}; for (const [, h] of kenar) gelen[h] = (gelen[h] ?? 0) + 1; console.log(` en cok baglanilan: ` + Object.entries(gelen).sort((a, b) => b[1] - a[1]) .slice(0, 3).map(([p, n]) => `${p} ${n} kenar`).join(", ")); // Veri kaynagi, sorguya uyan her seyi dondurur: kaydin kapsamadigi kaynagi da. const saglayici = JSON.parse(readFileSync("saglayici.json", "utf8")); const donen = saglayici.filter((a) => a.startsWith("uretim_hesaplama/")).length; const kayitli = durum["uretim_hesaplama"].length; console.log(` veri kaynagi sorgusu (uretim_hesaplama): ${donen} kalem dondu, ` + `kayitta olan ${kayitli}, kayit disi ${donen - kayitli}`); console.log(` ayni bag cikti okumayla kurulsaydi: ${kayitli} kalem, kayit disi 0`); const bagliOlan = {}; // hedef parca -> ona bagli parcalar for (const [k, h] of kenar) (bagliOlan[h] ??= new Set()).add(k); const yayilim = (p) => { const gorulen = new Set([p]), yigin = [p]; while (yigin.length) { const x = yigin.pop(); for (const y of bagliOlan[x] ?? []) if (!gorulen.has(y)) { gorulen.add(y); yigin.push(y); } } return gorulen; }; for (const p of ["uretim_gozlem", "uretim_veri", "uretim_ag", "ortak"]) { const g = yayilim(p); const kalem = [...g].reduce((t, x) => t + durum[x].length, 0); console.log(` ${p.padEnd(13)} degisirse: ${String(g.size).padStart(2)} parca yeniden uygulanir, ` + `${String(kalem).padStart(3)} kalem tazelenir, ${String(g.size).padStart(2)} kilit alinir`); }
DM15. Ağ ve veri katmanları ile bölge omurgası değerlerini adıyla çıktı olarak yayınlar; hesaplama katmanı yayınlamaz, ona bağlanmak için veri kaynağı sorgusu gerekir. DM16. Üretim hesaplama katmanında kaydın kapsamadığı üç kaynak duruyor.
parcalar arasi bagimlilik: 66 kenar, 18 ayri parca cifti cikti okumayla cozulen: 51, veri kaynagiyla cozulen: 15 en cok baglanilan: deneme_ag 9 kenar, hazirlik_ag 9 kenar, uretim_ag 9 kenar veri kaynagi sorgusu (uretim_hesaplama): 53 kalem dondu, kayitta olan 50, kayit disi 3 ayni bag cikti okumayla kurulsaydi: 50 kalem, kayit disi 0 uretim_gozlem degisirse: 1 parca yeniden uygulanir, 50 kalem tazelenir, 1 kilit alinir uretim_veri degisirse: 3 parca yeniden uygulanir, 150 kalem tazelenir, 3 kilit alinir uretim_ag degisirse: 4 parca yeniden uygulanir, 200 kalem tazelenir, 4 kilit alinir ortak degisirse: 13 parca yeniden uygulanir, 620 kalem tazelenir, 13 kilit alinir
Altmış altı kenar on sekiz parça çiftine dağılıyor. Elli biri çıktı okumayla çözülüyor: hedef parça değeri adıyla yayınlıyor, bağlanan parça o adı okuyor. Bağ kayıtlıdır ve iki tarafı da bilinir; bedeli bir sürüm bağıdır — hedef parça bir çıktıyı kaldırırsa ona bağlı kenarlar aynı anda kırılır ve kırıldığı ancak uygulama anında öğrenilir.
Kenarların dağılımı da bir şey söylüyor: en çok bağlanılan üç parça, üç ortamın ağ katmanları ve her biri dokuz kenar taşıyor. Bu üç parça bölmenin kırılma noktasıdır. Ağ katmanının çıktı listesinde yapılan bir adlandırma değişikliği tek bir parçada yazılır, dokuz kenarda hissedilir ve üç parçayı yeniden uygulatır. Çıktı adları bu yüzden parçaların arayüzüdür; parça içindeki kaynak adları serbestçe değişebilir, çıktı adları değişemez.
Kalan on beşi veri kaynağıyla çözülüyor: hedef parça bağlanandan haberdar değil, bağlanan taraf sağlayıcıya sorgu atıyor. Ölçüm bu yolun bedelini gösteriyor: sorgu elli üç kalem döndürdü, kayıtta elli kalem var. Üç kalem kaydın kapsamadığı kaynaktır ve veri kaynağı onları da içeri alır. Aynı bağ çıktı okumayla kurulsaydı dönen kalem sayısı elli, kayıt dışı sıfır olurdu. Sorgu gerçeği okur, çıktı kaydı okur; ikisi bu üç kalem kadar ayrışır.
Yeniden Uygulama Yayılımı
Son dört satır bölmenin asıl faturasıdır. DM17. Bir parçanın çıktısı değişince ona bağlı parçalar yeniden uygulanır ve bu geçişlidir; bağlananın bağlananı da sıraya girer. Gözlem parçasını değiştirmek bir parçayı yeniden uygulatıyor; ağ parçasını değiştirmek dördünü; bölge omurgasını değiştirmek on üçünün tamamını.
Son satır dikkatle okunmalı: ortak parça değişince altı yüz yirmi kalem tazeleniyor — tek durum dosyasındaki sayının aynısı — ama bu kez on üç ayrı kilit alınıyor ve on üç uygulama sıraya konuyor. Bölme, geniş değişikliği ucuzlatmaz; pahalılaştırır. Tek durumda tek bir uygulama her şeyi doğru sırada yaparken, bölünmüş hâlde sırayı kuran şey artık bir kişidir.
Kural buradan çıkar: bölme yeri, değişim sıklığına göre seçilir. Sık değişen katman kendi parçasında durmalı ki etki yarıçapı küçük kalsın; seyrek değişen ve herkesin bağlandığı katman tek bir parçada toplanmalı ki yayılımı sık tetiklenmesin. Yanlış yerden bölünürse iki zarar birleşir: hem parçalar arası kenar sayısı büyür, hem her değişiklik çok parçaya yayılır.
Kaydın Çoğalması
Bölme kaydı da çoğaltır ve bu kaydın kendi riskidir. Tek dosya on üç dosya oldu: on üç kilit, on üç erişim noktası, on üç ayrı kayıp senaryosu. Önceki dersin ölçümü her biri için ayrı ayrı geçerlidir. Bir parçanın durumunun kaybı yalnız o parçayı yönetilemez kılar — bu bir kazançtır — ama kaybın fark edilmesi zorlaşır, çünkü öteki on iki parça sorunsuz uygulanmaya devam eder.
İkinci risk sınırın kendisindedir. Parça sınırı hiçbir durum dosyasında yazılı değildir; hangi kalemin hangi parçaya ait olduğu yalnız dosyaların dağılımında görünür. Bir kalem yanlışlıkla iki parçanın tanımına birden girerse iki kayıt aynı kaynağı sahiplenir ve iki uygulama sırayla birbirinin işini bozar. Kilit bunu engellemez: iki farklı dosyanın kilidi farklıdır.
Üçüncü risk sınırı değiştirmektedir. Bölme bir kez yapılıp bitmez; bir kalem zamanla yanlış parçada kaldığı anlaşılır ve taşınması gerekir. Taşıma, tanımı bir dosyadan ötekine kopyalamak değildir. Kalem eski parçanın tanımından çıkarıldığı anda o parçanın planı onu silinecek olarak gösterir; yeni parçanın planı ise oluşturulacak olarak. İki plan da tek başına doğrudur, ikisi arka arkaya uygulanırsa kaynak gerçekten silinir ve yeniden yaratılır. Doğru yol, kalemin kaydını eski durumdan kimliğiyle çıkarıp yeni duruma kimliğiyle eklemektir — yani ilk dersteki içe aktarmanın tersi ve devamı. Bu işlem iki dosyaya birden dokunduğu için iki kilidi aynı anda gerektirir ve arada kalırsa kalem hiçbir kayıtta görünmez: kaydın kapsamadığı kaynak sayısı üçten dörde çıkar.
Özet
- Altı yüz yirmi kalemlik ağın altı yüz yetmiş üç kenarından yalnız altmış altısı parça sınırını geçiyor; bağların yüzde doksanı zaten yerel.
- Tek durumda bir kalemlik değişiklik altı yüz yirmi kalem tazeledi ve altı yüz yirmi kalemi kilitledi; bölünmüş hâlde aynı iş elli kalem tazeledi, elli kalem kilitledi.
- Parçalar arası altmış altı kenarın elli biri çıktı okumayla, on beşi veri kaynağıyla çözülüyor; veri kaynağı sorgusu elli üç kalem döndürdü, üçü kaydın dışındaydı.
- Bir parça değişince yeniden uygulanan parça sayısı bir ile on üç arasında değişiyor; bölge omurgası değişince on üç kilit ve altı yüz yirmi tazeleme gerekiyor.
- Bölme kilidi küçültür, koordinasyonu büyütür; bölme yeri değişim sıklığına göre seçilir.
- On üç dosya on üç kayıp senaryosu demektir ve parça sınırı hiçbir durum dosyasında yazılı değildir.
Sonraki Adım
Bu dersin bölmesi katmana göre yapıldı: ağ, veri, hesaplama, gözlem. Parça adlarında ortam da vardı ama ortam ayrımının nasıl kurulduğuna hiç bakılmadı. Üç ortamın tanımı aynıysa aynı metni üç kez yazmak mı gerekir, yoksa tek tanımın üç örneği mi tutulur? İki kurulumun da bir bedeli vardır ve bedel aynı yerde toplanır: uygulamayı başlatan kişinin hangi ortamda olduğunu bilmesi. Sonraki ders üç ortamı iki ayrı biçimde kurar, yanlış ortama uygulama riskini adım sayısıyla ölçer, ortama özgü farkları sayar ve tanım değiştiğinde kaç ortamın zorunlu olarak birlikte değiştiğini karşılaştırır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.