Ders 18 / 27
Modül Yazımı
Üç toplama noktasında ayrı ayrı yazılmış aynı kayıt tek bir modüle çıkarılır; girdi yüzeyi, satır kazancı ve altı sürüm boyunca büyüyen esneklik bedeli sayılır. Modülü yazan tarafın hiç görmediği dört kalem de ölçülür.
İçindekiler
Bir önceki ders aynı tanımı birden çok ortamda koşturdu: çalışma alanı ayrımı durumu böldü, ortamlar birbirinin kaynağına dokunmadan aynı metinden üretildi. Tanımın kendisi ise hâlâ tek parça. Bölgesel ölçüm ağının üç toplama noktası aynı üç kaynağı içeriyor ve her biri kendi kayıt bloğunu baştan sona taşıyor; ortak hiçbir şey ortak bir yerde durmuyor.
Modül, birlikte oluşturulup birlikte silinen bir kaynak kümesine ad veren, arayüzü girdi ve
çıktı olan yapılandırma parçasıdır. Sözcük yazılım paketlerindeki modül ile aynıdır; bağlam
farkı şu: orada arayüz çağrılabilir işlevlerdi ve üretilen şey davranıştı, burada arayüz bir
değişken listesi, üretilen şey kaynağın kendisi.
Ölçüm yine bölgesel ölçüm ağı üzerindedir: su sayacı okumalarını toplayan kurgu bir yazılım.
Gerçek altyapı sağlanmaz; ölçülen şey node ile kurulmuş bir modeldir.
DM25. Üç toplama noktası vardır; her biri üç kaynaktan oluşur ve üç kayıt ayrı ayrı elle
yazılmıştır.
DM26. Yapılandırma dili bu ders için tanımlanır: kaynak <tür> <ad> { alan = değer },
girdi <ad> [= varsayılan], cikti <ad> = <değer>, modul <tür> <ad> { alan = değer }. $ad
çağrının adına bağlıdır, değeri yok olan alan üretilmez, eger yazan kaynak koşulludur.
DM27. Modül elle yazılır; hangi alanın arayüze çıkacağına yazan karar verir. Kod modülü üç
kayda karşı sınar ve kayıtta değişen alanlarla arayüzü karşılaştırır.
DM28. Çağrıya yalnız zorunlu girdilerle varsayılandan sapan alanlar yazılır.
DM29. Gerçekte dört kalem elle değiştirilmiştir ve hiçbir kayıtta yoktur; dördü de yazılıdır,
rastgelelik yoktur.
DM30. Modül beş yeni kullanımla altı sürüme çıkar; her sürümün değişikliği önceden yazılıdır ve
açılan sekiz kullanımdan biri sonradan kapatılır.
Üç Yerde Aynı Kayıt
// kayit.mjs — bolgesel olcum aginin uc toplama noktasi; ucu de ayri ayri elle yazilmis kayit. export const KAYIT = `# nokta kuzey kaynak sanal-makine-kumesi kuzey-toplayici { sayi = 4 imaj = toplayici-4.2.0 bolge = kuzey-1 } kaynak disk kuzey-tampon { boyut-gb = 200 baglanan = kuzey-toplayici } kaynak ag-kurali kuzey-giris { kaynak-blok = 10.20.0.0/16 kapi = 8080 yon = giris } # nokta bati kaynak sanal-makine-kumesi bati-toplayici { sayi = 6 imaj = toplayici-4.2.0 bolge = bati-1 } kaynak disk bati-tampon { boyut-gb = 500 baglanan = bati-toplayici } kaynak ag-kurali bati-giris { kaynak-blok = 10.30.0.0/16 kapi = 8080 yon = giris } # nokta dogu kaynak sanal-makine-kumesi dogu-toplayici { sayi = 6 imaj = toplayici-4.2.0 bolge = dogu-1 } kaynak disk dogu-tampon { boyut-gb = 500 baglanan = dogu-toplayici } kaynak ag-kurali dogu-giris { kaynak-blok = 10.40.0.0/16 kapi = 8443 yon = giris }`; export const NOKTA = ["kuzey", "bati", "dogu"]; export const ELLE = [ // gerceginde var, hicbir kayitta yok ["bati", "disk", "boyut-gb", "750"], // disk elle buyutuldu ["kuzey", "sanal-makine-kumesi", "sayi", "6"], // kume elle olceklendi ["dogu", "ag-kurali", "ek-kapi", "9443"], // kayitta olmayan alan elle eklendi ["kuzey", "nesne-deposu", "*", "kuzey-arsiv"]]; // kayit disi kaynak elle acildi
Kayıt kırk iki satırdır ve her noktada sekiz alan yuvası vardır. ELLE listesi kaydın yanında duran
ikinci gerçekliktir: kayıtta yazmayan ama sahada var olan dört kalem. Modülü yazan taraf bu dördün
kaçını görür — ölçülecek şey budur.
Modülün Girdi Yüzeyi
// modul.mjs — elle yazilmis modul uc kayda karsi sinanir; girdi yuzeyi, satir kazanci ve kayit-gercek farki. import { KAYIT, NOKTA, ELLE } from "./kayit.mjs"; export const satir = (t) => t.split("\n").filter((s) => s.trim()).length; export const METIN = Object.fromEntries(KAYIT.split(/^# nokta /m).filter(Boolean) .map((p) => [p.split("\n")[0], p.split("\n").slice(1).join("\n").trim()])); export const K = Object.fromEntries(NOKTA.map((n) => [n, METIN[n].split("\n").reduce((b, s) => { const k = s.match(/^kaynak (\S+) (\S+) \{$/), f = s.match(/^ {2}([\w-]+) = (.+)$/); if (k) b.push({ tur: k[1], ad: k[2], alan: [] }); else if (f) b.at(-1).alan.push([f[1], f[2]]); return b; }, [])])); const D = Object.fromEntries(NOKTA.map((n) => [n, Object.fromEntries(K[n].flatMap((b) => b.alan))])); export const MODUL = `girdi kaynak-blok girdi sayi = 6 girdi boyut-gb = 500 girdi kapi = 8080 kaynak sanal-makine-kumesi $ad-toplayici { sayi = $sayi imaj = toplayici-4.2.0 bolge = $ad-1 } kaynak disk $ad-tampon { boyut-gb = $boyut-gb baglanan = $ad-toplayici } kaynak ag-kurali $ad-giris { kaynak-blok = $kaynak-blok kapi = $kapi yon = giris } cikti tampon-adi = $ad-tampon cikti giris-kapisi = $kapi`; // elle yazildi; cikti secimi kayitta yok export const uret = (mod, ad, arg) => { // $ad cagri adidir; degeri `yok` olan alan yazilmaz const isim = [...mod.matchAll(/^girdi ([\w-]+)/gm)].map((m) => m[1]).sort((a, b) => b.length - a.length); const c = []; let atla = false; for (const s of mod.split("\n")) { if (/^(girdi|cikti) /.test(s)) continue; let t = s.replaceAll("$ad", ad); for (const i of isim) t = t.replaceAll("$" + i, arg[i]); const k = t.match(/ eger (\S+) \{$/); if (k) { atla = k[1] === "hayir"; t = t.replace(/ eger \S+ \{$/, " {"); } else if (t === "}" && atla) { atla = false; continue; } if (!atla && !/ = yok$/.test(t)) c.push(t); } return c.join("\n"); }; export const zorunlu = (m) => [...m.matchAll(/^girdi ([\w-]+)$/gm)].map((x) => x[1]); export const varsayilan = (m) => Object.fromEntries([...m.matchAll(/^girdi ([\w-]+) = (.+)$/gm)].map((x) => [x[1], x[2]])); export const gecen = (m, n) => { // cagriya zorunlu ve varsayilandan sapan alan yazilir const v = varsayilan(m); return Object.fromEntries([...zorunlu(m), ...Object.keys(v)].filter((a) => D[n][a] !== v[a]).map((a) => [a, D[n][a]])); }; export const cs = (m, n) => Object.keys(gecen(m, n)).length + 2; // cagrinin satir sayisi if (import.meta.main) { // surum.mjs iceri aktarinca bu bolum kosmaz const alan = Object.keys(D[NOKTA[0]]), v = varsayilan(MODUL); const degis = alan.filter((a) => new Set(NOKTA.map((n) => D[n][a])).size > 1); const ad = [...zorunlu(MODUL), ...Object.keys(v)], disi = degis.filter((a) => !ad.includes(a)); const ayni = NOKTA.filter((n) => uret(MODUL, n, { ...v, ...gecen(MODUL, n) }) === METIN[n]).length; const el = NOKTA.reduce((t, n) => t + satir(METIN[n]), 0), c = NOKTA.map((n) => cs(MODUL, n)); const t = c.reduce((x, y) => x + y); console.log(`alan yuvasi ${alan.length} | degisen ${degis} | sabit ${alan.length - degis.length}` + ` | girdi yuzeyi ${ad.length} = zorunlu ${zorunlu(MODUL)} + varsayilanli ${Object.keys(v)}`); console.log(`degisip arayuzde olmayan ${disi.length} (${disi}) | modul ${satir(MODUL)} = ${ad.length}+` + `${satir(MODUL) - ad.length - 2}+2 | elle ${el} | modul yolu ${satir(MODUL)}+${t}=${satir(MODUL) + t}` + ` | kazanc ${el - satir(MODUL) - t} | cagri ${c} | asliyla ayni ${ayni}/3`); let kap = 0; for (const [n, tur, a, d] of ELLE) { // kayit ile gercek arasindaki fark const b = K[n].find((x) => x.tur === tur), y = b?.alan.find(([x]) => x === a); console.log(` ${n.padEnd(6)} ` + (!b ? `kayit disi kaynak ${d}` : !y ? `kayitta olmayan alan ${a} = ${d}` : `deger farki ${a}: kayit ${y[1]}, gercek ${d}`).padEnd(44) + (b ? "kapsamda" : "kapsam disi")); if (b) kap++; } console.log(`kayitta yazmayan kalem ${ELLE.length} | modulu yazanin gordugu 0 | geri alinacak ${kap}` + ` | kayitsiz kalan ${ELLE.length - kap}`); }
alan yuvasi 8 | degisen sayi,bolge,boyut-gb,baglanan,kaynak-blok,kapi | sabit 2 | girdi yuzeyi 4 = zorunlu kaynak-blok + varsayilanli sayi,boyut-gb,kapi degisip arayuzde olmayan 2 (bolge,baglanan) | modul 20 = 4+14+2 | elle 42 | modul yolu 20+12=32 | kazanc 10 | cagri 5,3,4 | asliyla ayni 3/3 bati deger farki boyut-gb: kayit 500, gercek 750 kapsamda kuzey deger farki sayi: kayit 4, gercek 6 kapsamda dogu kayitta olmayan alan ek-kapi = 9443 kapsamda kuzey kayit disi kaynak kuzey-arsiv kapsam disi kayitta yazmayan kalem 4 | modulu yazanin gordugu 0 | geri alinacak 3 | kayitsiz kalan 1
Girdi yüzeyi dörttür: bir zorunlu, üç varsayılanlı. Modül yirmi satırdır; dördü arayüz bildirimi, on dördü gövde, ikisi çıktıdır.
İlk satır arayüzün eksik yanını sayar. Sekiz alan yuvasının altısı üç kayıt arasında değişiyor ama
arayüzde yalnız dördü var; değişip arayüzde olmayan iki alan bolge ile baglanan. İkisi de
$ad üzerinden yazıldığı için ölçüm onları ayırt edemiyor. Oysa baglanan haklı olarak dışarıdadır:
bir bağımlılık adıdır, modülün kendi ürettiği kaynağa işaret eder. bolge öyle değil — bölge
değerinin nokta adıyla başlaması bir kural değil, üç örnekte tutan bir kalıptır. Modülü yazan taraf
kalıba güvenip alanı arayüzden sakladı; bedeli bir sonraki bölümde ödenir.
Ters yönde cikti durur. Hangi kaynağın adının dışarıya verileceği üç kaydın hiçbir yerinde yazmaz.
Girdi kaydın farkından çıkarılabilir, çıktı çıkarılamaz.
Çağrı satırları beş, üç ve dörttür. Batı üç satırla çağrılıyor çünkü üç varsayılanın üçü de batının kaydına uyuyor; varsayılan seçimi çoğunluğu ödüllendirir, azınlıkta kalana bir satır fazladan yazdırır. Satır kazancı kırk ikiden otuz ikiye, yani yüzde yirmi dörttür — üç kullanımda tek başına modül yazdıracak bir sayı değil.
Kaydın Kapsamadığı Dört Kalem
Modül üç kaydı birebir yeniden üretti, ama gerçekte var olan dört kalemin sıfırını gördü: bakılan
yer kayıttı. İkisi değer farkıdır — batının diski beş yüz yerine yedi yüz elli gigabayt,
kuzeyin makine kümesi dört yerine altı. Biri kayıtta hiç olmayan alandır: doğunun ağ kuralına
elle ikinci bir kapı açılmış. Sonuncusu kayıt dışı kaynaktır: kuzeydeki kuzey-arsiv nesne
deposundan hiçbir kayıt söz etmiyor.
Modülün kapsamı bu dördü ikiye böler. Üçü modülün ürettiği blokların içindedir; uygulandığı anda disk beş yüze, makine sayısı dörde döner ve fazladan kapı silinir — modülleştirme bu üç kalemi görmedi ama uygulama onları sessizce geri alacak. Dördüncüsü modülün hiç bilmediği bir kaynak türündedir; modül onu ne üretir ne siler. Modülleştirme sapmayı azaltmaz: kapsadığını tek bir yerden geri alınabilir kılar, kapsamadığını olduğu yerde bırakır.
Esneklik Bedeli
// surum.mjs — modul bes yeni kullanimla alti surume cikar; girdi yuzeyi ve esneklik bedeli sayilir. import { MODUL, uret, satir, zorunlu, varsayilan } from "./modul.mjs"; const ek = (m, ...s) => m.replace(/^(girdi .*)$(?![\s\S]*^girdi )/m, ["$1", ...s].join("\n")); const SURUM = [ // her surumun getirdigi degisiklik onceden yazili ["v1", "elle yazildi", ["kuzey", "bati", "dogu"], (m) => m], ["v2", "bolge adi kalibi kirildi", ["guney"], (m) => ek(m.replace(" bolge = $ad-1", " bolge = $bolge"), "girdi bolge")], ["v3", "disk istemeyen kullanim", ["sinama-alani"], (m) => ek(m.replace("k disk $ad-tampon {", "k disk $ad-tampon eger $disk-var {"), "girdi disk-var = evet")], ["v4", "cikis yonu ve ikinci kapi", ["saha-laboratuvari"], (m) => ek(m.replace(" yon = giris", " yon = $yon") .replace(" kapi = $kapi\n", " kapi = $kapi\n ek-kapi = $ek-kapi\n"), "girdi yon = giris", "girdi ek-kapi = yok")], ["v5", "eski imaji isteyen kullanim", ["arsiv-nokta"], (m) => ek(m.replace(" imaj = toplayici-4.2.0", " imaj = $imaj"), "girdi imaj = toplayici-4.2.0")], ["v6", "makine etiketi", ["ic-anadolu"], (m) => ek(m.replace(" sayi = $sayi\n", " sayi = $sayi\n etiket = $etiket\n"), "girdi etiket = yok")]]; const HAM = { // her kullanimin module gectigi degerler kuzey: "kaynak-blok=10.20.0.0/16 bolge=kuzey-1 sayi=4 boyut-gb=200", bati: "kaynak-blok=10.30.0.0/16 bolge=bati-1", dogu: "kaynak-blok=10.40.0.0/16 bolge=dogu-1 kapi=8443", guney: "kaynak-blok=10.50.0.0/16 bolge=guney-2", "sinama-alani": "kaynak-blok=10.60.0.0/16 bolge=sinama-1 disk-var=hayir sayi=1", "saha-laboratuvari": "kaynak-blok=10.70.0.0/16 bolge=saha-1 yon=cikis ek-kapi=9443 sayi=1", "arsiv-nokta": "kaynak-blok=10.80.0.0/16 bolge=arsiv-1 imaj=toplayici-3.9.0 boyut-gb=2000", "ic-anadolu": "kaynak-blok=10.90.0.0/16 bolge=ic-1 etiket=pilot sayi=2" }; const G = Object.fromEntries(Object.entries(HAM).map(([k, v]) => [k, Object.fromEntries(v.split(" ").map((x) => x.split("=")))])); let m = MODUL, acik = [], onceki = 1; console.log("surum degisiklik kullanim girdi (zorunlu/varsayilanli) modul satir kirici duzeltme"); for (const [s, not, yeni, d] of SURUM) { m = d(m); const zor = zorunlu(m).length, top = zor + Object.keys(varsayilan(m)).length; const kirici = zor > onceki ? acik.length : 0; // yeni zorunlu girdi eski cagrilari kirar acik = [...acik, ...yeni]; console.log(`${s} ${not.padEnd(28)} ${String(acik.length).padEnd(9)} ${`${top} (${zor}/${top - zor})`.padEnd(28)} ` + `${String(satir(m)).padEnd(12)} ${kirici ? `${kirici} tanim, ${kirici} satir` : "-"}`); onceki = zor; } acik = acik.filter((a) => a !== "arsiv-nokta"); // bir kullanim kapandi, actirdigi girdi modulde kaldi const vd = varsayilan(m), vars = Object.keys(vd); const kac = (a) => acik.filter((k) => a in G[k]).length; const olu = vars.filter((a) => kac(a) === 0), tek = vars.filter((a) => kac(a) === 1); const el = acik.reduce((t, a) => t + satir(uret(m, a, { ...vd, ...G[a] })), 0); const cs = acik.reduce((t, a) => t + Object.keys(G[a]).length + 2, 0); console.log(`acilan 8, kapanan 1, acik ${acik.length} | modulu degistirmeden karsilanan 0/5 | girdi yuzeyi 4 -> ` + `${vars.length + 2}, kullanim basina ${((vars.length - 2) / 5).toFixed(1)} | elle ${el} | modul yolu ` + `${satir(m)}+${cs}=${satir(m) + cs} | kazanc ${el - satir(m) - cs}`); console.log(`varsayilanli girdi ${vars.length}: iki ve uzeri ${vars.length - tek.length - olu.length}` + `, tek kullanim ${tek.length} (${tek}), olu ${olu.length} (${olu})`);
surum degisiklik kullanim girdi (zorunlu/varsayilanli) modul satir kirici duzeltme v1 elle yazildi 3 4 (1/3) 20 - v2 bolge adi kalibi kirildi 4 5 (2/3) 21 3 tanim, 3 satir v3 disk istemeyen kullanim 5 6 (2/4) 22 - v4 cikis yonu ve ikinci kapi 6 8 (2/6) 25 - v5 eski imaji isteyen kullanim 7 9 (2/7) 26 - v6 makine etiketi 8 10 (2/8) 28 - acilan 8, kapanan 1, acik 7 | modulu degistirmeden karsilanan 0/5 | girdi yuzeyi 4 -> 10, kullanim basina 1.2 | elle 96 | modul yolu 28+38=66 | kazanc 30 varsayilanli girdi 8: iki ve uzeri 1, tek kullanim 6 (boyut-gb,kapi,disk-var,yon,ek-kapi,etiket), olu 1 (imaj)
Esnekliğin bedeli tek satırda okunur: beş yeni kullanımın sıfırı modülü değiştirmeden karşılandı. Her yeni kullanım arayüzde yeni bir girdi açtırdı; girdi yüzeyi dörtten ona, modül metni yirmiden yirmi sekiz satıra çıktı. Ölçülen hız kullanım başına 1,2 girdidir.
İkinci sürümdeki kirici duzeltme sütunu bedelin en görünür biçimidir. bolge alanının ad kalıbı
dördüncü noktada kırılınca alan zorunlu girdiye çıktı ve o anda var olan üç tanımın üçü birden
geçersiz oldu. Yeni bir zorunlu girdi açmanın yükü modülü yazanın değil, kullananların üstündedir.
Son satır esnekliğin kime hizmet ettiğini sayar. Sekiz varsayılanlı girdinin yalnız biri iki ya da
daha çok kullanım tarafından geçiliyor; altısı tek bir kullanım için var, biri hiçbiri için. Ölü
girdi olan imaj beşinci sürümde arşiv noktası eski bir imaj istediği için açıldı; arşiv noktası
kapandı, girdi modülde kaldı. Bir girdiyi kaldırmak kullanan her tanımı ilgilendirdiği için kimse
kaldırmaya girişmez.
Kazanç yine de büyüdü. Yedi açık kullanım elle yazılsaydı doksan altı satır tutardı; modül yolu altmış altı satır ve kazanç yüzde otuz bir. Kazanç kullanım sayısıyla büyür, arayüz de büyür, ikisi aynı hızda gitmez.
Özet
- Elle yazılan modülün girdi yüzeyi 4’tür: 1 zorunlu, 3 varsayılanlı; 20 satırın 4’ü arayüz bildirimi, 2’si çıktıdır ve çıktı seçimi kayıttan okunamaz.
- 8 alan yuvasının 6’sı üç kayıt arasında değişiyor ama arayüzde 4’ü var;
bolgebir ad kalıbına güvenildiği için dışarıda kaldı. - Satır kazancı üç kullanımda 42’den 32’ye (yüzde 24), yedi kullanımda 96’dan 66’ya (yüzde 31) çıktı.
- Modül üç kaydı birebir yeniden üretti (3/3) ama gerçekte var olan 4 kalemin 0’ını gördü;
uygulamada 3’ü sessizce geri alınır, kayıt dışı
kuzey-arsivkayıtsız kalır. - 5 yeni kullanımın 0’ı modülü değiştirmeden karşılandı; girdi yüzeyi 4’ten 10’a çıktı, 8 varsayılanlı girdinin 6’sı tek kullanıma, 1’i hiçbirine hizmet ediyor.
- Yeni bir zorunlu girdi açmak kırıcıdır: ikinci sürüm o anki 3 tanımın 3’ünde 1’er satır düzeltme istedi; bu yük kullananların üstündedir.
Sonraki Adım
Bu modülün altı sürümü de aynı depoda, aynı elin altında durdu; yazan da kullanan da aynı taraftı. Modüller paylaşılan bir yerden çekilmeye başladığında bu ilişki kopar: yükseltme kararı bir yerde alınır, başka yerlerde hissedilir. Bir sonraki ders sürümü sabitlenmiş ve sabitlenmemiş iki düzeni bir dönem boyunca koşturur ve şunu sayar: tanım dosyası hiç değişmediği hâlde kaç uygulama farklı sonuç üretiyor, bir yükseltme kaç kullanıcıya dokunuyor, kırıcı bir değişiklik kaç tanımda kaç satır düzeltme istiyor. Sabitlemenin bedeli de aynı yerde ölçülür: kaç modül eski sürümde kalıyor ve kaç düzeltmeyi hiç almıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.