Ders 21 / 27
Biçim ve Doğrulama Denetimleri
Altyapı kaydının uygulanmadan önce sınanması: kurgu tanım dosyaları üzerinde biçim, söz dizimi ve tip/başvuru denetimlerinin gerçekten koşturulması, on altı bilinen kusurda her denetimin tuttuğu, yalnız kendisinin tuttuğu ve kaçırdığı sayıların ölçülmesi, koşum maliyetinin okunan dosya ve karşılaştırma cinsinden sayılması, statik denetimin göremediği kusur sınıfının adıyla yazılması.
İçindekiler
Kayıt yazıldı: altyapı bir yapılandırma dilinde tanımlandı, dosyalara bölündü, tekrar eden parçalar modülleşti. Buraya kadar ölçülen kaydın düzeniydi; hiç ölçülmeyen ise doğruluğu. Bu dosyalar uygulandığında istenen altyapı mı çıkıyor, yoksa uygulama yarıda mı kalıyor? Soru uygulamadan önce sorulmazsa yanıtı uygulamanın kendisi verir ve yanıtın bedeli yaratılmış kaynaktır.
Statik denetim kaydı uygulamadan önce okur: hiçbir kaynağa dokunmaz, sağlayıcıya bağlanmaz, yalnız diskteki metni okur. Bu ders üç statik denetimi yazar ve kurgu tanım dosyaları üzerinde koşturur — biçim, söz dizimi, tip ve başvuru tutarlılığı. Her denetimin bilinen kusur kümesinde tuttuğu ve yalnız kendisinin tuttuğu, kaçırdığı ve koşum maliyeti sayılır; sonda statik denetimin göremediği kusur sınıfı uygulamada ölçülür. Through-line bölgesel ölçüm ağıdır ve kurgudur.
Kayıt ve Bilinen Kusur Kümesi
AD1 — ölçüm ağının üretim ortamı dört tanım dosyasında on bir kaynakta tutulur: üç ağ, beş
makine, bir kuyruk, bir veri deposu, bir nesne deposu (kurgu). AD2 — yapılandırma dilinin
biçimi kendi küçük biçimimizdir: sütun 0’da blok başlığı, altında iki boşluk girintili alanlar,
= düz değer, -> başvuru. AD3 — bilinen kusur kümesi on altı kusurdur ve elle
işaretlenmiştir; her kusur temiz kaydın belirli bir satırına enjekte edilir ve bulgunun hangi
kusura karşılık geldiği satır numarasından okunur.
// kur.mjs — olcum aginin kaydini yazar, kopyasina bilinen kusurlari enjekte eder (kurgu) import { mkdirSync, writeFileSync } from "node:fs"; // bicim: sutun 0'da "kaynak <tur>/<ad>:", altinda iki bosluk girintili alan satirlari; // "=" duz deger, "->" baska bir kaynaga basvuru, "#" yorum, bloklar arasinda bir bos satir const blok = (tur, ad, alan) => `kaynak ${tur}/${ad}:\n` + alan.map(([a, y, d]) => ` ${a.padEnd(8)}${y} ${d}`).join("\n") + "\n"; const MAKINE = [["bati-1", "bati", "orta", 40], ["bati-2", "bati", "orta", 40], ["bati-3", "bati", "buyuk", 80], ["dogu-1", "dogu", "orta", 40], ["dogu-2", "dogu", "buyuk", 80]]; const DOSYA = { "ag.tanim": ["bati", "dogu", "kuzey"].map((b, i) => blok("ag", `omurga-${b}`, [["bolge", "=", b], ["blok", "=", `10.2${i}.0.0/16`], ["ozel", "=", "evet"]])).join("\n"), "makine.tanim": MAKINE.map(([n, b, by, d]) => blok("makine", `toplayici-${n}`, [["bolge", "=", b], ["boyut", "=", by], ["disk_gb", "=", d], ["ag", "->", `ag/omurga-${b}`]])).join("\n"), "akis.tanim": blok("kuyruk", "olcum-kuyrugu", [["bolge", "=", "bati"], ["saklama", "=", 72], ["ag", "->", "ag/omurga-bati"]]) + "\n" + blok("veri_deposu", "olcum-deposu", [["bolge", "=", "bati"], ["boyut", "=", "buyuk"], ["disk_gb", "=", 500], ["yedek", "=", 14], ["ag", "->", "ag/omurga-bati"]]), "arsiv.tanim": blok("nesne_deposu", "olcum-arsiv", [["bolge", "=", "bati"], ["surumlu", "=", "evet"], ["sinif", "=", "soguk"]]), }; // bilinen kusur kumesi: [kod, dosya, temiz kayittaki satir, yerine gelen, kusurun konumu] const KUSUR = [ ["K01", "ag.tanim", 2, " bolge = bati "], ["K02", "ag.tanim", 9, " ozel = acik"], ["K03", "makine.tanim", 3, " boyut = orta"], ["K04", "makine.tanim", 5, " ag -> nesne_deposu/olcum-arsiv"], ["K05", "makine.tanim", 10, " disk_gb=40"], ["K06", "makine.tanim", 16, " disk_gb = seksen"], ["K07", "makine.tanim", 18, "@sil", 19], ["K08", "makine.tanim", 22, " disk_gb = 40\n disk_gb = 60"], ["K09", "makine.tanim", 23, " ag -> ag/omurga-guney"], ["K10", "makine.tanim", 25, "kaynak makine/toplayici-dogu-1:"], ["K11", "ag.tanim", 11, "kaynak ag/omurga-kuzey"], ["K12", "akis.tanim", 2, " bolge = bati\n bolgeler = bati,dogu"], ["K13", "akis.tanim", 3, "\tsaklama = 72"], ["K14", "akis.tanim", 8, " boyut ="], ["K15", "akis.tanim", 11, " # ag baglantisi sonra eklenecek", 6], ["K16", "arsiv.tanim", 4, " sinif = soguk\n etiket olcum arsivi"], ]; mkdirSync("kayit", { recursive: true }); mkdirSync("taslak", { recursive: true }); const konum = []; for (const [dosya, icerik] of Object.entries(DOSYA)) { writeFileSync(`kayit/${dosya}`, icerik); const yama = new Map(KUSUR.filter((k) => k[1] === dosya).map((k) => [k[2], k[3]])); const yeni = [], harita = {}; icerik.split("\n").forEach((s, i) => { // bir kusur satiri silebilir ya da satir ekleyebilir const d = yama.get(i + 1); if (d === "@sil") return; yeni.push(...(d ?? s).split("\n")); harita[i + 1] = yeni.length; // temiz kayittaki satir -> taslaktaki son karsiligi }); writeFileSync(`taslak/${dosya}`, yeni.join("\n")); for (const [kod, , satir, , k] of KUSUR.filter((x) => x[1] === dosya)) konum.push({ kod, dosya, satir: harita[k ?? satir] }); } writeFileSync("kusur.json", JSON.stringify(konum)); const metin = Object.values(DOSYA).join(""); console.log(`kayit: ${Object.keys(DOSYA).length} dosya, ${metin.match(/^kaynak /gm).length} kaynak, ` + `${metin.split("\n").length} satir; taslak: ayni kayit + ${KUSUR.length} bilinen kusur\n`); console.log(DOSYA["akis.tanim"].split("\n").slice(0, 4).join("\n"));
kayit: 4 dosya, 11 kaynak, 59 satir; taslak: ayni kayit + 16 bilinen kusur kaynak kuyruk/olcum-kuyrugu: bolge = bati saklama = 72 ag -> ag/omurga-bati
Üç Statik Denetim
AD4 — biçim kuralları altı tanedir ve ekipçe kabul edilmiştir. AD5 — her kaynak türünün
alanları, tipleri ve zorunlulukları kayıtla birlikte tutulan bir şemadadır. Üçü de aynı biçimde
bulgu üretir — dosya, satır, kural — ama bağlamları farklıdır: biçim tek satıra bakar, söz dizimi
satıra ve bloğuna bakar, tip/başvuru kaydın tamamını ister; çünkü bir makinenin ag alanı başka
dosyadaki kaynağa işaret eder.
// denetim.mjs — ayni kayit uzerinde uc statik denetim: bicim, soz dizimi, tip/basvuru import { readFileSync, readdirSync } from "node:fs"; const SEMA = { // tur -> "alan:tip" listesi; "!" zorunlu, "@t" t turunden bir kaynaga basvuru ag: "bolge:metin! blok:metin! ozel:mantik!", makine: "bolge:metin! boyut:metin! disk_gb:sayi! ag:@ag!", kuyruk: "bolge:metin! saklama:sayi! ag:@ag!", veri_deposu: "bolge:metin! boyut:metin! disk_gb:sayi! yedek:sayi! ag:@ag!", nesne_deposu: "bolge:metin! surumlu:mantik! sinif:metin!", }; const alanlar = (tur) => Object.fromEntries(SEMA[tur].split(" ").map((x) => x.split(":"))); const BASLIK = /^kaynak ([a-z_]+)\/([a-z0-9-]+):$/, ALAN = /^\s+([a-z_]+)\s*(=|->)\s*(.*)$/; const oku = (dz) => readdirSync(dz).filter((d) => d.endsWith(".tanim")) .map((d) => [d, readFileSync(`${dz}/${d}`, "utf8").split("\n")]); const say = { bicim: 0, sozdizim: 0, tip: 0 }; // 1) bicim: alti satir-yerel kural; bir satirdan baska hicbir sey okumaz const R = [ ["R1", (s) => s.length && s !== s.trimEnd()], // satir sonunda bosluk ["R2", (s) => /^ *\t/.test(s)], // girintide sekme ["R3", (s) => /^ +\S/.test(s) && !/^ {2}\S/.test(s)], // girinti iki bosluk degil ["R4", (s) => /=|->/.test(s) && !/^\s*#/.test(s) && !/ (=|->)( |$)/.test(s)], // ayrac boslugu yok ["R5a", (s) => /^[^\s#]/.test(s) && !s.endsWith(":")], // baslik iki nokta ile bitmiyor ["R5b", (s, i, L) => /^[^\s#]/.test(s) && i > 0 && L[i - 1] !== ""], // blok oncesi bos satir yok ]; function bicim(dosyalar) { const b = []; for (const [d, L] of dosyalar) L.forEach((s, i) => R.forEach(([k, f]) => { say.bicim += 1; if (f(s, i, L)) b.push({ dosya: d, satir: i + 1, kural: k }); })); return b; } // 2) soz dizimi: satir ve blok yerel; ayni dosyanin disina hic bakmaz function sozdizim(dosyalar) { const b = [], ad = new Map(); for (const [d, L] of dosyalar) { let anahtar = null; L.forEach((s, i) => { const E = (k) => b.push({ dosya: d, satir: i + 1, kural: k }); say.sozdizim += 3; if (s === "" || /^\s*#/.test(s)) return; if (/^\S/.test(s)) { const m = BASLIK.exec(s); anahtar = null; if (!m) return E("P1 baslik kalibi tutmuyor"); anahtar = new Set(); const yol = `${m[1]}/${m[2]}`; if (ad.has(yol)) E(`P4 yinelenen kaynak adi (${ad.get(yol)})`); else ad.set(yol, `${d}:${i + 1}`); return; } const m = ALAN.exec(s); if (!m) return E("P2 taninmayan satir"); if (m[3].trim() === "") return E("P2 deger yok"); if (anahtar?.has(m[1])) E(`P3 yinelenen anahtar (${m[1]})`); anahtar?.add(m[1]); }); } return b; } // 3) tip/basvuru: kaydin tamamini ister, cunku basvurular dosya sinirini asar function tipBasvuru(dosyalar) { const kaynak = [], b = []; for (const [d, L] of dosyalar) { // bozuk baslikli blok modele hic girmez let c = null; L.forEach((s, i) => { const m = /^\S/.test(s) ? BASLIK.exec(s) : ALAN.exec(s); if (/^\S/.test(s)) c = m ? { tur: m[1], ad: m[2], dosya: d, satir: i + 1, alan: {} } : null; else if (c && m) c.alan[m[1]] = { deger: m[3].trim(), satir: i + 1 }; else return; if (/^\S/.test(s) && c) kaynak.push(c); }); } const tipi = (v) => (["evet", "hayir"].includes(v) ? "mantik" : /^-?\d+$/.test(v) ? "sayi" : "metin"); for (const k of kaynak) { const sema = alanlar(k.tur); for (const [a, v] of Object.entries(k.alan)) { const E = (kural) => b.push({ dosya: k.dosya, satir: v.satir, kural }); say.tip += 2; if (!sema[a]) { E(`C1 semada yok (${a})`); continue; } const t = sema[a].replace("!", ""); if (!t.startsWith("@")) { if (v.deger === "" || tipi(v.deger) !== t) E(`C2 ${t} degil ("${v.deger}")`); continue; } let h = null; for (const x of kaynak) { say.tip += 1; if (`${x.tur}/${x.ad}` === v.deger) { h = x; break; } } if (!h) E(`C4 hedef yok (${v.deger})`); else if (h.tur !== t.slice(1)) E(`C5 yanlis tur (${h.tur})`); } for (const [a, t] of Object.entries(sema)) { say.tip += 1; if (t.endsWith("!") && !k.alan[a]) b.push({ dosya: k.dosya, satir: k.satir, kural: `C3 zorunlu alan yok (${a})` }); } } return b; } const taslak = oku("taslak"), temiz = oku("kayit"); const D = [["bicim", bicim, 1], ["sozdizim", sozdizim, 1], ["tip/basvuru", tipBasvuru, 4]]; const bulgu = D.map(([ad, f]) => [ad, f(taslak)]); const m = [say.bicim, say.sozdizim, say.tip]; // sayaclar yalniz taslak kosumundan okunur const K = JSON.parse(readFileSync("kusur.json", "utf8")); const kod = (f) => K.find((x) => x.dosya === f.dosya && x.satir === f.satir)?.kod ?? "??"; console.log(`${"denetim".padEnd(13)}${"kod".padEnd(5)}${"konum".padEnd(20)}kural`); for (const [ad, l] of bulgu) for (const f of l) console.log(`${ad.padEnd(13)}${kod(f).padEnd(5)}${`${f.dosya}:${f.satir}`.padEnd(20)}${f.kural}`); const tut = bulgu.map(([, l]) => new Set(l.map(kod))); console.log(`\n${"denetim".padEnd(13)}${"bulgu".padStart(6)}${"tuttugu".padStart(9)}` + `${"yalniz kendisi".padStart(16)}${"kacirdigi".padStart(11)}${"karsilastirma".padStart(15)}` + `${"tek dosya icin".padStart(16)}`); bulgu.forEach(([ad, l], j) => { const y = [...tut[j]].filter((k) => tut.every((s, o) => o === j || !s.has(k))).length; console.log(`${ad.padEnd(13)}${String(l.length).padStart(6)}${String(tut[j].size).padStart(9)}` + `${String(y).padStart(16)}${String(K.length - tut[j].size).padStart(11)}` + `${String(m[j]).padStart(15)}${String(D[j][2]).padStart(16)}`); }); console.log(`\nuc denetim birlikte ${K.length} kusurun ` + `${new Set(tut.flatMap((s) => [...s])).size} tanesini tutar; hicbir kusura karsilik gelmeyen ` + `bulgu ${bulgu.flatMap(([, l]) => l).filter((f) => kod(f) === "??").length}; ` + `temiz kayitta toplam bulgu ${D.reduce((s, [, f]) => s + f(temiz).length, 0)}`);
denetim kod konum kural
bicim K01 ag.tanim:2 R1
bicim K11 ag.tanim:11 R5a
bicim K13 akis.tanim:4 R2
bicim K03 makine.tanim:3 R3
bicim K05 makine.tanim:10 R4
bicim K07 makine.tanim:18 R5b
sozdizim K11 ag.tanim:11 P1 baslik kalibi tutmuyor
sozdizim K14 akis.tanim:9 P2 deger yok
sozdizim K16 arsiv.tanim:5 P2 taninmayan satir
sozdizim K08 makine.tanim:22 P3 yinelenen anahtar (disk_gb)
sozdizim K10 makine.tanim:25 P4 yinelenen kaynak adi (makine.tanim:18)
tip/basvuru K02 ag.tanim:9 C2 mantik degil ("acik")
tip/basvuru K12 akis.tanim:3 C1 semada yok (bolgeler)
tip/basvuru K14 akis.tanim:9 C2 metin degil ("")
tip/basvuru K15 akis.tanim:7 C3 zorunlu alan yok (ag)
tip/basvuru K04 makine.tanim:5 C5 yanlis tur (nesne_deposu)
tip/basvuru K06 makine.tanim:16 C2 sayi degil ("seksen")
tip/basvuru K09 makine.tanim:23 C4 hedef yok (ag/omurga-guney)
denetim bulgu tuttugu yalniz kendisi kacirdigi karsilastirma tek dosya icin
bicim 6 6 5 10 384 1
sozdizim 5 5 3 11 192 1
tip/basvuru 7 7 6 9 131 4
uc denetim birlikte 16 kusurun 16 tanesini tutar; hicbir kusura karsilik gelmeyen bulgu 0; temiz kayitta toplam bulgu 0
Kapsam, Örtüşme ve Bedel
Üç denetim on altı kusurun on altısını da tutar, ama hiçbiri tek başına yetmez: en geniş kapsamlı olan tip/başvuru bile dokuz kusuru kaçırır. Örtüşme iki yerdedir — başlıktaki iki nokta eksikliği (K11) hem biçime hem söz dizimine, boş bırakılmış değer (K14) hem söz dizimine hem tipe takılır. Kalan on dört kusur tek bir denetimin işidir; “yalnız kendisi” sütunu 5, 3 ve 6 verir.
Karşılaştırma sayıları sezginin tersini söyler: en çok karşılaştırma yapan denetim en az bağlam isteyendir. Biçim 384 karşılaştırma yapar (64 satır çarpı altı kural), tip/başvuru yalnız 131. Bedel karşılaştırmada değil kapsamdadır: tek dosya değiştiğinde ilk iki denetim o dosyayı okuyup karar verebilir, tip/başvuru dördünü birden okumak zorundadır. Üstelik başvuru çözümü kaynak listesini baştan taradığından tip denetiminin karşılaştırması kaynak sayısıyla karesel büyür. Tabloda sessiz iki sayı daha var: kusura karşılık gelmeyen bulgu ile temiz kayıttaki toplam bulgu sıfırdır.
Statik Denetimin Göremediği Sınıf
Üç denetimin de göremediği kusurların ortak adı sağlayıcı gerçeğine bağlı kusurlardır: kararı veren durum kayıtta değildir — bölgedeki kota, bölgede bulunan makine boyutları, ad havuzunda tutulmuş bir ad, o kaynak türünde yaratma izni. Hiçbiri metinde yazmaz.
AD6 — sağlayıcının kotası, bölge-boyut kataloğu, ad havuzu, izinleri ve kaynak yaratma süreleri kurgudur. Aşağıdaki blok statik denetimin sıfır bulgu verdiği temiz kaydı bu modele uygular; kalemler kaydın okunma sırasına göre işlenir ve reddedilen atlanır.
// uygulama.mjs — temiz kayit bir saglayici modeline uygulanir; saglayici da sureler de kurgudur import { readFileSync, readdirSync } from "node:fs"; const SAGLAYICI = { kota: { bati: 2, dogu: 4 }, // bolge basina makine ust siniri katalog: { bati: ["orta", "buyuk"], dogu: ["orta"] }, // bolgede bulunan boyutlar tutulanAd: ["olcum-arsiv"], // ad kuresel benzersiz izin: { ag: 1, makine: 1, kuyruk: 1, veri_deposu: 0, nesne_deposu: 1 }, }; const SURE = { ag: 20, makine: 45, kuyruk: 30, veri_deposu: 180, nesne_deposu: 15, red: 5 }; const kaynak = []; for (const d of readdirSync("kayit")) for (const s of readFileSync(`kayit/${d}`, "utf8").split("\n")) { const h = /^kaynak ([a-z_]+)\/([a-z0-9-]+):$/.exec(s), a = /^\s+([a-z_]+)\s*=\s*(.+)$/.exec(s); if (h) kaynak.push({ tur: h[1], ad: h[2], alan: {} }); else if (a) kaynak.at(-1).alan[a[1]] = a[2].trim(); } let saat = 0, yaratilan = 0; const red = [], sayilan = {}; for (const k of kaynak) { let hata = null; if (!SAGLAYICI.izin[k.tur]) hata = "yetki reddi"; else if (SAGLAYICI.tutulanAd.includes(k.ad)) hata = "ad tutulmus"; else if (k.tur === "makine") { sayilan[k.alan.bolge] = (sayilan[k.alan.bolge] ?? 0) + 1; if (sayilan[k.alan.bolge] > SAGLAYICI.kota[k.alan.bolge]) hata = "kota asildi"; else if (!SAGLAYICI.katalog[k.alan.bolge].includes(k.alan.boyut)) hata = "katalogda yok"; } if (hata) red.push({ ad: `${k.tur}/${k.ad}`, hata, saniye: saat, yaratilan }); else yaratilan += 1; saat += hata ? SURE.red : SURE[k.tur]; } console.log(`${kaynak.length} kalem uygulandi: ${yaratilan} yaratildi, ${red.length} reddedildi, ` + `toplam ${saat} saniye\n`); console.log(`${"reddedilen kalem".padEnd(28)}${"neden".padEnd(15)}${"kacinci saniyede".padStart(17)}` + `${"o ana dek yaratilan".padStart(21)}`); for (const r of red) console.log(`${r.ad.padEnd(28)}${r.hata.padEnd(15)}` + `${String(r.saniye).padStart(17)}${String(r.yaratilan).padStart(21)}`);
11 kalem uygulandi: 7 yaratildi, 4 reddedildi, toplam 245 saniye reddedilen kalem neden kacinci saniyede o ana dek yaratilan veri_deposu/olcum-deposu yetki reddi 90 4 nesne_deposu/olcum-arsiv ad tutulmus 95 4 makine/toplayici-bati-3 kota asildi 190 6 makine/toplayici-dogu-2 katalogda yok 240 7
Dört kusur da statik denetimin temiz saydığı kayıttadır; fark zamanda ve bedeldedir. Üç denetim 64 satırı 707 karşılaştırmayla tarar ve sonucu hiç kaynak yaratmadan verir; sağlayıcıya bağlı ilk kusur 90., sonuncusu 240. saniyede görünür ve o anlara kadar dört ve yedi kaynak yaratılmıştır. Koşum bittiğinde altyapı yarımdır: kuyruk vardır, beslediği veri deposu yoktur. Bu sınıfı statik denetime taşımanın tek yolu sağlayıcı durumunu kayda getirmektir — yeni bir kayıt, yeni bir sapma kaynağı.
Özet
- Kurgu ölçüm ağının kaydı dört dosyada on bir kaynak tutar; kopyasına on altı bilinen kusur enjekte edilip üç statik denetim gerçek dosyalarda koşturuldu.
- Biçim 6 bulgu verdi (5’i yalnız kendisinin), söz dizimi 5 (3’ü), tip/başvuru 7 (6’sı); üçü birlikte 16 kusurun 16’sını tuttu, en geniş denetim tek başına 9 kusur kaçırdı.
- Örtüşme iki kusurdadır: iki nokta eksikliği biçim ile söz dizimine, boş değer söz dizimi ile tipe takılır.
- Bedel karşılaştırmada değil kapsamdadır: biçim 384, söz dizimi 192, tip/başvuru 131 karşılaştırma yapar, ama tek dosya değişince ilk ikisi 1, tip/başvuru 4 dosya okur.
- Göremediği sınıf sağlayıcı gerçeğine bağlı kusurlardır (kota, bölge kataloğu, tutulmuş ad, yetki); sıfır bulguyla geçen kayıtta dördü uygulamada çıktı (ilki 90., sonuncusu 240. saniyede) ve geride 7 yaratılmış kaynakla yarım bir altyapı bıraktı.
Sonraki Adım
Bu dersin denetimleri kaydın kendi kurallarını sınadı. Hiçbiri “bu kaynak açık ağa bakmamalı” türünden bir kuralı sınamadı; böyle kurallar dışarıdan gelir ve kaydın doğruluğuyla değil kabul edilebilirliğiyle ilgilidir. Sonraki ders bir politika kümesini aynı kayıt üzerinde koşturur: politika tanım üzerinde mi plan üzerinde mi koşmalı? İki tarama aynı ihlal kümesinde karşılaştırılır — hangisi hesaplanan değerleri görüyor ve muafiyet kaç ihlale yol açıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.