Ders 07 / 27
Sağlayıcılar ve Kaynaklar
Sağlayıcı, yarat-oku-güncelle-sil arayüzü olarak modellenir ve beş kaynak tipi bu arayüzü uygular; kaydın yazdığı alanla hedefte gerçekten duran alan sayılır. Bir değişiklik kümesinde planın kaç kalemi öngördüğü, hangi alanların kaynağı yeniden oluşturmaya zorladığı ve sağlayıcı sürümü değiştiğinde kayıt hiç değişmeden kaç alanın kaydığı ölçülür.
İçindekiler
Bir önceki ders kaydı bir dile bağladı, ama kaydın karşısındaki hedef sistem beş satırlık bir işlevdi: değeri alan, yazan ya da reddeden. Gerçekte bir kaynak yaratılır, okunur, güncellenir ve silinir; bu dört işlemi bilen, kayıttaki alan adlarını hedef sistemin karşılıklarına çeviren ve hangi alanın yerinde değiştirilebileceğini bilen katmanın adı sağlayıcıdır. Bu ders sağlayıcıyı bir arayüz olarak modeller ve üç şeyi sayar: kaydın yazdığı alanla hedefte gerçekten duran alan arasındaki fark, bir değişikliğin planının uygulamayı ne oranda öngördüğü, ve sağlayıcı sürümü değiştiğinde kayıt hiç değişmeden ne kadarının kaydığı.
Ölçüm bölgesel ölçüm ağının batı ortamı üzerindedir ve kurgudur; sağlayıcı, kaynak tipi
kataloğu ve hedef depo node ile kurulmuş bir modeldir. Hiçbir gerçek ürün ya da hedef sistem
çağrılmaz.
BS7. Sağlayıcı beş kaynak tipi tanır: makine, kuyruk, veri deposu, ağ, nesne deposu. BS8. Her alanın üç özelliği vardır: türü, yerinde güncellenip güncellenemediği, varsayılanı. Varsayılanı olmayan alan zorunludur. BS9. Bir alan yerinde güncellenemiyorsa, o alan değiştiğinde kaynak silinip yeniden yaratılır ve bunun tip başına sabit bir kesinti dakikası vardır. BS10. Plan aşaması yalnız katalogdaki yerinde güncellenebilirlik bayrağını bilir. BS11. Hedef sistem, sayısal bir alan iki katından fazla büyütülürse yerinde büyütmeyi reddeder; bu kural katalogda yazılı değildir. BS12. Kayıtta yazılmayan alanları sağlayıcı kendi varsayılanıyla doldurur ve doldurduğu değer kayıtta hiçbir yerde görünmez.
Sağlayıcı Arayüzü ve Kaynak Tipleri
// saglayici.mjs — kurgu saglayici: kaynak tipi katalogu, yarat/oku/guncelle/sil arayuzu (model). const A = (tur, yerinde, varsayilan) => ({ tur, yerinde, varsayilan }); export const KATALOG = { // saglayici surum 1 makine: { bolge: A("dize", false), sayi: A("sayi", true), cekirdek: A("sayi", false), bellek_gb: A("sayi", true), disk_gb: A("sayi", true, 100), imaj: A("dize", false, "temel-2"), izleme: A("dize", true, "acik") }, kuyruk: { bolge: A("dize", false), bolme: A("sayi", false), saklama_gn: A("sayi", true, 7), bicim: A("dize", false, "ikili"), sifreleme: A("dize", true, "kapali") }, veri_deposu: { bolge: A("dize", false), boyut_gb: A("sayi", true), surum: A("dize", false, "3"), yedek_gn: A("sayi", true, 1), sifreleme: A("dize", false, "kapali") }, ag: { bolge: A("dize", false), alt_ag: A("sayi", false), gecis: A("sayi", true, 1) }, nesne_deposu: { bolge: A("dize", false), sinif: A("dize", true, "sicak"), surum_tut: A("dize", false, "kapali"), saklama_gn: A("sayi", true, 365) }, }; export const KESINTI = { makine: 4, kuyruk: 9, veri_deposu: 25, ag: 12, nesne_deposu: 30 }; export const KAYIT = { // batı ortaminin kurgu kaydi "makine.toplayici": { bolge: "bati", sayi: 6, cekirdek: 4, bellek_gb: 8 }, "makine.dogrulayici": { bolge: "bati", sayi: 2, cekirdek: 8, bellek_gb: 16, disk_gb: 200, imaj: "temel-2" }, "kuyruk.sayac": { bolge: "bati", bolme: 12 }, "kuyruk.uyari": { bolge: "bati", bolme: 3, bicim: "metin" }, "veri_deposu.olcum": { bolge: "bati", boyut_gb: 300 }, "ag.omurga": { bolge: "bati", alt_ag: 3 }, "nesne_deposu.arsiv": { bolge: "bati" }, }; export const varsayilanlar = (t) => Object.fromEntries(Object.entries(t) .filter(([, a]) => a.varsayilan !== undefined).map(([f, a]) => [f, a.varsayilan])); export function saglayiciYap(katalog) { // arayuz: yarat / oku / guncelle / sil const depo = new Map(), cagri = { oku: 0, yarat: 0, guncelle: 0, sil: 0 }; return { katalog, cagri, depo, oku: (t, a) => (cagri.oku++, depo.get(`${t}.${a}`) ?? null), yarat: (t, a, alan) => (cagri.yarat++, depo.set(`${t}.${a}`, { ...varsayilanlar(katalog[t]), ...alan })), guncelle: (t, a, alan) => (cagri.guncelle++, Object.assign(depo.get(`${t}.${a}`), alan)), sil: (t, a) => (cagri.sil++, depo.delete(`${t}.${a}`)) }; } const ASIRI = (a, eski, yeni) => a.tur === "sayi" && yeni > eski * 2; // yerinde buyutulemeyen artis export function karar(s, anahtar, istenen, plan) { // plan asamasi ASIRI kuralini bilmez const [t, a] = anahtar.split("."); const gercek = s.oku(t, a); if (!gercek) return ["yaratilacak", []]; const fark = Object.keys(istenen).filter((f) => gercek[f] !== istenen[f]); const zorlayan = fark.filter((f) => !s.katalog[t][f].yerinde || (!plan && ASIRI(s.katalog[t][f], gercek[f], istenen[f]))); return zorlayan.length ? ["yeniden olusturulacak", zorlayan] : fark.length ? ["guncellenecek", fark] : ["degismeyecek", []]; } export const GECMIS = { yaratilacak: "yaratildi", "yeniden olusturulacak": "yeniden olusturuldu", guncellenecek: "guncellendi", degismeyecek: "degismedi", silinecek: "silindi" }; export function uygula(s, kayit) { const sonuc = []; for (const [k, istenen] of Object.entries(kayit)) { const [t, a] = k.split("."), [ne, alan] = karar(s, k, istenen, false); if (ne === "yaratilacak") s.yarat(t, a, istenen); else if (ne === "yeniden olusturulacak") { s.sil(t, a); s.yarat(t, a, istenen); } else if (ne === "guncellenecek") s.guncelle(t, a, Object.fromEntries(alan.map((f) => [f, istenen[f]]))); sonuc.push([k, GECMIS[ne], alan]); } for (const k of [...s.depo.keys()]) if (!(k in kayit)) { const [t, a] = k.split("."); s.sil(t, a); sonuc.push([k, "silindi", []]); } return sonuc; }
Sağlayıcı dört işlemden ibarettir ve hepsi sayılır; kaydın hedef sisteme dokunuşu bu dört çağrının toplamıdır. Kaynak tipi ise bir alan tablosudur: her alanın türü, yerinde güncellenip güncellenemediği ve varsayılanı yazılıdır. Bu üç özellikten ikincisi kaydın bedelini, üçüncüsü kaydın sessizliğini belirler. Karar işlevi ikisini de kullanır: değişen alanlardan biri bile yerinde güncellenemiyorsa kaynak silinip yeniden yaratılır, aksi hâlde tek bir güncelleme çağrısı yeter.
Plan ile Uygulamanın Farkı
// degisim.mjs — katalog sayilir, kayit uygulanir, sonra bir degisiklik kumesi planlanip uygulanir. import { KATALOG, KESINTI, KAYIT, saglayiciYap, karar, uygula, GECMIS } from "./saglayici.mjs"; const alanlar = Object.values(KATALOG).flatMap(Object.values); const yaz = (...s) => console.log(s.map((x, i) => String(x).padEnd(i ? 22 : 20)).join("").trimEnd()); console.log(`saglayici v1: ${Object.keys(KATALOG).length} kaynak tipi, ${alanlar.length} alan ` + `(tip basina ${(alanlar.length / Object.keys(KATALOG).length).toFixed(1)}); ` + `${alanlar.filter((a) => a.varsayilan === undefined).length} zorunlu, ` + `${alanlar.filter((a) => !a.yerinde).length} alan yerinde guncellenemez`); const dar = (...s) => console.log(s.map((x) => String(x).padEnd(14)).join("").trimEnd()); dar("tip", "alan", "zorunlu", "yerinde disi", "kesinti"); for (const [t, alan] of Object.entries(KATALOG)) { const v = Object.values(alan); dar(t, v.length, v.filter((a) => a.varsayilan === undefined).length, v.filter((a) => !a.yerinde).length, `${KESINTI[t]} dk`); } console.log(); const s = saglayiciYap(KATALOG); uygula(s, KAYIT); const yazili = Object.values(KAYIT).reduce((t, r) => t + Object.keys(r).length, 0); const gercekAlan = [...s.depo.values()].reduce((t, r) => t + Object.keys(r).length, 0); console.log(`kayit ${Object.keys(KAYIT).length} kaynak icin ${yazili} alan yaziyor; ` + `hedefte ${gercekAlan} alan var, ${gercekAlan - yazili} tanesini saglayici varsayilani doldurdu`); console.log(`ilk uygulama cagrilari: ${JSON.stringify(s.cagri)}\n`); const KAYIT2 = structuredClone(KAYIT); // degisiklik kumesi KAYIT2["makine.toplayici"].sayi = 10; KAYIT2["makine.toplayici"].cekirdek = 8; KAYIT2["makine.dogrulayici"].bellek_gb = 48; KAYIT2["kuyruk.sayac"].bolme = 16; KAYIT2["veri_deposu.olcum"].boyut_gb = 600; delete KAYIT2["kuyruk.uyari"]; KAYIT2["makine.arsivci"] = { bolge: "bati", sayi: 1, cekirdek: 2, bellek_gb: 4 }; Object.assign(s.cagri, { oku: 0, yarat: 0, guncelle: 0, sil: 0 }); const plan = Object.entries(KAYIT2).map(([k, r]) => [k, ...karar(s, k, r, true)]) .concat([...s.depo.keys()].filter((k) => !(k in KAYIT2)).map((k) => [k, "silinecek", []])); const planOkuma = s.cagri.oku; const sonuc = uygula(s, KAYIT2); yaz("kaynak", "plan", "uygulama", "zorlayan alan"); let sapma = 0; for (const [k, ne, alan] of plan) { const g = sonuc.find((x) => x[0] === k); if (GECMIS[ne] !== g[1]) sapma++; yaz(k, ne, g[1], (g[2].length ? g[2] : alan).join(", ")); } const yeniden = (l, e) => l.filter((x) => x[1].startsWith("yeniden")).map((x) => KESINTI[x[0].split(".")[0]]); const p = yeniden(plan), u = yeniden(sonuc); console.log(`\nplan ${p.length} yeniden olusturma ongordu, uygulama ${u.length} yapti; ` + `${plan.length - sapma}/${plan.length} kalem tuttu`); console.log(`kesinti: planda ${p.reduce((a, b) => a + b, 0)} dk, uygulamada ${u.reduce((a, b) => a + b, 0)} dk`); console.log(`plan ${planOkuma} okuma yapti; degisiklik cagrilari ${JSON.stringify(s.cagri)}`);
saglayici v1: 5 kaynak tipi, 24 alan (tip basina 4.8); 11 zorunlu, 13 alan yerinde guncellenemez
tip alan zorunlu yerinde disi kesinti
makine 7 4 3 4 dk
kuyruk 5 2 3 9 dk
veri_deposu 5 2 3 25 dk
ag 3 2 2 12 dk
nesne_deposu 4 1 2 30 dk
kayit 7 kaynak icin 20 alan yaziyor; hedefte 36 alan var, 16 tanesini saglayici varsayilani doldurdu
ilk uygulama cagrilari: {"oku":7,"yarat":7,"guncelle":0,"sil":0}
kaynak plan uygulama zorlayan alan
makine.toplayici yeniden olusturulacak yeniden olusturuldu cekirdek
makine.dogrulayici guncellenecek yeniden olusturuldu bellek_gb
kuyruk.sayac yeniden olusturulacak yeniden olusturuldu bolme
veri_deposu.olcum guncellenecek guncellendi boyut_gb
ag.omurga degismeyecek degismedi
nesne_deposu.arsiv degismeyecek degismedi
makine.arsivci yaratilacak yaratildi
kuyruk.uyari silinecek silindi
plan 2 yeniden olusturma ongordu, uygulama 3 yapti; 7/8 kalem tuttu
kesinti: planda 13 dk, uygulamada 17 dk
plan 7 okuma yapti; degisiklik cagrilari {"oku":14,"yarat":4,"guncelle":1,"sil":4}
İlk tablo kaydın kapsamını verir. Beş tip toplam yirmi dört alan tanır; on biri zorunludur, on üçü yerinde güncellenemez. Dağılım tipe göre değişir ve bedeli de öyle: makinenin yedi alanının üçü, ağın üç alanının ikisi yerinde güncellenemez, ama makinenin yeniden oluşturulması dört dakika, nesne deposununki otuz dakika sürer. Yerinde güncellenemeyen alan oranı ile kesinti dakikası ayrı iki ölçüdür ve birlikte okunmaları gerekir: ağın oranı yüksektir ama kaynağı ucuzdur; nesne deposunun oranı düşüktür, kaynağı pahalıdır.
İkinci satır kayıt ile gerçek arasındaki farkı verir. Kayıt yedi kaynak için yirmi alan yazar, ama uygulamadan sonra hedefte otuz altı alan durur. Aradaki on altı alanı sağlayıcı kendi varsayılanıyla doldurmuştur ve bu değerler kayıtta hiçbir yerde yazılı değildir. Kaydı okuyan biri makinelerin hangi imajla kurulduğunu, kuyruğun şifreli olup olmadığını ya da veri deposunun kaç gün yedeklendiğini kayıttan öğrenemez. Kaydın kapsamadığı altyapı burada başlar: aynı kaydın kurduğu kaynakların alanlarının neredeyse yarısı kayıt dışı değerlerle koşar.
Üçüncü ölçü planın öngörme oranıdır. Sekiz kalemlik değişiklik kümesinde plan yedi kalemi doğru söyler, biri tutmaz. Kaçırdığı kalem şudur: doğrulayıcı makinelerin belleği on altıdan kırk sekize çıkarılır ve katalog bu alanı yerinde güncellenebilir işaretlemiştir, dolayısıyla plan “güncellenecek” der. Hedef sistem ise iki katından fazla büyümeyi yerinde kabul etmez, kaynağı siler ve yeniden yaratır. Sonuç: plan iki yeniden oluşturma öngörür, uygulama üç yapar; öngörülen kesinti on üç dakika, gerçekleşen on yedi dakika. Planın yanıldığı yer, kataloğun eksik olduğu yerdir — ve katalog kaydın değil sağlayıcının malıdır.
Son satır çağrı yüküdür. Sekiz kalem için sağlayıcı on dört okuma, dört yaratma, bir güncelleme ve dört silme alır. Okumaların yarısı planın kendisine aittir: plan üretmek, hedef sistemi baştan sona bir kez okumak demektir; kaynak sayısı arttıkça planın maliyeti kaynak sayısıyla birlikte artar. Yeniden oluşturulan üç kaynak ise dört silmenin üçünü ve dört yaratmanın üçünü tek başına açıklar. Yerinde güncellenemeyen üç alan, altı çağrıya ve on yedi dakika kesintiye dönüşür.
Sağlayıcı Sürümü Değişince
// surum.mjs — saglayici surumu degisince: kayan varsayilan, korunan alan, degisen karar. import { KATALOG, KESINTI, KAYIT, saglayiciYap, karar, uygula } from "./saglayici.mjs"; const KATALOG2 = structuredClone(KATALOG); // saglayici surum 2 KATALOG2.makine.imaj.varsayilan = "temel-3"; KATALOG2.kuyruk.sifreleme.varsayilan = "acik"; KATALOG2.veri_deposu.yedek_gn.varsayilan = 7; KATALOG2.nesne_deposu.sinif.varsayilan = "soguk"; KATALOG2.kuyruk.saklama_gn.yerinde = false; // yerinde guncellenebilirlik de degisti KATALOG2.nesne_deposu.erisim = { tur: "dize", yerinde: true, varsayilan: "ozel" }; const kur = (k) => { const s = saglayiciYap(k); uygula(s, KAYIT); return s; }; const a = kur(KATALOG), b = kur(KATALOG2), fark = []; for (const [k, r] of a.depo) for (const f of new Set([...Object.keys(r), ...Object.keys(b.depo.get(k))])) if (r[f] !== b.depo.get(k)[f]) fark.push([k, f, r[f], b.depo.get(k)[f]]); console.log(`ayni kayit v1 ve v2 ile kuruldu: ${fark.length} alan farkli, ` + `${new Set(fark.map((x) => x[0])).size}/${a.depo.size} kaynak etkilendi`); for (const [k, f, x, y] of fark) console.log(` ${k.padEnd(20)}${f.padEnd(11)}${x} -> ${y}`); const korunan = Object.entries(KAYIT).flatMap(([k, r]) => Object.keys(r) .filter((f) => KATALOG[k.split(".")[0]][f].varsayilan !== KATALOG2[k.split(".")[0]][f].varsayilan) .map((f) => `${k}.${f}`)); console.log(`kayitta yazili oldugu icin kaymayan alan: ${korunan.length} (${korunan.join(", ")})\n`); const DEG = structuredClone(KAYIT); // kayittaki tek satirlik degisiklik DEG["kuyruk.sayac"].saklama_gn = 30; for (const [ad, kat] of [["v1", KATALOG], ["v2", KATALOG2]]) { const s = kur(kat), p = Object.entries(DEG).map(([k, r]) => [k, ...karar(s, k, r, true)]); const y = p.filter((x) => x[1].startsWith("yeniden")); console.log(`${ad}: ayni degisiklik -> ${p.filter((x) => x[1] === "guncellenecek").length} guncelleme, ` + `${y.length} yeniden olusturma, ${y.reduce((t, x) => t + KESINTI[x[0].split(".")[0]], 0)} dk kesinti`); }
ayni kayit v1 ve v2 ile kuruldu: 6 alan farkli, 5/7 kaynak etkilendi makine.toplayici imaj temel-2 -> temel-3 kuyruk.sayac sifreleme kapali -> acik kuyruk.uyari sifreleme kapali -> acik veri_deposu.olcum yedek_gn 1 -> 7 nesne_deposu.arsiv sinif sicak -> soguk nesne_deposu.arsiv erisim undefined -> ozel kayitta yazili oldugu icin kaymayan alan: 1 (makine.dogrulayici.imaj) v1: ayni degisiklik -> 1 guncelleme, 0 yeniden olusturma, 0 dk kesinti v2: ayni degisiklik -> 0 guncelleme, 1 yeniden olusturma, 9 dk kesinti
Sürüm değişimi kaydın en sessiz riskidir, çünkü kayıtta değişen hiçbir şey yoktur. Aynı yedi kaynaklı kayıt sağlayıcının iki sürümüyle kurulduğunda hedefteki durum altı alanda ayrılır ve yedi kaynağın beşi etkilenir: makinenin imajı, iki kuyruğun şifrelemesi, veri deposunun yedek sıklığı, nesne deposunun saklama sınıfı, ve ikinci sürümün eklediği yepyeni bir erişim alanı. Sonuncusu ayrıca gösterir ki sürüm yalnız var olan varsayılanları değiştirmez, kayıtta karşılığı hiç bulunmayan yeni alanlar da ekleyebilir.
Kaymayan tek alan, kayıtta açıkça yazılmış olandır: doğrulayıcı makineler imajını kendi yazdığı için sürüm değişiminden etkilenmez. Kural buradan çıkar: kayıt bir alanı yazıyorsa o alan korunur, yazmıyorsa sağlayıcının sürümüne emanettir. On altı alanı varsayılana bırakan bir kayıt, on altı alanı sağlayıcının sürüm notlarına bırakmış demektir. Bu, ilk bölümdeki on altı sayısının ikinci yüzüdür: aynı sayı bir yerde okunabilirlik kazancı, başka yerde sapma yüzeyidir.
Sürüm ayrıca kararı da değiştirir. Kuyruğun saklama süresini yedi günden otuz güne çıkaran tek satırlık değişiklik, birinci sürümde bir güncelleme çağrısı ve sıfır dakika kesintiyle biter; ikinci sürümde aynı satır kaynağı yeniden oluşturur ve dokuz dakika kesinti üretir. Kayıt aynı, değişiklik aynı, sonuç başka. Bir alanın yerinde güncellenip güncellenemeyeceği kaydın değil sağlayıcının bilgisidir, o bilgi sürümle değişir, ve değiştiğini haber veren tek yer plan çıktısıdır — plan okunmadan uygulanan bir değişiklik bu farkı ancak kesinti sırasında öğrenir.
Özet
- Sağlayıcı v1’in 5 kaynak tipi 24 alan tanıdı; 11’i zorunlu, 13’ü yerinde güncellenemez alandı. Yeniden oluşturma bedeli tipe göre 4 ile 30 dakika arasında değişti.
- Kayıt 7 kaynak için 20 alan yazdı, hedefte 36 alan oluştu: 16 alanı sağlayıcı varsayılanı doldurdu ve bu değerler kayıtta hiç görünmedi.
- 8 kalemlik değişiklikte plan 7 kalemi tutturdu; yeniden oluşturma 2 öngörüldü, 3 oldu, kesinti 13 dakika yerine 17 dakika çıktı. Plan yalnız katalogda yazılı olanı bilir.
- Çağrı yükü 14 okuma, 4 yaratma, 1 güncelleme, 4 silme oldu; okumaların 7’si planın kendisine aitti.
- Sağlayıcı sürümü değişince kayıt hiç değişmeden 6 alan kaydı ve 7 kaynağın 5’i etkilendi; kayıtta açıkça yazılı olan 1 alan korundu.
- Aynı tek satırlık değişiklik v1’de 1 güncelleme ve 0 dakika, v2’de 1 yeniden oluşturma ve 9 dakika kesinti üretti.
Sonraki Adım
Buraya kadar hedefteki her kaynağı kayıt kurdu. Gerçek ortamlarda böyle olmaz: ağın bir bölümü, bir kimlik tanımı ya da başka bir ekibin kurduğu bir depo kaydın dışında durur ve kayıt onlara yalnız okuyarak bağlanabilir. Bir sonraki ders bu bağı ölçer: var olan altyapıyı okuyan veri kaynakları kaç okuma yapıyor, ne sıklıkta, okunan bir değer değiştiğinde kaç kaynak yeniden hesaplanıyor, ve kaydın kapsamadığı kalemler sapma tespitinde neden hiç görünmüyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.