---
title: 'Kod Olarak Altyapı'
source: 'https://academia.sh/tr/kurslar/kod-olarak-altyapi'
language: tr
updated: '2026-08-17T04:40:38+00:00'
license: 'CC BY-SA 4.0'
lessons: 27
outcomes:
    - 'Bildirimsel altyapı tanımıyla kaynak sağlar ve değiştirir'
    - 'Durum yönetimini ekip çalışmasına uygun kurar'
    - 'Modüllerle yeniden kullanılabilir altyapı bileşenleri yazar'
---

# Kod Olarak Altyapı

Altyapının bir kayda bağlanması ve kaydın gerçekle uyuşmasının ölçülmesi: yeniden üretilebilirlik ve izlenebilirlik, bildirimsel ile buyurgan yaklaşımın ayrımı, yerinde değiştirme yerine yeniden oluşturma, sağlama ile yapılandırma yönetiminin ayrı katmanları, elle değişikliğin sapma olarak tespiti, yapılandırma dilinin blok ve tip yapıları, sağlayıcılar, kaynaklar ve veri kaynakları, parametreleştirme, bağımlılık sıralaması, yineleme, planlama ve uygulama akışı, kaynak değiştirme ve içe aktarma, durum dosyası, uzak durum ve kilitleme, durum bölme, çalışma alanları, modül yazımı ve kayıtları, hassas değerler, statik denetimler, politika taraması, altyapı testi, maliyet tahmini, ajanlı ve ajansız yapılandırma yönetimi, sunucu imajı üretimi ve hat ile tümleştirme.

## Temel Kavramlar

Altyapının koda bağlanmasının ne kazandırdığı: yeniden üretilebilirlik ve izlenebilirliğin sayıya bağlanması, istenen durum bildirimi ile adım listesinin farkı, yerinde değiştirme yerine yeniden oluşturmanın bedeli ve kazancı, sağlama katmanı ile yapılandırma yönetimi katmanının ayrımı ve elle yapılan değişikliğin sapma olarak tespiti.

### Kod Olarak Altyapı Nedir

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/kod-olarak-altyapi-nedir

Aynı hazırlık ortamı iki kez kurulur — bir düzyazı talepten elle, bir kayıttan — ve iki kurulum kalem kalem karşılaştırılır. Bir çeyreklik değişikliklerde kim, ne zaman ve ne değiştirdi sorusunun kaç kez yanıtlandığı, kaydın kapsamadığı kalem sayısı ve kayda bağlamanın satır ve ek adım olarak bedeli sayılır.

Bir önceki kurs uygulamanın üretime çıkış yolunu depoya bağladı: dal birleşince hat koşuyor,
kapılar geçiliyor, dağıtım kendiliğinden ilerliyor. Uygulamanın yolu baştan sona yazılı. Altında
durduğu makineler, kuyruk, veri deposu ve ağ ise başka bir yolla kuruluyor — bir talep açılıyor,
biri o talebi okuyup ortamı kuruyor ve kurulumun sonucu yalnız ortamın kendisinde duruyor.

**Kod olarak altyapı**, altyapının okunabilir bir **kayda** bağlanmasıdır: ortamın ne olması
gerektiği bir dosyada yazılıdır, ortam o dosyadan kurulur. Bu kursun ölçü birimi kayıttır ve her
ders üç şeyi sayar — kaydın söylediği ile gerçeğin farkı, kaydın kapsamı ve kaydın bedeli.
Bu ders ilkini kurar: aynı ortam iki kez kurulur, bir düzyazı talepten elle ve bir kayıttan, sonra
**kalem kalem** karşılaştırılır. Ardından bir çeyreklik değişiklikte "kim, ne zaman, ne değiştirdi"
sorusunun kaç kez yanıtlandığı, kaydın kapsamadığı kalem sayısı ve kayda bağlamanın satır ve adım
olarak bedeli ölçülür.

Ölçüm **bölgesel ölçüm ağı** üzerindedir: su sayacı okumalarını toplayan, doğrulayan, faturaya
çeviren ve saha iş emri açan **kurgu** yazılım. Üç ortamı vardır; burada yalnız hazırlık ortamı
kurulur. Altyapı gerçekten sağlanmaz — kaynaklar ve kurulumlar `node` ile modellenir; modelin
girdisi olan kayıt ve talep dosyaları gerçekten diske yazılır.

**AT1.** Hazırlık ortamı altı kaynakla temsil edilir — bir ağ, iki makine grubu, bir kuyruk, bir
veri deposu, bir nesne deposu. Kalem bir kaynağın bir niteliğidir; kayıtta yirmi kalem yazılıdır.
**AT2.** Kurulum talebi düzyazıdır ve on bir maddenin yedisi sayı vermez. **El bir modeldir**,
yorumu önceden yazılıdır; rastgelelik yoktur.
**AT3.** Sağlayıcı varsayılanı beş kalemdir: kayıtta yazmayan, ortamda her hâlükârda oluşan
ayarlar. İki kurulumda da aynı doğar.
**AT4.** Bir çeyrekte on dört değişiklik olur; her değişiklik üç soru sorar — kim, ne zaman, ne
değiştirdi. Her değişikliğin yolu önceden yazılıdır.
**AT5.** Kayıt üzerinden geçen değişiklik üç soruyu da yanıtlar; bir iş kaydına düşen değişiklik
ikisini yanıtlar (ne değiştiğini yazmaz); izsiz değişiklik hiçbirini.
**AT6.** Kayıt yolunun değişiklik başına üç ek adımı vardır: kaydı yaz, gözden geçirt, planı oku.

#### Aynı Ortam, İki Tarif

```bash
#!/usr/bin/env bash
# Ayni ortamin iki tarifi: kayit (yapilandirma dili) ve duzyazi kurulum talebi.
set -e

cat > kayit.alt <<'SON'
# Yapilandirma dilimizin bicimi: kaynak <tur> "<ad>" { <anahtar> = <deger> }
# Deger sayi, "dize" ya da dogru/yanlis olur; # ile baslayan satir yorumdur.
kaynak ag "olcum-agi" { blok = "10.40.0.0/16"  ozel = dogru  bolge = "kuzey" }
kaynak makine "toplayici" { sayi = 3  boyut = "orta"  disk_gb = 40  imaj = "taban-7" }
kaynak makine "dogrulayici" { sayi = 2  boyut = "kucuk"  disk_gb = 20  imaj = "taban-7" }
kaynak kuyruk "okuma-kuyrugu" { saklama_saat = 72  bolum = 6  olu_mektup = dogru }
kaynak veri_deposu "olcum-deposu" { boyut_gb = 200  yedek_gun = 14  surum = "5.2" }
kaynak nesne_deposu "arsiv" { sinif = "seyrek"  surumleme = dogru  yasam_gun = 365 }
SON

cat > talep.txt <<'SON'
hazirlik ortami kurulum talebi
1. olcum agini ozel olarak ac, adres blogu 10.40.0.0/16, kuzey bolgesi
2. toplayici makinelerden yeterli sayida ac, boyut orta, imaj taban-7
3. toplayici diski olcum hacmine gore ver
4. dogrulayici makinelerden iki tane, boyut kucuk, disk 20 GB, imaj taban-7
5. okuma kuyrugunu ac, saklama suresi uretime yakin olsun, olu mektup acik
6. kuyruk bolum sayisini isci sayisina gore ayarla
7. olcum deposunu 200 GB ac, surum 5.2
8. deponun yedek penceresini kurumsal olcute gore ayarla
9. arsiv icin nesne deposu ac, surumleme acik
10. arsiv saklama sinifini maliyete gore sec
11. arsiv yasam dongusu gunu icin makul bir deger yaz
SON
```

Yapılandırma dili burada kendi küçük biçimimizle tanımlanır: bir `kaynak` satırı bir türü, bir adı
ve süslü parantez içinde anahtar-değer çiftlerini taşır. Altı satır yirmi kalemi sayar ve her kalem
tek bir değere bağlıdır — okuyan iki kişi aynı şeyi anlar. Talep aynı ortamı hedefler ama yedi
maddesi sayı yerine ölçüt verir: "yeterli sayıda", "ölçüm hacmine göre", "üretime yakın", "kurumsal
ölçüte göre", "maliyete göre", "makul bir değer". Bu yedi nokta talebin **yazılmamış** kısmıdır ve
onu kuran kişi doldurur.

```js
// kur.mjs — kaydi ayristirir ve ayni ortami iki yoldan kurar: kayittan ve elle. Ikisi de modeldir.
import { readFileSync } from "node:fs";

const BLOK = /^kaynak\s+(\w+)\s+"([\w-]+)"\s*\{(.*)\}$/;      // dilin tek satirlik bicimi
export const kaydiOku = (metin) => {
  const k = new Map();
  for (const satir of metin.split("\n")) {
    const m = BLOK.exec(satir.trim());                        // yorum ve bos satir atlanir
    if (m) for (const x of m[3].matchAll(/(\w+)\s*=\s*("[^"]*"|[\w./-]+)/g))
      k.set(`${m[1]}.${m[2]}.${x[1]}`, x[2].replaceAll('"', ""));
  }
  return k;
};
export const kayit = kaydiOku(readFileSync("kayit.alt", "utf8"));

// Saglayici varsayilani: kayitta yazmayan, ortamda her halukarda olusan kalemler.
const varsayilan = new Map([["ag.olcum-agi.cikis_yolu", "acik"],
  ["makine.toplayici.erisim_kurali", "yonetim-portu-acik"],
  ["kuyruk.okuma-kuyrugu.sifreleme", "saglayici-anahtari"],
  ["veri_deposu.olcum-deposu.bakim_penceresi", "pazar-03:00"],
  ["nesne_deposu.arsiv.erisim_kurali", "hesap-ici"]]);

// Elin onceden yazili yorumu: talebin sayi vermedigi yedi nokta (2, 3, 5, 6, 8, 10, 11).
const elinYorumu = new Map([["makine.toplayici.sayi", "2"], ["makine.toplayici.disk_gb", "40"],
  ["kuyruk.okuma-kuyrugu.saklama_saat", "24"], ["kuyruk.okuma-kuyrugu.bolum", "6"],
  ["veri_deposu.olcum-deposu.yedek_gun", "7"], ["nesne_deposu.arsiv.sinif", "sik"],
  ["nesne_deposu.arsiv.yasam_gun", "90"]]);
const elinAtladigi = ["nesne_deposu.arsiv.surumleme"];        // talepte yazili, uygulamada atlandi
const elinEkledigi = new Map([["ag.olcum-agi.erisim_kurali", "ofis-blogu"],
  ["makine.toplayici.ek_disk", "bagli"], ["makine.dogrulayici.ad_kaydi", "dogrulayici.ic"]]);

export const kayittanKur = () => new Map([...kayit, ...varsayilan]);
export const elleKur = () => {
  const o = new Map([...kayit, ...elinYorumu, ...varsayilan, ...elinEkledigi]);
  for (const a of elinAtladigi) o.delete(a);
  return o;
};
export const yorumNoktasi = elinYorumu.size, kayitDisi = varsayilan.size;
```

#### Kalem Kalem Fark

```js
// fark.mjs — iki kurulumu kalem kalem karsilastirir; hicbir sey duzeltmez.
import { kayit, kayittanKur, elleKur, yorumNoktasi, kayitDisi } from "./kur.mjs";

const a = kayittanKur(), b = elleKur();
const tum = [...new Set([...a.keys(), ...b.keys()])].sort();
const fark = tum.filter((k) => a.get(k) !== b.get(k)).map((k) => ({ k,
  tur: !b.has(k) ? "eksik" : !a.has(k) ? "fazla" : "ayrik" }));
const say = (t) => fark.filter((f) => f.tur === t).length;

console.log(`kalem  kayittan ${a.size}   elle ${b.size}   birlesim ${tum.length}`);
console.log(`fark ${fark.length} kalem   (ayrik ${say("ayrik")}, eksik ${say("eksik")},` +
  ` fazla ${say("fazla")})`);
for (const f of fark)
  console.log(`  ${f.tur.padEnd(6)} ${f.k.padEnd(38)} ${(a.get(f.k) ?? "-").padEnd(20)}` +
    ` ${b.get(f.k) ?? "-"}`);
console.log(`kayittaki kalem ${kayit.size}   talebin sayi vermedigi nokta ${yorumNoktasi}` +
  `   bu noktalarin ayrildigi ${fark.filter((f) => f.tur === "ayrik").length}`);
console.log(`kaydin kapsamadigi kalem ${kayitDisi}   iki kurulumda da ayni,` +
  ` hicbirinde yazili degil`);
```

```
kalem  kayittan 25   elle 27   birlesim 28
fark 9 kalem   (ayrik 5, eksik 1, fazla 3)
  fazla  ag.olcum-agi.erisim_kurali             -                    ofis-blogu
  ayrik  kuyruk.okuma-kuyrugu.saklama_saat      72                   24
  fazla  makine.dogrulayici.ad_kaydi            -                    dogrulayici.ic
  fazla  makine.toplayici.ek_disk               -                    bagli
  ayrik  makine.toplayici.sayi                  3                    2
  ayrik  nesne_deposu.arsiv.sinif               seyrek               sik
  eksik  nesne_deposu.arsiv.surumleme           dogru                -
  ayrik  nesne_deposu.arsiv.yasam_gun           365                  90
  ayrik  veri_deposu.olcum-deposu.yedek_gun     14                   7
kayittaki kalem 20   talebin sayi vermedigi nokta 7   bu noktalarin ayrildigi 5
kaydin kapsamadigi kalem 5   iki kurulumda da ayni, hicbirinde yazili degil
```

Aynı ortamın iki kurulumu dokuz kalemde ayrıldı ve fark üç ayrı türden geliyor. **Ayrık** beş
kalem, talebin sayı vermediği yedi noktanın beşidir: kalan ikisi elin yorumu kayıtla aynı değere
düştüğü için görünmez. Bu ikisi bir kazanç değil, bir tesadüftür — yorum noktası hâlâ yedidir,
yalnız beşi bu koşumda ayrıldı. **Eksik** bir kalem talepte açıkça yazılıydı ve uygulanmadı;
düzyazı bir listede atlanan madde hiçbir yerde iz bırakmaz. **Fazla** üç kalem ise talepte hiç
geçmiyor: bir erişim kuralı, bir ek disk ve bir ad kaydı. Bunlar ortamı kuran kişinin rutininden
geliyor ve hiçbir tarifte yazılı değil — kurulum bittiğinde varlar, ama neden var oldukları
yalnız bir kişinin belleğinde.

Farkın yönü de anlamlıdır. Ayrık beş kalemin dördünde elin değeri kayıttakinden **küçüktür** — iki
toplayıcı yerine üç, yetmiş iki saat yerine yirmi dört, on dört gün yerine yedi, üç yüz altmış beş
gün yerine doksan. Ölçüt vermeyen bir talep okunduğunda seçilen değer, o anda görünen yükü karşılar;
kaydın taşıdığı değer ise bir kez kararlaştırılmış olanı taşır. İki kurulum arasındaki fark bu
yüzden bir hata listesi değil, **iki ayrı karar anının** listesidir.

Son satır bu dersin ölçü ekseninin ikinci yarısıdır. Kaydın kapsamadığı beş kalem **her iki
kurulumda da** aynıdır, çünkü ikisini de sağlayıcı doğuruyor: çıkış yolu, yönetim portu kuralı,
şifreleme anahtarı, bakım penceresi, hesap içi erişim kuralı. Kayıt bunlar hakkında bir şey
söylemediği için bir karşılaştırma bunları hiçbir zaman fark olarak göstermez. Kaydı yazmak farkı
sıfırlamaz; farkı **kaydın kapsamı kadar** görünür kılar.

#### Kim, Ne Zaman, Ne Değiştirdi

```js
// izle.mjs — bir donemdeki degisiklikler iki ortamda ne kadar izlenebiliyor. Model.
import { kayit } from "./kur.mjs";

// Bir ceyrekteki on dort degisiklik; onceden yazilidir, rastgelelik yoktur.
const D = [["makine.toplayici.sayi", "kayit"], ["makine.toplayici.boyut", "kayit"],
  ["kuyruk.okuma-kuyrugu.bolum", "kayit"], ["veri_deposu.olcum-deposu.boyut_gb", "kayit"],
  ["makine.toplayici.erisim_kurali", "elle"], ["nesne_deposu.arsiv.yasam_gun", "kayit"],
  ["ag.olcum-agi.blok", "kayit"], ["makine.dogrulayici.sayi", "kayit"],
  ["kuyruk.okuma-kuyrugu.saklama_saat", "elle"], ["veri_deposu.olcum-deposu.yedek_gun", "kayit"],
  ["makine.dogrulayici.imaj", "kayit"], ["nesne_deposu.arsiv.sinif", "kayit"],
  ["ag.olcum-agi.cikis_yolu", "elle"], ["makine.toplayici.disk_gb", "kayit"]];
const isKaydiOlan = [0, 3, 7, 10];                    // elle kurulan ortamda bilet acilan dortu

// Kayit uzerinden gecen degisiklik uc soruyu da yanitlar; is kaydi ikisini (ne degistigini yazmaz).
const yanit = { kayit: 3, bilet: 2, izsiz: 0 };
const A = D.map(([, y]) => (y === "kayit" ? "kayit" : "izsiz"));
const B = D.map((_, i) => (isKaydiOlan.includes(i) ? "bilet" : "izsiz"));
const topla = (x) => x.reduce((t, s) => t + yanit[s], 0);
const oran = (n) => (100 * n / (D.length * 3)).toFixed(1);

console.log(`degisiklik ${D.length}   soru ${D.length * 3} (kim / ne zaman / ne degisti)`);
for (const [ad, x] of [["kayittan kurulan ortam", A], ["elle kurulan ortam", B]])
  console.log(`  ${ad.padEnd(22)}: yanitlanan ${topla(x)}/${D.length * 3}` +
    ` (%${oran(topla(x))})   izsiz degisiklik ${x.filter((s) => s === "izsiz").length}`);

const elleGecen = D.filter(([, y]) => y === "elle").map(([k]) => k);
console.log(`kayittan kurulan ortamda kaydi atlayan ${elleGecen.length} degisiklik:`);
for (const k of elleGecen)
  console.log(`  ${k.padEnd(38)} ${kayit.has(k) ? "kayitta var  -> sapma olarak gorunur"
    : "kayitta yok  -> hicbir karsilastirmada cikmaz"}`);
```

```
degisiklik 14   soru 42 (kim / ne zaman / ne degisti)
  kayittan kurulan ortam: yanitlanan 33/42 (%78.6)   izsiz degisiklik 3
  elle kurulan ortam    : yanitlanan 8/42 (%19.0)   izsiz degisiklik 10
kayittan kurulan ortamda kaydi atlayan 3 degisiklik:
  makine.toplayici.erisim_kurali         kayitta yok  -> hicbir karsilastirmada cikmaz
  kuyruk.okuma-kuyrugu.saklama_saat      kayitta var  -> sapma olarak gorunur
  ag.olcum-agi.cikis_yolu                kayitta yok  -> hicbir karsilastirmada cikmaz
```

Kırk iki sorunun otuz üçü kayıt yolunda, sekizi elle yolda yanıtlanıyor. Aradaki fark bir disiplin
farkı değil, bir **saklama yeri** farkıdır: kayıt yolunda değişiklik bir dosyada duruyor ve dosya
sürümleniyor, dolayısıyla üç sorunun üçü de aynı yerden okunuyor. İş kaydı yolunda iki soru
yanıtlanır — kim açtı, ne zaman kapandı — ama üçüncüsü, **ne değişti**, düzyazıya kalır ve
düzyazıda ölçülebilir bir değer yoktur.

Asıl bilgi son üç satırdadır. Kayıttan kurulan ortamda bile üç değişiklik kaydı atladı ve bu üçü
birbirinden farklı sonuç doğurdu. Kayıtta yazılı olan kalem elle değiştirildiğinde bir
karşılaştırma onu **sapma** olarak gösterir; kayıtta yazılı olmayan kalem elle değiştirildiğinde
hiçbir karşılaştırmada çıkmaz. İzlenebilirlik kaydın varlığından değil, **kapsamından** doğuyor:
kaydın dokunmadığı kalemde kayıt yolu ile elle yol arasında hiçbir fark yok.

#### Kaydın Bedeli

```bash
#!/usr/bin/env bash
# Kayda baglamanin bedeli: yazilan satir ve degisiklik basina ek adim.
printf 'yazilan satir   talep %s   kayit %s   ayristirici ve uygulayici %s (bir kez)\n' \
  "$(wc -l < talep.txt | tr -d ' ')" "$(wc -l < kayit.alt | tr -d ' ')" \
  "$(wc -l < kur.mjs | tr -d ' ')"
printf 'kayitta %s kalem satiri, %s kalem  -> satir basina %s kalem\n' \
  "$(grep -c '^kaynak' kayit.alt)" "20" "$(node -e 'console.log((20/6).toFixed(2))')"
printf 'degisiklik basina ek adim   elle yol 0   kayit yolu 3 (yaz, gozden gecir, plani oku)\n'
printf 'bir donemde (14 degisiklik)  elle yol 0 ek adim   kayit yolu %s ek adim\n' "$((14 * 3))"
```

```
yazilan satir   talep 12   kayit 8   ayristirici ve uygulayici 38 (bir kez)
kayitta 6 kalem satiri, 20 kalem  -> satir basina 3.33 kalem
degisiklik basina ek adim   elle yol 0   kayit yolu 3 (yaz, gozden gecir, plani oku)
bir donemde (14 degisiklik)  elle yol 0 ek adim   kayit yolu 42 ek adim
```

Kaydın kendisi talepten kısadır: sekiz satır, altısı kalem taşıyor, satır başına 3,33 kalem.
Bedel orada değil. Otuz sekiz satırlık ayrıştırıcı ve uygulayıcı bir kez yazılır ve her ortam için
yeniden kullanılır — bu bir kereliktir. Sürekli ödenen bedel son satırdadır: kayıt yolunda her
değişiklik üç ek adım ister ve bir çeyrekte bu kırk iki ek adıma çıkar. Elle yolda bu adımlar
sıfırdır; bir kişi ortama girer ve değeri değiştirir.

Kırk iki ek adım karşılığında alınan şey de sayılıdır: yirmi beş sorunun daha yanıtlanması ve
yirmi kalemin karşılaştırılabilir olması. Ama alınmayan şey aynı ölçüde nettir. Beş kalem kaydın
dışında kaldı ve kırk iki ek adım onlara dokunmuyor; üç değişiklik kaydı atladı ve ek adımlar
onları da durduramadı. Kayıt bir zorunluluk üretmez, bir **karşılaştırma zemini** üretir — o zemini
kimin kullandığı ayrı bir sorudur ve bu kursun geri kalanı büyük ölçüde onu ölçer.

#### Özet

- Aynı ortamın iki kurulumu 9 kalemde ayrıldı: 5 ayrık (talebin sayı vermediği 7 noktanın 5'i),
  1 eksik (yazılıydı, atlandı), 3 fazla (hiçbir tarifte yazılı değil).
- Talebin yedi yorum noktasının ikisi elin yorumu kayıtla aynı değere düştüğü için görünmedi;
  yorum noktası sayısı değişmedi, yalnız bu koşumda ayrılmadılar.
- Kaydın kapsamadığı 5 kalem iki kurulumda da aynı doğdu; bir karşılaştırma bunları hiçbir zaman
  fark olarak göstermez.
- Bir çeyreklik 42 sorunun 33'ü kayıt yolunda, 8'i elle yolda yanıtlandı; farkı yapan şey disiplin
  değil, değişikliğin saklandığı yer.
- Kayıt 8 satır, ayrıştırıcı ve uygulayıcı 38 satır (bir kez); sürekli bedel değişiklik başına
  3 ek adım, bir çeyrekte 42 ek adım.

#### Sonraki Adım

Kayıt bu derste bir kalem listesiydi: her satır bir kaynağın niteliklerini sayıyordu ve ortamın ne
**olması gerektiğini** yazıyordu. Bu, kayıt yazmanın tek biçimi değil. Aynı ortam bir adım listesi
olarak da yazılabilir — önce ağı aç, sonra makineleri aç, sonra kuyruğu bağla — ve o liste de bir
dosyada durur, sürümlenir, gözden geçirilir. İki biçim de kayıttır, ikisi de aynı üç ek adımı
ister.

Fark ilk koşumda görünmez. Sonraki ders aynı hedefi iki biçimde yazar ve ikisini **kısmi
başarısızlıkla** koşturur: üçüncü adımda hata çıktığında adım listesi nerede kalıyor, istenen durum
tanımı ne yapıyor; ikisi yeniden koşturulduğunda ne kadar iş üretiyor; ve hedef değiştiğinde her
birinde kaç satır yazmak gerekiyor.

### Bildirimsel ve Buyurgan Yaklaşım

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/bildirimsel-ve-buyurgan-yaklasim

Aynı dokuz kalemlik hedef iki biçimde yazılır — dört satırlık istenen durum tanımı ve dokuz satırlık adım listesi — ve üçüncü adımda hata çıkacak biçimde koşturulur. Kısmi başarısızlıktan sonra hedefe ulaşan kalem, yeniden koşumda üretilen boş iş ile hata ve hedef değiştiğinde yazılan satır sayılır.

Bir önceki ders kaydı bir kalem listesi olarak yazdı: her satır bir kaynağın ne **olması
gerektiğini** söylüyordu. Aynı ortam bir adım listesi olarak da yazılabilir — önce ağı aç, sonra
depoyu aç, sonra makineyi bağla — ve o liste de bir dosyada durur, sürümlenir, gözden geçirilir.
İki biçim de kayıttır ve ikisi de aynı ek adımları ister.

**Bildirimsel** tarif istenen durumu yazar; **buyurgan** tarif oraya götüren adımları yazar.
M22/K01 **DevOps Kültürü ve Temelleri** kursundaki `Altyapı Sağlama Modelleri` dersi elle, betikle
ve bildirimsel sağlamayı karşılaştırmış, sapmanın neden yalnız bildirimsel modelde sayılabildiğini
göstermişti; o karşılaştırma burada tekrarlanmıyor. Bu ders ölçüyü başka bir eksene taşır:
**kısmi başarısızlık**. Aynı hedef iki biçimde yazılır, üçüncü adımda hata çıkacak biçimde
koşturulur ve üç şey sayılır — hatadan sonra hedefe ulaşan kalem, aynı tarif yeniden koşturulunca
üretilen iş ve hedef değiştiğinde yazılan satır.

Ortam yine **bölgesel ölçüm ağı**dır: sayaç okumalarını toplayan, doğrulayan ve faturaya çeviren
**kurgu** yazılım. Altyapı sağlanmaz; ortam, işlemler ve kota `node` ile modellenir. Tarif
dosyaları gerçekten diske yazılır.

**AT7.** Hedef dört kaynak ve dokuz kalemdir; **kaynağın kendisi de bir kalemdir**, nitelikleri de.
**AT8.** Buyurgan tarif dokuz satırlık bir adım listesi, bildirimsel tarif dört satırlık bir
tanımdır; ikisi de aynı dokuz kalemi hedefler.
**AT9.** `yarat` var olan kaynakta hata verir; `ayarla` aynı değere yazıldığında **boş yazma**
sayılır. Etkisizliğin olup olmadığı bu iki kuralda kararlaşır.
**AT10.** İlk koşumda veri deposu kotası sıfırdır ve üçüncü adım düşer; kota ikinci koşumdan önce
bire çıkarılır. Hata önceden yazılıdır, rastgelelik yoktur.
**AT11.** Bildirimsel uygulayıcı kalemleri bağımlılık sırasına göre uygular; bağımlılığı
karşılanmayan kalem **atlanır**, hata sayılmaz.
**AT12.** Buyurgan koşucunun iki modu vardır: ilk hatada durur ya da hatayı atlayarak devam eder.

#### Aynı Hedef, İki Biçim

```bash
#!/usr/bin/env bash
# Ayni hedefin iki bicimi: bildirimsel tanim ve buyurgan adim listesi. Ikinci surumler de yazilir.
set -e

cat > hedef.alt <<'SON'
kaynak ag "olcum-agi" { blok = "10.40.0.0/16" }
kaynak veri_deposu "olcum-deposu" { boyut_gb = 200 }
kaynak makine "toplayici" { sayi = 3  bagli = "veri_deposu.olcum-deposu" }
kaynak kuyruk "okuma-kuyrugu" { bolum = 6 }
SON

cat > adimlar.txt <<'SON'
yarat ag.olcum-agi
ayarla ag.olcum-agi.blok 10.40.0.0/16
yarat veri_deposu.olcum-deposu
ayarla veri_deposu.olcum-deposu.boyut_gb 200
yarat makine.toplayici
ayarla makine.toplayici.sayi 3
yarat kuyruk.okuma-kuyrugu
ayarla kuyruk.okuma-kuyrugu.bolum 6
bagla makine.toplayici veri_deposu.olcum-deposu
SON

# Hedef degisir: toplayici 3 -> 5, kuyruk bolumu 6 -> 12, bir nesne deposu eklenir.
cat > hedef-v2.alt <<'SON'
kaynak ag "olcum-agi" { blok = "10.40.0.0/16" }
kaynak veri_deposu "olcum-deposu" { boyut_gb = 200 }
kaynak makine "toplayici" { sayi = 5  bagli = "veri_deposu.olcum-deposu" }
kaynak kuyruk "okuma-kuyrugu" { bolum = 12 }
kaynak nesne_deposu "arsiv" { sinif = "seyrek" }
SON

cat > adimlar-v2.txt <<'SON'
varsa-atla yarat ag.olcum-agi
ayarla ag.olcum-agi.blok 10.40.0.0/16
varsa-atla yarat veri_deposu.olcum-deposu
ayarla veri_deposu.olcum-deposu.boyut_gb 200
varsa-atla yarat makine.toplayici
ayarla makine.toplayici.sayi 5
varsa-atla yarat kuyruk.okuma-kuyrugu
ayarla kuyruk.okuma-kuyrugu.bolum 12
varsa-atla bagla makine.toplayici veri_deposu.olcum-deposu
varsa-atla yarat nesne_deposu.arsiv
ayarla nesne_deposu.arsiv.sinif seyrek
SON
```

Bildirimsel tanım dört satır, adım listesi dokuz satırdır ve ikisi de aynı dokuz kalemi taşır.
Aradaki asıl fark boyut değil: tanımda `bagli` bir niteliktir ve makinenin veri deposuna **bağlı
olduğunu** söyler; listede `bagla` en sondaki bir adımdır ve bağlamanın **ne zaman** yapılacağını
söyler. İlki bir bağımlılık, ikincisi bir sıra bildirir.

```js
// motor.mjs — ortam modeli ve iki kosucu; ikisi de modeldir, gercek altyapi saglanmaz.
import { readFileSync } from "node:fs";

export const yeniDunya = (kota) => ({ kaynak: new Set(), kalem: new Map(), kota,
  denenen: 0, basarili: 0, bos: 0, hata: 0, atlanan: 0 });
export const kopya = (d) => ({ ...d, kaynak: new Set(d.kaynak), kalem: new Map(d.kalem),
  denenen: 0, basarili: 0, bos: 0, hata: 0, atlanan: 0 });

export const islem = (d, ad, hedef, deger) => {           // yarat / ayarla / bagla
  d.denenen++;
  if (ad === "yarat") {
    if (d.kaynak.has(hedef)) return { ok: false, neden: "zaten var" };
    const tur = hedef.split(".")[0];
    const n = [...d.kaynak].filter((k) => k.startsWith(tur + ".")).length;
    if (n >= (d.kota[tur] ?? 9)) return { ok: false, neden: tur + " kotasi dolu" };
    d.kaynak.add(hedef); d.basarili++; return { ok: true };
  }
  const anahtar = ad === "bagla" ? hedef + ".bagli" : hedef;
  const sahip = anahtar.split(".").slice(0, 2).join(".");
  if (!d.kaynak.has(sahip)) return { ok: false, neden: "kaynak yok" };
  if (ad === "bagla" && !d.kaynak.has(deger)) return { ok: false, neden: "hedef kaynak yok" };
  if (d.kalem.get(anahtar) === deger) { d.bos++; return { ok: true, bos: true }; }
  d.kalem.set(anahtar, deger); d.basarili++; return { ok: true };
};

// Buyurgan: adim listesini sirayla kosar. mod "dur" ilk hatada durur, "gecerek" hatayi atlar.
export const buyurgan = (d, satirlar, mod) => {
  const gunluk = [];
  for (const [i, ham] of satirlar.entries()) {
    const koruma = ham.startsWith("varsa-atla ");
    const [ad, hedef, deger] = ham.replace("varsa-atla ", "").split(" ");
    if (koruma && ad === "yarat" && d.kaynak.has(hedef)) { d.atlanan++; continue; }
    if (koruma && ad === "bagla" && d.kalem.get(hedef + ".bagli") === deger) { d.atlanan++; continue; }
    const r = islem(d, ad, hedef, deger);
    if (!r.ok) { d.hata++; gunluk.push(`adim ${i + 1} (${ad} ${hedef}): ${r.neden}`);
      if (mod === "dur") return { gunluk, durulanAdim: i + 1, denenmeyen: satirlar.length - i - 1 }; }
  }
  return { gunluk, durulanAdim: 0, denenmeyen: 0 };
};

const BLOK = /^kaynak\s+(\w+)\s+"([\w-]+)"\s*\{(.*)\}$/;   // 01'deki yapilandirma dilinin ayni
export const hedefiOku = (yol) => {
  const k = new Map();
  for (const s of readFileSync(yol, "utf8").split("\n")) {
    const m = BLOK.exec(s.trim());
    if (!m) continue;
    k.set(`${m[1]}.${m[2]}`, "var");                       // kaynagin kendisi de bir kalemdir
    for (const x of m[3].matchAll(/(\w+)\s*=\s*("[^"]*"|[\w./-]+)/g))
      k.set(`${m[1]}.${m[2]}.${x[1]}`, x[2].replaceAll('"', ""));
  }
  return k;
};

// Bildirimsel: fark hesaplanir, bagimliligi karsilanmayan kalem atlanir, sira derinlige gore.
export const bildirimsel = (d, hedef) => {
  const gunluk = [];
  const fark = [...hedef].filter(([k, v]) => (k.split(".").length === 2
    ? !d.kaynak.has(k) : d.kalem.get(k) !== v));
  for (const [k, v] of fark.sort((a, b) => a[0].split(".").length - b[0].split(".").length)) {
    const gerek = [k.split(".").length > 2 ? k.split(".").slice(0, 2).join(".") : null,
      k.endsWith(".bagli") ? v : null].filter(Boolean);
    if (gerek.some((g) => !d.kaynak.has(g))) { d.atlanan++; gunluk.push(`${k}: bagimlilik yok`); continue; }
    const [ad, hedefAd] = k.split(".").length === 2 ? ["yarat", k]
      : k.endsWith(".bagli") ? ["bagla", k.split(".").slice(0, 2).join(".")] : ["ayarla", k];
    const r = islem(d, ad, hedefAd, v);
    if (!r.ok) { d.hata++; gunluk.push(`${k}: ${r.neden}`); }
  }
  return { gunluk, fark: fark.length };
};

export const rapor = (ad, d, ek = "") =>
  `  ${ad.padEnd(30)} denenen ${d.denenen}  uygulanan ${d.basarili}  bos yazma ${d.bos}` +
  `  hata ${d.hata}  atlanan ${d.atlanan}${ek}`;
```

#### Üçüncü Adımda Hata

```js
// senaryo.mjs — ayni hedef iki bicimde: kismi basarisizlik, yeniden kosum, ucuncu kosum.
import { readFileSync } from "node:fs";
import { yeniDunya, kopya, buyurgan, bildirimsel, hedefiOku, rapor } from "./motor.mjs";

const satir = (y) => readFileSync(y, "utf8").trim().split("\n");
const adimlar = satir("adimlar.txt"), hedef = hedefiOku("hedef.alt");
const dar = { veri_deposu: 0 }, genis = { veri_deposu: 1 };   // ucuncu adim dar kotada duser

console.log(`hedef ${hedef.size} kalem   adim listesi ${adimlar.length} satir\n`);
console.log("A. kismi basarisizlik (veri deposu kotasi 0)");
const bA = yeniDunya(dar), sA = buyurgan(bA, adimlar, "dur");
console.log(rapor("buyurgan (ilk hatada durur)", bA,
  `  denenmeyen adim ${sA.denenmeyen}`));
console.log(`     ${sA.gunluk.join("; ")}   nerede kalindigini yazan kayit: 0 dosya`);
const dA = yeniDunya(dar), tA = bildirimsel(dA, hedef);
console.log(rapor("bildirimsel (fark uygular)", dA, `  fark ${tA.fark}`));
console.log(`     ${tA.gunluk.join("; ")}`);
console.log(`  hedefe ulasan kalem   buyurgan ${bA.kaynak.size + bA.kalem.size}/${hedef.size}` +
  `   bildirimsel ${dA.kaynak.size + dA.kalem.size}/${hedef.size}\n`);

console.log("B. ikinci kosum (kota yukseltildi, tarif degismedi)");
const bB = kopya({ ...bA, kota: genis }), sB = buyurgan(bB, adimlar, "dur");
console.log(rapor("buyurgan (ayni liste bastan)", bB, `  denenmeyen adim ${sB.denenmeyen}`));
console.log(`     ${sB.gunluk.join("; ")}`);
const kisa = adimlar.filter((a) => a !== "yarat ag.olcum-agi");   // elle cikarilan 1 satir
const bB2 = kopya({ ...bA, kota: genis });
buyurgan(bB2, kisa, "dur");
console.log(rapor("buyurgan (1 satir cikarildi)", bB2, `  elle duzenleme 1 satir`));
const dB = kopya({ ...dA, kota: genis }), tB = bildirimsel(dB, hedef);
console.log(rapor("bildirimsel (ayni tarif)", dB, `  fark ${tB.fark}, elle duzenleme 0 satir`));
console.log(`  hedefe ulasan kalem   buyurgan ${bB2.kaynak.size + bB2.kalem.size}/${hedef.size}` +
  `   bildirimsel ${dB.kaynak.size + dB.kalem.size}/${hedef.size}\n`);

console.log("C. ucuncu kosum (hedefe ulasildi, tarif yeniden kosuluyor)");
const bC = kopya({ ...bB2, kota: genis });
buyurgan(bC, adimlar, "gecerek");
console.log(rapor("buyurgan (hatayi atlayarak)", bC));
const dC = kopya({ ...dB, kota: genis }), tC = bildirimsel(dC, hedef);
console.log(rapor("bildirimsel", dC, `  fark ${tC.fark}`));

console.log("\nD. hedef degisti (toplayici 3 -> 5, kuyruk bolumu 6 -> 12, nesne deposu eklendi)");
const hedef2 = hedefiOku("hedef-v2.alt"), adimlar2 = satir("adimlar-v2.txt");
const bD = kopya({ ...bC, kota: genis });
buyurgan(bD, adimlar2, "gecerek");
console.log(rapor("buyurgan v2 (korumali liste)", bD, `  liste ${adimlar2.length} satir`));
const dD = kopya({ ...dC, kota: genis }), tD = bildirimsel(dD, hedef2);
console.log(rapor("bildirimsel v2", dD, `  fark ${tD.fark}, tanim ${hedef2.size} kalem`));
```

```
hedef 9 kalem   adim listesi 9 satir

A. kismi basarisizlik (veri deposu kotasi 0)
  buyurgan (ilk hatada durur)    denenen 3  uygulanan 2  bos yazma 0  hata 1  atlanan 0  denenmeyen adim 6
     adim 3 (yarat veri_deposu.olcum-deposu): veri_deposu kotasi dolu   nerede kalindigini yazan kayit: 0 dosya
  bildirimsel (fark uygular)     denenen 7  uygulanan 6  bos yazma 0  hata 1  atlanan 2  fark 9
     veri_deposu.olcum-deposu: veri_deposu kotasi dolu; veri_deposu.olcum-deposu.boyut_gb: bagimlilik yok; makine.toplayici.bagli: bagimlilik yok
  hedefe ulasan kalem   buyurgan 2/9   bildirimsel 6/9

B. ikinci kosum (kota yukseltildi, tarif degismedi)
  buyurgan (ayni liste bastan)   denenen 1  uygulanan 0  bos yazma 0  hata 1  atlanan 0  denenmeyen adim 8
     adim 1 (yarat ag.olcum-agi): zaten var
  buyurgan (1 satir cikarildi)   denenen 8  uygulanan 7  bos yazma 1  hata 0  atlanan 0  elle duzenleme 1 satir
  bildirimsel (ayni tarif)       denenen 3  uygulanan 3  bos yazma 0  hata 0  atlanan 0  fark 3, elle duzenleme 0 satir
  hedefe ulasan kalem   buyurgan 9/9   bildirimsel 9/9

C. ucuncu kosum (hedefe ulasildi, tarif yeniden kosuluyor)
  buyurgan (hatayi atlayarak)    denenen 9  uygulanan 0  bos yazma 5  hata 4  atlanan 0
  bildirimsel                    denenen 0  uygulanan 0  bos yazma 0  hata 0  atlanan 0  fark 0

D. hedef degisti (toplayici 3 -> 5, kuyruk bolumu 6 -> 12, nesne deposu eklendi)
  buyurgan v2 (korumali liste)   denenen 6  uygulanan 4  bos yazma 2  hata 0  atlanan 5  liste 11 satir
  bildirimsel v2                 denenen 4  uygulanan 4  bos yazma 0  hata 0  atlanan 0  fark 4, tanim 11 kalem
```

Üçüncü adımdaki hata iki biçimde çok farklı bir maliyet üretti. Buyurgan liste durduğunda **iki
kalem** yerindeydi ve altı adım hiç denenmemişti — oysa o altı adımın dördünün veri deposuyla hiçbir
ilgisi yok. Liste bir **sıra** bildirdiği için sıradaki hata sonrasını da durdurur; makine ve kuyruk
kotadan etkilenmediği hâlde açılmadan kaldı. Bildirimsel uygulayıcı aynı hatayı aldı ve **altı
kalemi** yerine koydu; yalnız veri deposuna gerçekten bağımlı iki kalemi atladı. Aradaki dört
kalemlik fark bir uygulayıcı hızından değil, tarifin ne bildirdiğinden geliyor: bağımlılık
bildirilirse hata yalnız kendi alt ağacını durdurur, sıra bildirilirse hata kuyruğun tamamını.

Ortada bırakılan durumun **okunabilirliği** de ayrıdır. Buyurgan koşumun geriye bıraktığı tek bilgi
durduğu adım numarasıdır ve ortamın hangi kaleminin yerinde olduğunu ayrıca bakmak gerekir; nerede
kalındığını yazan bir kayıt yoktur. Bildirimsel koşum ise bir sonraki koşumda farkı yeniden
hesaplar — "nerede kalındı" sorusu sorulmaz, çünkü yanıt her koşumda baştan üretilir.

#### Yeniden Koşumda Üretilen İş

İkinci koşum aynı ayrımı başka bir yerden gösterir. Kota yükseltildikten sonra buyurgan liste
**baştan koşturulduğunda birinci adımda düşer**: ağ zaten vardır ve `yarat` var olan kaynakta hata
verir. Sekiz adım denenmez. Listeyi ilerletmek için bir satırın elle çıkarılması gerekir; bu
çıkarma ortamın o anki durumuna göre yapılır ve başka bir ortamda başka bir satır olur. Bildirimsel
tarif ise **hiç düzenlenmeden** koşar, farkı üç kalem bulur, üçünü uygular.

Üçüncü koşum etkisizliği doğrudan sayar. Hedefe ulaşılmış bir ortamda buyurgan liste hatayı
atlayarak koşturulduğunda dokuz adımın dördü hata verir ve beşi **boş yazma** üretir: aynı değer
aynı kaleme yeniden yazılır, ortam değişmez, ama dokuz işlem yine de denenmiştir. Bildirimsel tarif
aynı ortamda sıfır fark bulur ve **hiçbir işlem denemez**. Bu ikisi arasındaki fark bir verimlilik
ayrıntısı değil: dokuz denemenin dördü hata olarak görünür ve bir sonraki gerçek hatayı bu dört
hatanın arasından ayırmak gerekir.

#### Hedef Değiştiğinde Yazılan Satır

```bash
#!/usr/bin/env bash
# Hedef degistiginde yazilan satir: iki tarifin birinci ve ikinci surumu karsilastirilir.
printf 'tarif buyuklugu (satir)   bildirimsel %s -> %s   buyurgan %s -> %s\n' \
  "$(wc -l < hedef.alt | tr -d ' ')" "$(wc -l < hedef-v2.alt | tr -d ' ')" \
  "$(wc -l < adimlar.txt | tr -d ' ')" "$(wc -l < adimlar-v2.txt | tr -d ' ')"
printf 'hedef degisiminde yazilan satir   bildirimsel %s   buyurgan %s\n' \
  "$(diff hedef.alt hedef-v2.alt | grep -c '^>')" \
  "$(diff adimlar.txt adimlar-v2.txt | grep -c '^>')"
printf 'bunun %s satiri koruma eklemesi, %s satiri deger degisikligi, %s satiri yeni kaynak\n' \
  "$(diff adimlar.txt adimlar-v2.txt | grep '^> varsa-atla' | grep -vc arsiv)" \
  "$(diff adimlar.txt adimlar-v2.txt | grep '^> ayarla' | grep -vc arsiv)" \
  "$(diff adimlar.txt adimlar-v2.txt | grep '^>' | grep -c arsiv)"
```

```
tarif buyuklugu (satir)   bildirimsel 4 -> 5   buyurgan 9 -> 11
hedef degisiminde yazilan satir   bildirimsel 3   buyurgan 9
bunun 5 satiri koruma eklemesi, 2 satiri deger degisikligi, 2 satiri yeni kaynak
```

Aynı hedef değişikliği için bildirimsel tanımda üç, adım listesinde dokuz satır yazıldı. Dokuzun
yalnız dördü hedefin kendisiyle ilgili — iki değer değişikliği ve yeni kaynağın iki satırı. Kalan
**beş satır koruma**dır: her `yarat` adımının önüne "varsa atla" yazılır, yoksa liste ikinci kez
koşamaz. Bu beş satır D bölümünde işini yapıyor: koşum sıfır hatayla biter, beş adım atlanır, dört
kalem uygulanır. Bedeli iki boş yazmadır — koruma kaynağın varlığını okur, niteliğin değerini
okumaz.

Burada görülen şey bir dönüşümdür. Buyurgan listeyi yeniden koşulabilir kılmanın yolu her adımın
önüne bir **durum okuması** koymaktır; bu okumalar tamamlandığında liste artık "ne yapılacağını"
değil, "ne olması gerektiğini" söylemeye başlar. İki biçim arasındaki sınır bu noktada silinir ve
kalan tek fark, o okumaların tarifin içine elle mi yazıldığı yoksa uygulayıcıya mı bırakıldığıdır.
Bildirimsel biçimin kazancı bir üstünlük değil, bir **tekrar etmeme**dir: aynı okuma dokuz satırda
dokuz kez yazılmaz, uygulayıcıda bir kez yazılır.

#### Özet

- Üçüncü adımdaki hatadan sonra hedefe ulaşan kalem buyurgan biçimde 2/9, bildirimsel biçimde 6/9
  oldu; buyurgan koşumda denenmeyen 6 adımın 5'i hatayla ilgisizdi.
- Buyurgan koşum geriye adım numarasından başka kayıt bırakmadı; bildirimsel koşum bir sonraki
  koşumda farkı baştan hesapladığı için "nerede kalındı" sorusunu sormadı.
- İkinci koşumda buyurgan liste 1. adımda düştü ve ilerlemesi için 1 satır elle çıkarıldı;
  bildirimsel tarif 0 satır düzenlemeyle koştu ve 3 kalem uyguladı.
- Hedefe ulaşılmış ortamda buyurgan liste 9 işlem denedi (4 hata, 5 boş yazma), bildirimsel tarif
  0 işlem denedi.
- Hedef değişince bildirimsel tanımda 3, adım listesinde 9 satır yazıldı; 9'un 5'i yalnız listeyi
  yeniden koşulabilir kılan koruma satırıydı.

#### Sonraki Adım

Buraya kadar iki tarif de aynı şeyi yaptı: var olan bir kaynağı **yerinde** değiştirdi. Kuyruğun
bölüm sayısı altıdan on ikiye çıktığında kuyruk aynı kuyruktu; makinenin sayısı üçten beşe
çıktığında var olan üç makineye dokunulmadı. Bu, altyapıyı değiştirmenin tek yolu değil.

Sonraki ders aynı güncellemeyi iki yolla yapar — yerinde yama ve yeniden oluşturma — ve dört şeyi
sayar: bir dönem sonunda kaç makinenin birbirinden ayrıştığı, güncelleme süresi, geri alma turu ve
yeniden oluşturmanın **taşıyamadığı** şey. Yeniden oluşturmanın hiçbir şey kazandırmadığı durum da
aynı ölçüyle gösterilir.

### Değişmez Altyapı

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/degismez-altyapi

On iki makinelik bir filo sekiz dönem boyunca iki yolla güncellenir: yerinde yama ve imajdan yeniden oluşturma. Aynı olay dizisi iki yolda oynatılır; birbirinden ayrışan sunucu sayısı, güncelleme süresi, geri alma turu, yeniden oluşturmanın taşıyamadığı yerel durum ile bağlantı ve yeniden oluşturmanın hiçbir şey kazandırmadığı durum sayılır.

Önceki iki ders altyapıyı yerinde değiştirdi: kuyruğun bölüm sayısı arttı, makine sayısı arttı, var
olan kaynaklara dokunulmadı. Bir makinenin üzerindeki paket sürümü de aynı yolla ilerler — makine
aynı makine kalır, üzerindeki yazılım değişir. Bu **yerinde yama**dır ve her uygulaması makinenin
geçmişine bir katman ekler.

İkinci yol makineye hiç dokunmaz: yeni bir imaj hazırlanır, eski makine kapatılır, yerine imajdan
yeni bir makine doğar. **Değişmez altyapı** budur — koşan bir kaynak değiştirilmez, yerine yenisi
konur. Bu ders aynı güncellemeyi sekiz dönem boyunca iki yolla koşturur ve dört şey sayar:
birbirinden **ayrışan sunucu** sayısı, güncelleme süresi, geri alma turu ve yeniden oluşturmanın
**taşıyamadığı** şey. Sonunda yeniden oluşturmanın hiçbir şey kazandırmadığı durum da aynı ölçüyle
gösterilir.

Filo **bölgesel ölçüm ağı**nın toplayıcı makineleridir: sayaç okumalarını sahadan alıp kuyruğa
yazan **kurgu** yazılım. Makineler gerçekten kurulmaz; filo, olaylar ve süreler `node` ile
modellenir ve üreteç kendi yazılıdır.

**AT13.** Filo on iki makinedir ve sekiz dönem güncellenir. Bir makinenin **parmak izi** sürümü ile
taşıdığı izlerden oluşur; iki makine aynı parmak izini taşıyorsa ayrışmamıştır.
**AT14.** Olaylar bir kez üretilir (tohum 8140723, üreteç kendi yazılıdır) ve iki yolda **aynı
sırayla** oynatılır; aradaki fark yoldan gelir, olaydan değil.
**AT15.** Bir dönemde makine başına yamanın düşme olasılığı 0,10; bir acil düzeltme uygulanma
olasılığı 0,10'dur.
**AT16.** Yeniden oluşturma yolunda makine her dönem geçerli imajdan doğar. Dönem içinde uygulanan
acil düzeltme çift dönemlerde imaja işlenir, tek dönemlerde **kaybolur**.
**AT17.** Süreler: yerinde yama 40 saniye, yeniden oluşturma 360 saniye artı 90 saniye boşaltma;
dalga dört makinedir. Düşen yama iki kez daha denenir, düşen sağlık kontrolü bir tur daha.
**AT18.** Geri alma 3. ve 6. dönemde yapılır. Ters yama iz taşıyan makinede düşer ve her turda
kalanın yarısı düzelir.

#### Filo ve Sekiz Dönem

```js
// filo.mjs — on iki makinelik toplayici filosunun sekiz donemi. Model; uretec kendi yazilidir.
export const TOHUM = 8140723, MAKINE = 12, DONEM = 8;
const uretec = (t) => () => (t = (t * 1103515245 + 12345) % 2147483648) / 2147483648;

// Olaylar bir kez uretilir ve iki yolda ayni sirayla oynatilir; boylece fark yoldan gelir.
const r = uretec(TOHUM);
export const olay = [];
for (let d = 1; d <= DONEM; d++)
  for (let m = 0; m < MAKINE; m++)
    olay.push({ d, m, yamaDuser: r() < 0.10, acil: r() < 0.10 });
const donemin = (d) => olay.filter((o) => o.d === d);
const imza = (x) => `${x.surum}|${[...x.iz].sort().join(",")}`;
export const ayrisan = (s) => new Set(s.map(imza)).size;
const bosFilo = () => Array.from({ length: MAKINE }, () => ({ surum: "1.0.0", iz: new Set() }));

export const yolYerinde = () => {                        // yerinde yama: iz birikir, silinmez
  const s = bosFilo(), dizi = [];
  let dusen = 0, acil = 0;
  for (let d = 1; d <= DONEM; d++) {
    for (const o of donemin(d)) {
      if (o.yamaDuser) { s[o.m].iz.add(`yarim-yama-${o.d}`); dusen++; }
      else s[o.m].surum = `1.${d}.0`;
      if (o.acil) { s[o.m].iz.add(`acil-${d}`); acil++; }
    }
    dizi.push(ayrisan(s));
  }
  return { s, dizi, dusen, acil, kaybolan: 0 };
};

export const yolYeniden = () => {                        // yeniden olusturma: her donem imajdan
  const s = bosFilo(), dizi = [], imajIz = new Set();
  let dusen = 0, acil = 0, kaybolan = 0;
  for (let d = 1; d <= DONEM; d++) {
    for (let m = 0; m < MAKINE; m++) s[m] = { surum: `1.${d}.0`, iz: new Set(imajIz) };
    const islenecek = [];
    for (const o of donemin(d)) {
      if (o.yamaDuser) dusen++;                          // saglik kontrolu duser: bir tur daha
      if (o.acil) { acil++; s[o.m].iz.add(`acil-${d}`);  // donem icinde tek makineye uygulanir
        if (d % 2 === 0) islenecek.push(`acil-${d}`); else kaybolan++; }
    }
    dizi.push(ayrisan(s));
    for (const x of islenecek) imajIz.add(x);            // cift donemde duzeltme imaja islenir
  }
  return { s, dizi, dusen, acil, kaybolan };
};
```

#### Ölçüm

```js
// olc.mjs — ayrisma, guncelleme suresi, geri alma turu ve yeniden olusturmanin tasiyamadigi.
import { TOHUM, MAKINE, DONEM, olay, ayrisan, yolYerinde, yolYeniden } from "./filo.mjs";

const A = yolYerinde(), B = yolYeniden();
console.log(`tohum ${TOHUM}   makine ${MAKINE}   donem ${DONEM}   olay ${olay.length}`);
console.log(`donem donem ayrisan sunucu (tekil parmak izi)`);
console.log(`  yerinde yama       ${A.dizi.join("  ")}   -> son ${ayrisan(A.s)}/${MAKINE}`);
console.log(`  yeniden olusturma  ${B.dizi.join("  ")}   -> son ${ayrisan(B.s)}/${MAKINE}`);
console.log(`  dusen yama/saglik ${A.dusen}   acil duzeltme ${A.acil}` +
  `   imaja islenmeyip kaybolan ${B.kaybolan}\n`);

// Sureler saniyedir; dalga 4 makinedir. Yerinde yama 40 sn, yeniden olusturma 360 sn + 90 sn bosaltma.
const DALGA = Math.ceil(MAKINE / 4), YAMA = 40, KUR = 360, BOSALT = 90;
const donemDusen = (d) => olay.filter((o) => o.d === d && o.yamaDuser).length;
let sA = 0, sB = 0;
for (let d = 1; d <= DONEM; d++) {
  sA += DALGA * YAMA + donemDusen(d) * 2 * YAMA;          // dusen yama iki kez daha denenir
  sB += DALGA * (BOSALT + KUR) + donemDusen(d) * KUR;     // dusen saglik kontrolu bir tur daha
}
console.log(`guncelleme suresi (${DONEM} donem toplami, saniye)`);
console.log(`  yerinde yama       ${sA}   donem ortalamasi ${(sA / DONEM).toFixed(0)}`);
console.log(`  yeniden olusturma  ${sB}   donem ortalamasi ${(sB / DONEM).toFixed(0)}` +
  `   oran ${(sB / sA).toFixed(1)} kat\n`);

// Geri alma: 3. ve 6. donemin guncellemesi geri alinir. Ters yama, iz tasiyan makinede duser.
const geriAl = (s) => {
  const izli = s.filter((x) => x.iz.size).length;
  let tur = 1, kalan = izli;
  while (kalan > 0 && tur < 3) { kalan = Math.floor(kalan / 2); tur++; }
  return { tur: kalan > 0 ? tur + 1 : tur, izli, elle: kalan };
};
const g = geriAl(A.s);
console.log(`geri alma (3. ve 6. donem, iki kez)`);
console.log(`  yerinde yama       tur ${g.tur} x 2 = ${g.tur * 2}   ters yamanin dustugu makine` +
  ` ${g.izli}   elle onarim kalan ${g.elle}`);
console.log(`  yeniden olusturma  tur 1 x 2 = 2   onceki imaj etiketi secilir, filo ayni` +
  ` ${DALGA * (BOSALT + KUR)} saniyede doner\n`);

// Yeniden olusturmanin tasiyamadigi: yerel durum, acik baglanti, sicak onbellek.
const TAMPON = 1800, ISLEM = 20, BAGLANTI = 40, ISINMA = 720, OKUMA = 15, KAT = 3.4;
console.log(`yeniden olusturmanin tasiyamadigi (donem basina, ${MAKINE} makine)`);
console.log(`  kuyruk tamponu     bosaltma yoksa dusen okuma ${MAKINE * TAMPON}` +
  `   bosaltma varsa ek sure ${DALGA * BOSALT} sn (tampon ${TAMPON / ISLEM} sn'de biter)`);
console.log(`  acik baglanti      kopan ${MAKINE * BAGLANTI}   yerinde yamada 0`);
console.log(`  sicak onbellek     isinma ${ISINMA} sn, veri deposu okumasi ${KAT} kat` +
  `   ek okuma ${MAKINE * ISINMA * OKUMA * (KAT - 1)}\n`);

// Yeniden olusturmanin kazandirmadigi durum: tek makineli, durum tasiyan veri deposu.
const VERI_GB = 400, HIZ_MBS = 180;
const tasima = Math.round(VERI_GB * 1024 / HIZ_MBS);
console.log(`durum tasiyan tek makine (olcum deposu, ${VERI_GB} GB yerel veri)`);
console.log(`  yerinde yama ${YAMA} sn   yeniden olusturma ${tasima} sn` +
  ` (${(tasima / 60).toFixed(1)} dk, veri kopyalama ${HIZ_MBS} MB/sn)`);
console.log(`  ayrisan sunucu 1/1 -> 1/1   kazanc 0   bedel ${(tasima / YAMA).toFixed(0)} kat sure`);
```

```
tohum 8140723   makine 12   donem 8   olay 96
donem donem ayrisan sunucu (tekil parmak izi)
  yerinde yama       3  6  6  6  7  7  8  10   -> son 10/12
  yeniden olusturma  2  2  1  1  1  2  2  2   -> son 2/12
  dusen yama/saglik 7   acil duzeltme 9   imaja islenmeyip kaybolan 3

guncelleme suresi (8 donem toplami, saniye)
  yerinde yama       1520   donem ortalamasi 190
  yeniden olusturma  13320   donem ortalamasi 1665   oran 8.8 kat

geri alma (3. ve 6. donem, iki kez)
  yerinde yama       tur 4 x 2 = 8   ters yamanin dustugu makine 10   elle onarim kalan 2
  yeniden olusturma  tur 1 x 2 = 2   onceki imaj etiketi secilir, filo ayni 1350 saniyede doner

yeniden olusturmanin tasiyamadigi (donem basina, 12 makine)
  kuyruk tamponu     bosaltma yoksa dusen okuma 21600   bosaltma varsa ek sure 270 sn (tampon 90 sn'de biter)
  acik baglanti      kopan 480   yerinde yamada 0
  sicak onbellek     isinma 720 sn, veri deposu okumasi 3.4 kat   ek okuma 311040

durum tasiyan tek makine (olcum deposu, 400 GB yerel veri)
  yerinde yama 40 sn   yeniden olusturma 2276 sn (37.9 dk, veri kopyalama 180 MB/sn)
  ayrisan sunucu 1/1 -> 1/1   kazanc 0   bedel 57 kat sure
```

#### Ayrışan Sunucu ve Geri Alma

Aynı doksan altı olay iki yolda oynatıldı ve sekiz dönem sonunda yerinde yama yolunda on iki
makineden **onu tekil** hâle geldi: on farklı parmak izi, yani ortalama 1,2 makinelik gruplar. Dizi
birikimi gösteriyor — üçten ona, hiç geri dönmeden. Yeniden oluşturma yolunda dizi bir ile iki
arasında salınıyor ve hiç birikmiyor. Aradaki fark bir dikkat farkı değil: iki yolda da yedi yama
düştü, dokuz acil düzeltme uygulandı. Yerinde yamada bunların izi makinede **kalır**; yeniden
oluşturmada bir sonraki dönem makine sıfırdan doğduğu için iz **silinir**.

Silinmenin bir bedeli var ve o da sayılı: dokuz acil düzeltmenin üçü imaja geri işlenmedi ve
kayboldu. Yeniden oluşturma, düzeltmenin imaja geri yazılmasını **zorunlu** kılar; yazılmazsa
düzeltme bir sonraki dönemde yok olur ve giderdiği sorun geri gelir. Yerinde yamada böyle bir
zorunluluk yoktur — düzeltme kalır. Ayrışmayı üreten şey tam olarak budur: kalıcılık.

Ayrışmanın maliyeti dizinin kendisinde görünmez; sonraki bir işlemde görünür. On tekil parmak izi
demek, bir sonraki değişikliğin on ayrı başlangıç durumunda denenmesi demektir. Bir yamanın bir
makinede çalışıp ötekinde çalışmaması artık bir olasılık değil, sekiz dönemin biriktirdiği
sonuçtur. Yeniden oluşturma yolunda aynı değişiklik en çok iki başlangıç durumunda denenir ve
ikisinden biri dönem içinde uygulanmış tek bir acil düzeltmedir — dönem kapandığında o da yok olur.

Süre tablosu ters yönde çalışıyor. Yeniden oluşturma sekiz dönemde 13.320 saniye, yerinde yama
1.520 saniye — **8,8 kat**. Dönem başına 190 saniye ile 1.665 saniye arasındaki fark, bir bakım
penceresinin boyutunu belirleyecek büyüklükte. Geri almada işaret değişiyor: yerinde yama iki geri
almada toplam sekiz tur harcadı, çünkü ters yama iz taşıyan on makinede düştü ve iki makine sonunda
elle onarıldı. Yeniden oluşturma iki turda bitti — önceki imaj etiketi seçilir, filo 1.350 saniyede
o etikete döner. Geri almanın **tur sayısı** ile **süresi** burada birbirinden ayrılıyor: yeniden
oluşturmanın turu tektir ve öngörülebilir, yerinde yamanınki ortamın biriktirdiği ize bağlıdır ve
önceden bilinemez.

#### Yeniden Oluşturmanın Taşıyamadığı

Üçüncü tablo yeniden oluşturmanın bedelinin süreden ibaret olmadığını gösteriyor. Makine
kapatıldığında üzerinde taşıdığı üç şey vardır ve hiçbiri imajın parçası değildir. Kuyruk tamponunda
bekleyen okumalar: boşaltma yapılmazsa dönem başına 21.600 okuma düşer; boşaltma yapılırsa hiç
düşmez ama her dalgaya doksan saniye eklenir. Açık bağlantılar: sahadan gelen dört yüz seksen uzun
ömürlü bağlantı her dönemde kopar ve yeniden kurulur; yerinde yamada bu sayı sıfırdır. Sıcak
önbellek: yeni doğan makinenin önbelleği boştur, on iki dakikalık ısınma boyunca veri deposu
okuması 3,4 katına çıkar ve dönem başına 311.040 ek okuma doğar.

Üçünün de bir azaltma yolu var ve üçü de bedelini başka bir yere yazıyor. Boşaltma tamponu kurtarır
ve süreyi uzatır. Bağlantıların kopması, sahadaki birimin yeniden bağlanmayı beklemesi ve okumayı
yerelde tutmasıyla kayıpsız hâle gelir — ama bu, saha birimine bir tampon eklemek demektir.
Önbelleğin ısınması, makinenin trafiğe alınmadan önce ısıtılmasıyla gizlenir; o da dalga başına
ek süredir. Ölçü değişmiyor, yalnızca sütun değiştiriyor.

Bu üç kalemin ortak yanı, hiçbirinin **kayıtta yazılı olmaması**dır. İmaj sürümü, paket listesi ve
ayarlar kayda girer; tamponun doluluğu, bağlantı sayısı ve önbelleğin sıcaklığı girmez. Yeniden
oluşturma kayıtta yazılı olanı bire bir yeniden üretir ve kayıtta yazılı olmayanı **her seferinde
sıfırlar**. Ayrışmayı önleyen mekanizma ile yerel durumu yok eden mekanizma aynı mekanizmadır;
biri ötekinden ayrılamaz.

#### Kazanç Sağlamadığı Durum

Son tablo yeniden oluşturmanın işe yaramadığı yeri gösteriyor. Ölçüm deposu tek makinedir ve
üzerinde dört yüz gigabayt yerel veri taşır. Tek makine olduğu için ayrışacak bir şey yoktur:
parmak izi sayısı yeniden oluşturmadan önce de sonra da birdir. Kazanç sıfırdır. Bedel ise
kopyalama süresidir — 2.276 saniye, yerinde yamanın **elli yedi katı** — ve bu sürenin tamamı veri
taşımaya gider, yazılım güncellemeye değil.

Aynı hesap ölçüm ağının öteki bileşenlerinde başka sonuç verir. Doğrulayıcı makineler durum
taşımaz ve dört tanedir; orada yeniden oluşturma toplayıcılardakine yakın bir kazanç üretir. Kuyruk
ile nesne deposu zaten sağlayıcının işlettiği kaynaklardır ve üzerlerinde yama uygulanacak bir
işletim ortamı yoktur; bu iki bileşende sorunun kendisi sorulmaz. Karar, filoyu tek parça
görmekten değil, **rol rol** ayırmaktan çıkıyor.

Buradan çıkan ölçüt tek cümledir: yeniden oluşturmanın kazancı, aynı rolü paylaşan makine sayısıyla
ve o makinelerde biriken izle orantılıdır. On iki toplayıcıda kazanç ondan bire iner; tek bir
durum taşıyan makinede kazanç yoktur. Değişmez altyapı bir ilke olarak değil, **rol rol** karar
verilir ve kararın girdisi bu iki sayıdır.

#### Özet

- Aynı 96 olay iki yolda oynatıldığında yerinde yama 8 dönem sonunda 12 makinenin 10'unu tekil
  hâle getirdi (dizi 3'ten 10'a); yeniden oluşturmada dizi 1 ile 2 arasında kaldı.
- İki yolda da 7 yama düştü ve 9 acil düzeltme uygulandı; farkı üreten şey izin kalıcılığıydı.
- Yeniden oluşturma 9 acil düzeltmenin 3'ünü kaybetti; düzeltmenin imaja geri işlenmesi zorunluluk
  hâline geliyor.
- Güncelleme süresi yeniden oluşturmada 8,8 kat uzun (dönem başına 1.665'e karşı 190 saniye), ama
  geri alma 8 tur yerine 2 turda bitti ve turun sayısı önceden biliniyor.
- Yeniden oluşturmanın taşıyamadığı üç kalem dönem başına 21.600 düşen okuma, 480 kopan bağlantı ve
  311.040 ek veri deposu okumasıdır; durum taşıyan tek makinede kazanç 0, bedel 57 kat süredir.

#### Sonraki Adım

Bu derste iki iş birbirine karıştı. İmajın hangi paketleri taşıdığı, makinenin üzerindeki ayarların
ne olduğu bir iştir; makinenin kaç tane olduğu, hangi ağa bağlandığı, hangi diski taşıdığı başka bir
iştir. Yerinde yama birincisine, yeniden oluşturma ikincisine dokunur — ve acil düzeltmenin
kaybolması tam olarak bu iki işin sınırında oldu.

Sonraki ders bu sınırı çizer. Bir ortamın kalemleri iki katmana bölünür — kaynağı yaratan katman ve
kaynağın içini ayarlayan katman — ve sınırın nereye çizildiği sayılır: kaç kalem her iki katmanda da
tanımlı, kaç kalem hiçbirinde, sınır kaydığında kaç kalem iki kez uygulanıyor ve iki katman aynı
kaleme yazdığında hangisi kazanıyor.

### Sağlama ve Yapılandırma Yönetimi

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/saglama-ve-yapilandirma-yonetimi

Bir ortamın otuz dört kalemi iki katmana bölünür — kaynağı yaratan sağlama katmanı ve kaynağın içini ayarlayan yapılandırma katmanı — ve sınırın nereye çizildiği sayılır. Çakışan kalem, hiçbir katmanda olmayan boşluk, çelişen değerde hangi katmanın kazandığı, yeniden sağlamadan sonraki sapma penceresi ve üç ayrı sınır çiziminin bedeli ölçülür.

Önceki ders iki işi birbirine karıştırdı. İmajın hangi paketleri taşıdığı ve makinenin üzerindeki
ayarların ne olduğu bir iştir; makinenin kaç tane olduğu, hangi ağa bağlandığı ve hangi diski
taşıdığı başka bir iştir. Acil düzeltmenin kaybolması tam olarak bu iki işin sınırında oldu.

**Sağlama** kaynağı yaratır: makineyi açar, ağı kurar, diski bağlar, kuyruğu oluşturur.
**Yapılandırma yönetimi** kaynağın içini ayarlar: paket kurar, dosya yazar, servisi başlatır.
İkisinin de bir kaydı vardır ve iki kayıt farklı biçimde yazılır. Bu ders bir ortamın kalemlerini
iki katmana böler ve **sınırın nereye çizildiğini** sayar: kaç kalem her iki katmanda da tanımlı,
kaç kalem hiçbirinde, çelişen bir kalemde hangi katman kazanıyor ve sınır kaydığında kaç kalem iki
kez uygulanıyor.

Ortam yine **bölgesel ölçüm ağı**nın hazırlık ortamıdır ve **kurgu**dur. Kayıt dosyaları gerçekten
diske yazılır; katmanlara bölme, uygulama sırası ve sapma penceresi `node` ile modellenir.

**AT19.** Hazırlık ortamı otuz dört kalemle temsil edilir ve her kalemin **doğal katmanı**
envanterde yazılıdır: `kaynak` ya da `icerik`.
**AT20.** İki katmanın kaydı iki ayrı biçimdedir — sağlama kaydı yapılandırma dilinde bloklar
hâlinde, ayar kaydı yapılandırma ajanının satır biçiminde.
**AT21.** Bir kalem tek katmanda tanımlıysa o katman onu uygular; iki katmanda da tanımlıysa **son
koşan** kazanır.
**AT22.** Olağan sıra sağlama sonra ayardır. Bir yeniden sağlama tetiklendiğinde sıra tersine
döner: ayar önce koşmuştur, sağlama sonra.
**AT23.** Yapılandırma ajanı otuz dakikada bir koşar; ortamda on dört makine vardır.
**AT24.** Bir çeyrekte yirmi iki değişiklik olur — yedisi kaynak kalemine, on beşi içerik
kalemine; içerik değişikliklerinin altısı imaja giren kalemlerdedir.

#### Envanter ve İki Kaydın Biçimi

```bash
#!/usr/bin/env bash
# Bir ortamin kalem envanteri ve iki katmanin kaydi: saglama (kaynak yaratma), ayar (kaynak ici).
set -e

cat > envanter.txt <<'SON'
ag.olcum-agi.blok                            kaynak
ag.olcum-agi.alt_ag_sayisi                   kaynak
ag.olcum-agi.cikis_yolu                      kaynak
makine.toplayici.sayi                        kaynak
makine.toplayici.boyut                       kaynak
makine.toplayici.disk_gb                     kaynak
makine.toplayici.imaj                        kaynak
makine.dogrulayici.sayi                      kaynak
makine.dogrulayici.boyut                     kaynak
kuyruk.okuma.bolum                           kaynak
kuyruk.okuma.saklama_saat                    kaynak
veri_deposu.olcum.boyut_gb                   kaynak
veri_deposu.olcum.yedek_gun                  kaynak
nesne_deposu.arsiv.sinif                     kaynak
nesne_deposu.arsiv.yasam_gun                 kaynak
guvenlik.toplayici.giris                     kaynak
guvenlik.toplayici.cikis                     kaynak
ad_kaydi.toplayici.ic                        kaynak
makine.toplayici.paket.cozumleyici           icerik
makine.toplayici.paket.kural_paketi          icerik
makine.toplayici.ayar.kutuk_duzeyi           icerik
makine.toplayici.ayar.es_zamanli_isci        icerik
makine.toplayici.ayar.toplu_is_saati         icerik
makine.toplayici.dizin.veri                  icerik
makine.toplayici.dizin.kutuk                 icerik
makine.toplayici.servis.otomatik_baslat      icerik
makine.toplayici.saat_dilimi                 icerik
makine.toplayici.kutuk_dondurme              icerik
makine.dogrulayici.paket.cozumleyici         icerik
makine.dogrulayici.ayar.kutuk_duzeyi         icerik
makine.dogrulayici.servis.otomatik_baslat    icerik
veri_deposu.olcum.kullanici.rapor            icerik
veri_deposu.olcum.parametre.baglanti_sayisi  icerik
nesne_deposu.arsiv.erisim_politikasi         icerik
SON

cat > saglama.alt <<'SON'
kaynak ag "olcum-agi" { blok = "10.40.0.0/16"  alt_ag_sayisi = 3 }
kaynak makine "toplayici" { sayi = 3  boyut = "orta"  disk_gb = 40  imaj = "taban-7" }
kaynak makine "toplayici" { ayar.kutuk_duzeyi = "ozet"  saat_dilimi = "UTC" }
kaynak makine "toplayici" { servis.otomatik_baslat = "acik"  paket.cozumleyici = "3.4.0" }
kaynak makine "dogrulayici" { sayi = 2  boyut = "kucuk" }
kaynak kuyruk "okuma" { bolum = 6  saklama_saat = 72 }
kaynak veri_deposu "olcum" { boyut_gb = 200  yedek_gun = 14 }
kaynak nesne_deposu "arsiv" { sinif = "seyrek"  yasam_gun = 365  erisim_politikasi = "hesap-ici" }
kaynak guvenlik "toplayici" { giris = "10.40.0.0/16" }
SON

cat > ayar.kayit <<'SON'
makine/toplayici    paket.cozumleyici = 3.5.0
makine/toplayici    paket.kural_paketi = 9.1.0
makine/toplayici    ayar.kutuk_duzeyi = ozet
makine/toplayici    ayar.es_zamanli_isci = 8
makine/toplayici    ayar.toplu_is_saati = 02:30
makine/toplayici    dizin.veri = /veri/olcum
makine/toplayici    dizin.kutuk = /kutuk/toplayici
makine/toplayici    servis.otomatik_baslat = acik
makine/toplayici    saat_dilimi = UTC
makine/toplayici    disk_gb = 40
makine/dogrulayici  paket.cozumleyici = 3.5.0
makine/dogrulayici  ayar.kutuk_duzeyi = ayrinti
makine/dogrulayici  servis.otomatik_baslat = acik
veri_deposu/olcum   kullanici.rapor = salt-okur
nesne_deposu/arsiv  erisim_politikasi = hesap-ici-artiokuma
SON
```

Envanter ortamda **gerçekten var olan** kalemleri sayar; kayıtların ne söylediğinden bağımsızdır.
İki kayıt bunun üzerine düşer. Sağlama kaydı bloklar hâlinde yazılır ve bir bloğun içinde hem
kaynağın kabuğuna ait nitelikler (`sayi`, `boyut`, `disk_gb`) hem de kaynağın içine ait olanlar
(`paket.cozumleyici`, `saat_dilimi`) bulunabilir. Ayar kaydı satır biçimindedir ve yalnız kaynağın
içine yazar. Sınır ikisinin arasında değil, **ikisinin kesişiminde** duruyor.

#### Sınır Nereye Düşüyor

```js
// katman.mjs — envanteri iki katmanin kaydiyla esler; cakismayi ve boslugu sayar. Model.
import { readFileSync } from "node:fs";

const BLOK = /^kaynak\s+(\w+)\s+"([\w-]+)"\s*\{(.*)\}$/;
export const saglama = new Map();
for (const s of readFileSync("saglama.alt", "utf8").split("\n")) {
  const m = BLOK.exec(s.trim());
  if (m) for (const x of m[3].matchAll(/([\w.]+)\s*=\s*("[^"]*"|[\w./:-]+)/g))
    saglama.set(`${m[1]}.${m[2]}.${x[1]}`, x[2].replaceAll('"', ""));
}
export const ayar = new Map();                      // yapilandirma ajaninin kaydi: baska bir bicim
for (const s of readFileSync("ayar.kayit", "utf8").split("\n")) {
  const m = /^(\w+)\/([\w-]+)\s+([\w.]+)\s*=\s*(\S+)$/.exec(s.trim());
  if (m) ayar.set(`${m[1]}.${m[2]}.${m[3]}`, m[4]);
}
export const envanter = readFileSync("envanter.txt", "utf8").trim().split("\n")
  .map((s) => s.trim().split(/\s+/)).map(([k, dogal]) => ({ k, dogal }));

export const bol = (kalem, S, A) => {               // her kalem tam bir kutuya duser
  const g = { yalnizSaglama: [], yalnizAyar: [], cakisma: [], bosluk: [] };
  for (const { k } of kalem) {
    const s = S.has(k), a = A.has(k);
    g[s && a ? "cakisma" : s ? "yalnizSaglama" : a ? "yalnizAyar" : "bosluk"].push(k);
  }
  return g;
};

if (process.argv[1].endsWith("katman.mjs")) {       // dogrudan kosarsa yazar, ice aktarilirsa yazmaz
  const g = bol(envanter, saglama, ayar);
  console.log(`envanter ${envanter.length} kalem   saglama kaydi ${saglama.size}` +
    `   ayar kaydi ${ayar.size}`);
  console.log(`  yalniz saglama ${g.yalnizSaglama.length}   yalniz ayar ${g.yalnizAyar.length}` +
    `   cakisma ${g.cakisma.length}   bosluk ${g.bosluk.length}`);
  console.log(`cakisan ${g.cakisma.length} kalem (iki katmanda da tanimli):`);
  for (const k of g.cakisma)
    console.log(`  ${k.padEnd(40)} saglama "${saglama.get(k)}"   ayar "${ayar.get(k)}"` +
      `   ${saglama.get(k) === ayar.get(k) ? "ayni" : "CELISKI"}`);
  console.log(`bosluktaki ${g.bosluk.length} kalem (hicbir katmanda yok):`);
  for (const k of g.bosluk)
    console.log(`  ${k.padEnd(44)} dogal katman: ${envanter.find((e) => e.k === k).dogal}`);
}
```

```
envanter 34 kalem   saglama kaydi 20   ayar kaydi 15
  yalniz saglama 14   yalniz ayar 9   cakisma 6   bosluk 5
cakisan 6 kalem (iki katmanda da tanimli):
  makine.toplayici.disk_gb                 saglama "40"   ayar "40"   ayni
  makine.toplayici.paket.cozumleyici       saglama "3.4.0"   ayar "3.5.0"   CELISKI
  makine.toplayici.ayar.kutuk_duzeyi       saglama "ozet"   ayar "ozet"   ayni
  makine.toplayici.servis.otomatik_baslat  saglama "acik"   ayar "acik"   ayni
  makine.toplayici.saat_dilimi             saglama "UTC"   ayar "UTC"   ayni
  nesne_deposu.arsiv.erisim_politikasi     saglama "hesap-ici"   ayar "hesap-ici-artiokuma"   CELISKI
bosluktaki 5 kalem (hicbir katmanda yok):
  ag.olcum-agi.cikis_yolu                      dogal katman: kaynak
  guvenlik.toplayici.cikis                     dogal katman: kaynak
  ad_kaydi.toplayici.ic                        dogal katman: kaynak
  makine.toplayici.kutuk_dondurme              dogal katman: icerik
  veri_deposu.olcum.parametre.baglanti_sayisi  dogal katman: icerik
```

Otuz dört kalem dört kutuya bölündü: on dördü yalnız sağlamada, dokuzu yalnız ayarda, altısı
ikisinde birden, beşi hiçbirinde. İki kayıt toplam otuz beş kalem tanımlıyor ama örttükleri kalem
sayısı yirmi dokuz — aradaki altı fark **çakışma**dır ve ikinci kez yazılan tanım hiçbir kalem
kazandırmaz.

Çakışan altı kalemin dördü aynı değeri taşıyor. Bu dördü zararsız görünür ve zararsız oldukları
tek koşul, birinin değiştiğinde ötekinin de değiştirilmesidir; ikisi ayrı dosyada, ayrı biçimde ve
çoğu zaman ayrı kişiler tarafından yazıldığı için bu koşul kendiliğinden sağlanmaz. Kalan iki kalem
zaten **çelişkili**: çözümleyici sürümü bir kayıtta 3.4.0, ötekinde 3.5.0; arşivin erişim
politikası bir kayıtta dar, ötekinde geniş.

Çakışmanın nereden doğduğu da okunabilir. Altı kalemin beşi makine kaynağının üzerindedir ve
beşinin de bir imaj karşılığı vardır: sağlama katmanı makineyi bir imajdan açtığı için o imajın
taşıdığı paket sürümünü, saat dilimini ve servis durumunu **dolaylı olarak** tanımlamış olur. Ayar
katmanı aynı değerleri doğrudan tanımlar. İki kayıt aynı kaleme iki farklı yoldan ulaşıyor ve
ikisinin de yazdığından haberi yok; çakışma bir dikkatsizlik değil, imajın kayıtta görünmeyen bir
tanım taşımasıdır.

Boşluktaki beş kalem 01'in son satırındaki ölçünün aynısıdır ve burada doğal katmanlarıyla birlikte
görünüyor: üçünün doğal katmanı kaynak, ikisinin içerik. Bunlar kayıt yazılmadığı için değil, iki
kaydın **arasına düştüğü** için yazısız kaldı — her katmanın sahibi ötekinin yazdığını varsaydı.
Çıkış yolu, güvenlik kuralının çıkış yönü, ad kaydı, kütük döndürme ve bağlantı sayısı: beşi de
kimsenin sahiplenmediği kalemler.

#### Hangi Katman Kazanıyor

```js
// sinir.mjs — uygulama sirasi, yeniden saglama sonrasi sapma penceresi ve uc sinir cizimi.
import { saglama, ayar, envanter, bol } from "./katman.mjs";

const g = bol(envanter, saglama, ayar);
const celisen = g.cakisma.filter((k) => saglama.get(k) !== ayar.get(k));
console.log(`cakisan kalemde hangi katman kazaniyor (son yazan kazanir)`);
for (const [ad, sira] of [["saglama sonra ayar (olagan)", ["saglama", "ayar"]],
  ["ayar sonra yeniden saglama", ["ayar", "saglama"]]]) {
  const son = (k) => (sira[1] === "ayar" ? ayar : saglama).get(k);
  console.log(`  ${ad.padEnd(28)} celisen ${celisen.length} kalem -> ` +
    celisen.map((k) => `${k.split(".").pop()}="${son(k)}"`).join("  "));
}

// Yeniden saglama: makine imajdan yeniden dogar, ayar katmaninin yazdigi kalem imaja doner.
const MAKINE = 14, AJAN_DK = 30;
const makineKalem = [...ayar.keys()].filter((k) => k.startsWith("makine."));
const donen = makineKalem.filter((k) => (saglama.get(k) ?? "varsayilan") !== ayar.get(k));
console.log(`\nyeniden saglama sonrasi sapma penceresi (ajan ${AJAN_DK} dakikada bir kosar)`);
console.log(`  makinede ayar katmaninin yazdigi kalem ${makineKalem.length}` +
  `   yeniden saglamadan sonra yanlis degerde ${donen.length}`);
console.log(`  ${MAKINE} makine icin ${donen.length * MAKINE} kalem` +
  `   ortalama ${AJAN_DK / 2} dk, en kotu ${AJAN_DK} dk` +
  `   -> ${donen.length * MAKINE * AJAN_DK / 2} kalem-dakika sapma`);

// Uc sinir cizimi. Imaja giren kalem: paket, servis ve saat dilimi.
const imajaGirer = (k) => /paket\.|servis\.|saat_dilimi/.test(k);
const cizim = {
  "C1 sinir kaynagin kabugunda": (k) => [saglama.has(k), ayar.has(k)],
  "C2 sinir imaja kaydirildi": (k) => [saglama.has(k) || (ayar.has(k) && imajaGirer(k)),
    ayar.has(k) && !imajaGirer(k)],
  "C3 tek katman (hepsi saglamada)": (k) => [saglama.has(k) || ayar.has(k), false],
};
const AGIR = { "C1 sinir kaynagin kabugunda": 0, "C2 sinir imaja kaydirildi": 6,
  "C3 tek katman (hepsi saglamada)": 15 };
console.log(`\nsinir nereye cizilirse ne oluyor (ceyrekte 22 degisiklik: 7 kaynak, 15 icerik)`);
for (const [ad, f] of Object.entries(cizim)) {
  const S = new Map(), A = new Map();
  for (const { k } of envanter) { const [s, a] = f(k); if (s) S.set(k, 1); if (a) A.set(k, 1); }
  const b = bol(envanter, S, A);
  console.log(`  ${ad.padEnd(32)} saglama ${S.size}  ayar ${A.size}` +
    `  cakisma ${b.cakisma.length}  bosluk ${b.bosluk.length}` +
    `  iki kez uygulanan ${b.cakisma.length}  agir yola dusen degisiklik ${AGIR[ad]}`);
}
```

```
cakisan kalemde hangi katman kazaniyor (son yazan kazanir)
  saglama sonra ayar (olagan)  celisen 2 kalem -> cozumleyici="3.5.0"  erisim_politikasi="hesap-ici-artiokuma"
  ayar sonra yeniden saglama   celisen 2 kalem -> cozumleyici="3.4.0"  erisim_politikasi="hesap-ici"

yeniden saglama sonrasi sapma penceresi (ajan 30 dakikada bir kosar)
  makinede ayar katmaninin yazdigi kalem 13   yeniden saglamadan sonra yanlis degerde 9
  14 makine icin 126 kalem   ortalama 15 dk, en kotu 30 dk   -> 1890 kalem-dakika sapma

sinir nereye cizilirse ne oluyor (ceyrekte 22 degisiklik: 7 kaynak, 15 icerik)
  C1 sinir kaynagin kabugunda      saglama 20  ayar 15  cakisma 6  bosluk 5  iki kez uygulanan 6  agir yola dusen degisiklik 0
  C2 sinir imaja kaydirildi        saglama 23  ayar 9  cakisma 3  bosluk 5  iki kez uygulanan 3  agir yola dusen degisiklik 6
  C3 tek katman (hepsi saglamada)  saglama 29  ayar 0  cakisma 0  bosluk 5  iki kez uygulanan 0  agir yola dusen degisiklik 15
```

Çelişen iki kalemin son değeri **koşum sırasına** bağlı. Olağan sırada ayar katmanı sonra koştuğu
için o kazanır; çözümleyici 3.5.0, erişim politikası geniş olur. Bir yeniden sağlama tetiklendiğinde
sağlama katmanı sonra koşar ve aynı iki kalem ters değere döner. Kazanan katman bir karar değil,
bir **zamanlama sonucu**dur — ve o zamanlamayı belirleyen şey çoğu zaman kimsenin izlemediği bir
olaydır: bir makinenin ölmesi, bir ölçek değişikliği, bir imaj güncellemesi.

İkinci tablo bunun süresini veriyor. Yeniden sağlamadan sonra makinede ayar katmanının yazdığı on üç
kalemin dokuzu imajın taşıdığı değere döner; ajan otuz dakikada bir koştuğu için ortalama on beş
dakika yanlış değerde kalır. On dört makinede bu 126 kalem ve 1.890 kalem-dakikadır. Bu süre bir
kesinti değil, bir **sapma penceresi**dir: ortam koşuyor, ölçüm yapıyor, ama dokuz kalemi kaydın
söylediği değerde değil.

#### Sınır Kaydığında

Son tablo sınırın üç ayrı yere çizilmesini karşılaştırıyor. `C1` sınırı kaynağın kabuğunda tutar:
her katman kendi kaydını yazar, çakışma altı, iki kez uygulanan kalem altı, ama hiçbir değişiklik
ağır yola düşmez — on beş içerik değişikliği ajan koşumuyla biter. `C2` sınırı imaja kaydırır:
paket, servis ve saat dilimi kalemleri imaja pişirilir, ayar katmanı on beşten dokuza iner, çakışma
altıdan üçe düşer — ama altı değişiklik artık imaj pişirmeyi ve makinelerin yeniden oluşturulmasını
gerektirir. `C3` sınırı tümüyle kaldırır: yirmi dokuz kalem tek katmanda, çakışma sıfır, iki kez
uygulanan kalem sıfır — ve on beş içerik değişikliğinin tamamı ağır yola düşer.

Sağlama kaydının kalem sayısı da üç çizimde yirmiden yirmi üçe, oradan yirmi dokuza çıkıyor. Tek
katmana inmek kaydı sadeleştirmiyor; iki kaydın toplamını tek dosyaya yığıyor ve o dosyayı okuyan
herkesi ilgilendirmeyen kalemlerle karşılaştırıyor.

Üç çizimde de değişmeyen tek sayı **boşluk**tur: beş. Sınırın yerini değiştirmek çakışmayı azaltır
ya da artırır, ama hiçbir katmanın sahiplenmediği kalemi kapsama almaz. Boşluk sınırın nerede
olduğuyla ilgili değil, **envanterin kaydın dışında kalan kısmıyla** ilgilidir; onu kapatmanın tek
yolu kaleme bir sahip yazmaktır.

Çakışmanın azalması da bedava değil. `C1` ile `C3` arasında çakışma altıdan sıfıra inerken ağır yola
düşen değişiklik sıfırdan on beşe çıkıyor. İki sayı ters yönde hareket ediyor ve karar bu iki sayı
arasında veriliyor: bir çeyrekte altı çakışmanın gözden kaçırdığı bir tutarsızlık mı, yoksa on beş
yeniden oluşturmanın süresi mi daha pahalı. Sorunun tek bir doğru yanıtı yok, ama iki yanıt da
sayılabilir.

#### Özet

- Otuz dört kalem iki kayda bölündüğünde 14'ü yalnız sağlamada, 9'u yalnız ayarda, 6'sı ikisinde
  birden, 5'i hiçbirinde çıktı; 35 tanım yalnız 29 kalemi örtüyor.
- Çakışan 6 kalemin 4'ü aynı değeri taşıyor ve zararsızlıkları iki kaydın birlikte güncellenmesine
  bağlı; 2'si zaten çelişkili.
- Çelişen kalemde kazanan katman son koşandır: olağan sırada ayar, yeniden sağlamada sağlama;
  kazanan bir karar değil, zamanlama sonucudur.
- Yeniden sağlamadan sonra makine başına 9 kalem imajın değerine döner ve ajan koşana kadar
  ortalama 15 dakika yanlış kalır; 14 makinede 1.890 kalem-dakika sapma.
- Sınır üç ayrı yere çizildiğinde çakışma 6, 3 ve 0 oldu; ağır yola düşen değişiklik 0, 6 ve 15'e
  çıktı; boşluk üç çizimde de 5'te kaldı.

#### Sonraki Adım

Bu dersin son iki sayısı aynı şeyi iki yerden söylüyor. Yeniden sağlamadan sonra dokuz kalem
kaydın söylediği değerde değildi ve on beş dakika öyle kaldı; beş kalem hiçbir kaydın kapsamında
olmadığı için her zaman öyle. İkisi de aynı ada sahip: kayıt bir şey diyor, gerçek başka.

Sonraki ders bu konunun kapanışıdır ve bu farkı doğrudan ölçer. Bir sapma tespiti yazılır — kayıt
okunur, gerçek durum okunur, kalem kalem fark çıkarılır — ve dört şey sayılır: bir dönem boyunca
biriken sapma, tarama sıklığına göre **tespit gecikmesi**, sapmanın kaynağı ve düzeltmenin bedeli.
Sonuncusu en az öteki üçü kadar önemlidir: bir sapmayı geri almak bazen çalışan bir şeyi bozar.

### Sapma

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/sapma

Bir sapma tespiti yazılır: kayıt okunur, gerçek durum diskten okunur, kalem kalem fark çıkarılır ve her farkın kaynağı ayrılır. Otuz günlük bir dönemde biriken sapma, tarama sıklığına göre tespit gecikmesi, kaydın kapsamı dışında kaldığı için hiç kapanmayan sapma ve geri almanın çalışan bir şeyi bozduğu durum sayılır.

Önceki dört ders aynı şeyi dört yerden söyledi. İki kurulum dokuz kalemde ayrıldı; kısmi
başarısızlıktan sonra kayıt ile ortam üç kalemde uyuşmadı; yeniden oluşturma acil düzeltmeleri
sildi; yeniden sağlamadan sonra dokuz kalem on beş dakika kaydın söylemediği değerde kaldı.
Hepsinin adı aynıdır.

**Sapma**, kaydın söylediği ile gerçeğin arasındaki farktır. Bu ders onu doğrudan ölçer: bir tespit
yazılır — kayıt okunur, gerçek durum **diskten** okunur, kalem kalem fark çıkarılır ve her farkın
kaynağı ayrılır. Ardından otuz günlük bir dönem koşturulur ve dört şey sayılır: biriken sapma,
tarama sıklığına göre **tespit gecikmesi**, kaydın kapsamı dışında kaldığı için hiç kapanmayan
sapma ve düzeltmenin bedeli. Sonuncusu en az öteki üçü kadar önemli: bir sapmayı geri almak bazen
çalışan bir şeyi bozar.

Bir ayrım baştan konur. M22/K03'te küme mimarisi ele alınırken ölçülen uzlaşma döngüsü **sürekli**
koşuyordu ve fark doğduğu anda kapanıyordu; burada uygulama **tetiklenir** ve iki tetik arasında
sapma birikir. Bu dersin tüm sayıları o boşlukta doğuyor.

Ortam **bölgesel ölçüm ağı**nın hazırlık ortamıdır ve **kurgu**dur. Kayıt ile gerçek durum
gerçekten diske yazılır ve tespit gerçekten koşar; otuz günlük dönem `node` ile modellenir ve
üreteç kendi yazılıdır.

**AT25.** Kayıt yirmi kalem yazar. Gerçek durum bir dizin ağacıdır ve yirmi beş dosyadır; tespit onu
kayıttan değil, **diskten** okur.
**AT26.** Sağlayıcının belgelenmiş varsayılanları ayrı bir dosyadadır. Kayıtta olmayan bir kalem o
listedeyse kaynağı **sağlayıcı varsayılanı**, değilse **kayıt dışı kaynak**tır.
**AT27.** Otuz günlük dönemde olaylar üreteçle üretilir (tohum 4471903, üreteç kendi yazılıdır);
günlük olay sayısı sıfır, bir ya da ikidir.
**AT28.** Bir olayın kaynağı elle değişiklik, kayıt dışı kaynak ya da sağlayıcı varsayılanıdır.
Yalnız **elle değişiklik** kaydın kapsamındadır; ötekiler bir uygulamayla kapanmaz.
**AT29.** Uygulama tetiklenir: 1., 12. ve 25. günlerde. **Tarama uygulamadan ayrıdır** — tespit
eder, kapatmaz.
**AT30.** Elle değişikliklerin bir kısmı bir olayı kapatmak için yapılmış **canlı düzeltme**dir;
geri alındığında kapattığı sorun geri gelir.

#### Kayıt ve Gerçek

```bash
#!/usr/bin/env bash
# Kayit ve gercek ortam: kayit bir dosya, gercek durum bir dizin agacidir. Ikisi de diske yazilir.
set -e
rm -rf gercek

cat > kayit.alt <<'SON'
kaynak ag "olcum-agi" { blok = "10.40.0.0/16"  alt_ag_sayisi = 3  ozel = "dogru" }
kaynak makine "toplayici" { sayi = 3  boyut = "orta"  disk_gb = 40  imaj = "taban-7" }
kaynak makine "dogrulayici" { sayi = 2  boyut = "kucuk"  disk_gb = 20  imaj = "taban-7" }
kaynak kuyruk "okuma" { bolum = 6  saklama_saat = 72  olu_mektup = "acik" }
kaynak veri_deposu "olcum" { boyut_gb = 200  yedek_gun = 14  surum = "5.2" }
kaynak nesne_deposu "arsiv" { sinif = "seyrek"  surumleme = "acik"  yasam_gun = 365 }
SON

# Saglayicinin belgelenmis varsayilanlari: kayitta yazmayan, kendiliginden olusan alanlar.
cat > varsayilan.txt <<'SON'
ag.olcum-agi.cikis_yolu
kuyruk.okuma.sifreleme
veri_deposu.olcum.bakim_penceresi
nesne_deposu.arsiv.erisim_kurali
SON

yaz() { mkdir -p "gercek/$(dirname "$1")"; printf '%s\n' "$2" > "gercek/$1"; }
# Kayitla uyusan on dort kalem
yaz ag/olcum-agi/blok 10.40.0.0/16;        yaz ag/olcum-agi/ozel dogru
yaz makine/toplayici/boyut orta;           yaz makine/toplayici/imaj taban-7
yaz makine/dogrulayici/sayi 2;             yaz makine/dogrulayici/boyut kucuk
yaz makine/dogrulayici/disk_gb 20;         yaz makine/dogrulayici/imaj taban-7
yaz kuyruk/okuma/bolum 6;                  yaz kuyruk/okuma/olu_mektup acik
yaz veri_deposu/olcum/boyut_gb 200;        yaz veri_deposu/olcum/surum 5.2
yaz nesne_deposu/arsiv/surumleme acik;     yaz nesne_deposu/arsiv/yasam_gun 365
# Elle degistirilmis bes kalem (kayittaki degerden farkli)
yaz ag/olcum-agi/alt_ag_sayisi 4;          yaz makine/toplayici/sayi 5
yaz makine/toplayici/disk_gb 80;           yaz kuyruk/okuma/saklama_saat 24
yaz veri_deposu/olcum/yedek_gun 7
# nesne_deposu/arsiv/sinif silinmis: kayitta var, gercekte yok
# Saglayici varsayilani (uc kalem) ve kayit disi kaynak (uc kalem)
yaz ag/olcum-agi/cikis_yolu acik;          yaz kuyruk/okuma/sifreleme saglayici-anahtari
yaz veri_deposu/olcum/bakim_penceresi pazar-03:00
yaz guvenlik/gecici/giris 0.0.0.0-8443;    yaz ad_kaydi/toplayici/ic toplayici.ic
yaz makine/tanilama/sayi 1
printf 'kayit %s satir   gercek %s dosya\n' "$(wc -l < kayit.alt | tr -d ' ')" \
  "$(find gercek -type f | wc -l | tr -d ' ')"
```

```
kayit 6 satir   gercek 25 dosya
```

#### Kalem Kalem Fark

```js
// sapma.mjs — kaydi okur, gercek durumu diskten okur, kalem kalem fark cikarir. Hicbir sey duzeltmez.
import { readFileSync, readdirSync, statSync } from "node:fs";

const BLOK = /^kaynak\s+(\w+)\s+"([\w-]+)"\s*\{(.*)\}$/;
const kayit = new Map();
for (const s of readFileSync("kayit.alt", "utf8").split("\n")) {
  const m = BLOK.exec(s.trim());
  if (m) for (const x of m[3].matchAll(/(\w+)\s*=\s*("[^"]*"|[\w./-]+)/g))
    kayit.set(`${m[1]}.${m[2]}.${x[1]}`, x[2].replaceAll('"', ""));
}
const varsayilan = new Set(readFileSync("varsayilan.txt", "utf8").trim().split("\n"));

const gercek = new Map();                       // gercek durum diskten okunur, kayittan degil
const gez = (yol, on = []) => {
  for (const ad of readdirSync(yol).sort()) {
    const t = `${yol}/${ad}`;
    if (statSync(t).isDirectory()) gez(t, [...on, ad]);
    else gercek.set([...on, ad].join("."), readFileSync(t, "utf8").trim());
  }
};
gez("gercek");

const tum = [...new Set([...kayit.keys(), ...gercek.keys()])].sort();
const fark = tum.filter((k) => kayit.get(k) !== gercek.get(k)).map((k) => ({ k,
  kaynak: !gercek.has(k) ? "eksik kalem" : !kayit.has(k)
    ? (varsayilan.has(k) ? "saglayici varsayilani" : "kayit disi kaynak") : "elle degisiklik" }));
const say = (t) => fark.filter((f) => f.kaynak === t).length;

console.log(`kayit ${kayit.size} kalem   gercek ${gercek.size} kalem   birlesim ${tum.length}`);
console.log(`sapma ${fark.length} kalem` +
  `   elle ${say("elle degisiklik")}   eksik ${say("eksik kalem")}` +
  `   kayit disi ${say("kayit disi kaynak")}   varsayilan ${say("saglayici varsayilani")}`);
for (const f of fark)
  console.log(`  ${f.kaynak.padEnd(22)} ${f.k.padEnd(34)} kayit "${kayit.get(f.k) ?? "-"}"` +
    `  gercek "${gercek.get(f.k) ?? "-"}"`);
const gorunen = fark.filter((f) => kayit.has(f.k)).length;
console.log(`kaydin kapsaminda olan, yani bir uygulamanin kapatabilecegi sapma ${gorunen}` +
  `   kapsam disi ${fark.length - gorunen}`);
```

```
kayit 20 kalem   gercek 25 kalem   birlesim 26
sapma 12 kalem   elle 5   eksik 1   kayit disi 3   varsayilan 3
  kayit disi kaynak      ad_kaydi.toplayici.ic              kayit "-"  gercek "toplayici.ic"
  elle degisiklik        ag.olcum-agi.alt_ag_sayisi         kayit "3"  gercek "4"
  saglayici varsayilani  ag.olcum-agi.cikis_yolu            kayit "-"  gercek "acik"
  kayit disi kaynak      guvenlik.gecici.giris              kayit "-"  gercek "0.0.0.0-8443"
  elle degisiklik        kuyruk.okuma.saklama_saat          kayit "72"  gercek "24"
  saglayici varsayilani  kuyruk.okuma.sifreleme             kayit "-"  gercek "saglayici-anahtari"
  kayit disi kaynak      makine.tanilama.sayi               kayit "-"  gercek "1"
  elle degisiklik        makine.toplayici.disk_gb           kayit "40"  gercek "80"
  elle degisiklik        makine.toplayici.sayi              kayit "3"  gercek "5"
  eksik kalem            nesne_deposu.arsiv.sinif           kayit "seyrek"  gercek "-"
  saglayici varsayilani  veri_deposu.olcum.bakim_penceresi  kayit "-"  gercek "pazar-03:00"
  elle degisiklik        veri_deposu.olcum.yedek_gun        kayit "14"  gercek "7"
kaydin kapsaminda olan, yani bir uygulamanin kapatabilecegi sapma 6   kapsam disi 6
```

On iki kalem sapmış ve son satır ölçünün asıl yerini gösteriyor: on ikinin **altısı** kaydın
kapsamında. Kalan altı kalem için bir uygulama koşturmak hiçbir şey değiştirmez, çünkü kayıt o
kalemler hakkında bir şey söylemiyor — üçü sağlayıcının kendiliğinden açtığı alan, üçü kayıt
yazılmadan yaratılmış kaynak. Bunları görebilmenin tek nedeni tespitin **iki yönlü** olmasıdır:
yalnız kayıttaki kalemler taranmıyor, gerçek durumun tamamı dolaşılıyor ve kayıtta karşılığı
olmayanlar da yazılıyor.

Kaynak ayrımı bir etiketleme işi değil, bir **eylem ayrımı**dır. Elle değişiklik bir uygulamayla
kapanır. Eksik kalem bir uygulamayla geri gelir. Kayıt dışı kaynak ancak kayda alınarak ya da
silinerek kapanır; sağlayıcı varsayılanı ise ne kapanır ne kapanmalıdır — yalnız kaydın onun
hakkında sessiz olduğu bilinmelidir. Dört ayrı fark türü, dört ayrı karar.

#### Bir Dönem Boyunca

```js
// donem.mjs — otuz gunluk sapma birikimi, tespit gecikmesi ve duzeltmenin bedeli. Model.
const TOHUM = 4471903, GUN = 30, UYGULAMA = [1, 12, 25];   // uygulama tetiklenir, surekli kosmaz
const uretec = (t) => () => (t = (t * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(TOHUM);

const olay = [];
for (let g = 1; g <= GUN; g++)
  for (let n = (r() < 0.55) + (r() < 0.18); n > 0; n--) {
    const p = r();
    const kaynak = p < 0.45 ? "elle" : p < 0.72 ? "kayit disi" : "varsayilan";
    olay.push({ g, kaynak, kapsamda: kaynak === "elle", canli: kaynak === "elle" && r() < 0.35 });
  }
const say = (t) => olay.filter((o) => o.kaynak === t).length;
const kapsamli = olay.filter((o) => o.kapsamda);
console.log(`tohum ${TOHUM}   gun ${GUN}   olay ${olay.length}   uygulama gunleri` +
  ` ${UYGULAMA.join(", ")}`);
console.log(`sapmanin kaynagi   elle ${say("elle")}   kayit disi ${say("kayit disi")}` +
  `   saglayici varsayilani ${say("varsayilan")}   kapsamda ${kapsamli.length}`);

// Iki rejim: uygulama tetiklenir (uc gun) ve surekli uzlasma (her gun). Ikisi de yalniz kapsamdakini kapatir.
const rejim = (gunler) => {
  const toplam = [], kapsam = [];
  let kapatilan = 0;
  for (let g = 1; g <= GUN; g++) {
    if (gunler.includes(g)) kapatilan = kapsamli.filter((o) => o.g <= g).length;
    kapsam.push(kapsamli.filter((o) => o.g <= g).length - kapatilan);
    toplam.push(olay.filter((o) => o.g <= g).length - kapatilan);
  }
  const alan = (x) => x.reduce((a, b) => a + b, 0);
  return { toplam, kapsam, alanT: alan(toplam), alanK: alan(kapsam) };
};
const A = rejim(UYGULAMA), B = rejim([...Array(GUN)].map((_, i) => i + 1));
console.log(`\nacik sapma (gun sonu, ${GUN} gun)`);
console.log(`  tetiklenen uygulama  toplam  ${A.toplam.join(" ")}`);
console.log(`  tetiklenen uygulama  kapsam  ${A.kapsam.join(" ")}`);
console.log(`  surekli uzlasma      toplam  ${B.toplam.join(" ")}`);
console.log(`  gun-kalem alani   kapsamdaki sapma: tetiklenen ${A.alanK}   surekli ${B.alanK}`);
console.log(`  gun-kalem alani   tum sapma:        tetiklenen ${A.alanT}   surekli ${B.alanT}` +
  `   fark ${A.alanT - B.alanT}`);

console.log(`\ntespit gecikmesi (yalniz kapsamdaki ${kapsamli.length} sapma; tarama tespit eder, kapatmaz)`);
for (const n of [1, 3, 7, 30]) {
  const gecikme = kapsamli
    .map((o) => { for (let g = o.g; g <= GUN; g++) if (g % n === 0) return g - o.g; return null; })
    .filter((x) => x !== null);
  console.log(`  tarama ${String(n).padStart(2)} gunde bir   ortalama` +
    ` ${(gecikme.reduce((a, b) => a + b, 0) / gecikme.length).toFixed(1)} gun` +
    `   en kotu ${Math.max(...gecikme)} gun   tespit edilen ${gecikme.length}/${kapsamli.length}`);
}

const geriAlinan = kapsamli.filter((o) => o.g <= UYGULAMA[UYGULAMA.length - 1]);
const bozulan = geriAlinan.filter((o) => o.canli);
console.log(`\nduzeltmenin bedeli`);
console.log(`  geri alinan sapma ${geriAlinan.length}   bunun canli duzeltme olani` +
  ` ${bozulan.length}   geri alinca calisan bir sey bozulur`);
console.log(`  kalici olarak kapanan ${geriAlinan.length - bozulan.length}` +
  `   geri gelen ${bozulan.length} (%${(100 * bozulan.length / geriAlinan.length).toFixed(0)})`);
console.log(`  kapsam disi ${olay.length - kapsamli.length} sapma hicbir uygulamayla kapanmadi;` +
  ` donem sonunda hepsi duruyor`);
```

```
tohum 4471903   gun 30   olay 21   uygulama gunleri 1, 12, 25
sapmanin kaynagi   elle 9   kayit disi 6   saglayici varsayilani 6   kapsamda 9

acik sapma (gun sonu, 30 gun)
  tetiklenen uygulama  toplam  1 1 2 2 2 3 3 3 4 5 6 3 5 6 7 8 8 8 9 9 10 11 12 13 9 10 10 11 11 13
  tetiklenen uygulama  kapsam  0 0 1 1 1 1 1 1 2 3 3 0 1 2 2 2 2 2 2 2 2 2 3 4 0 1 1 1 1 1
  surekli uzlasma      toplam  1 1 1 1 1 2 2 2 2 2 3 3 4 4 5 6 6 6 7 7 8 9 9 9 9 9 9 10 10 12
  gun-kalem alani   kapsamdaki sapma: tetiklenen 45   surekli 0
  gun-kalem alani   tum sapma:        tetiklenen 205   surekli 160   fark 45

tespit gecikmesi (yalniz kapsamdaki 9 sapma; tarama tespit eder, kapatmaz)
  tarama  1 gunde bir   ortalama 0.0 gun   en kotu 0 gun   tespit edilen 9/9
  tarama  3 gunde bir   ortalama 1.0 gun   en kotu 2 gun   tespit edilen 9/9
  tarama  7 gunde bir   ortalama 3.1 gun   en kotu 5 gun   tespit edilen 9/9
  tarama 30 gunde bir   ortalama 13.7 gun   en kotu 27 gun   tespit edilen 9/9

duzeltmenin bedeli
  geri alinan sapma 8   bunun canli duzeltme olani 2   geri alinca calisan bir sey bozulur
  kalici olarak kapanan 6   geri gelen 2 (%25)
  kapsam disi 12 sapma hicbir uygulamayla kapanmadi; donem sonunda hepsi duruyor
```

Otuz günde yirmi bir sapma doğdu ve dokuzu kaydın kapsamındaydı. İki dizi arasındaki fark
tetiklenen uygulamanın ne demek olduğunu tek satıra indiriyor: kapsamdaki sapma tetiklenen rejimde
kırk beş gün-kalem açık kaldı, sürekli uzlaşmada sıfır. Üç tetik günü dizide açıkça görünüyor —
1., 12. ve 25. günlerde kapsam satırı sıfıra iniyor, sonra yeniden tırmanıyor. Sapma yok olmuyor,
her tetik arasında yeniden doğuyor; biriken şey sapmanın kendisi değil, **kapanmamış** sapmadır.

Toplam satırı ise iki rejimde de tırmanmaya devam ediyor ve otuzuncu günde on üç ile on ikide
duruyor. Aradaki fark yalnız kırk beş gün-kalemdir ve o da kapsamdaki dokuz kalemden geliyor. Kalan
on iki sapma hiçbir rejimde kapanmadı: uygulamanın sürekli koşması onları görünür kılmıyor, çünkü
görünmemelerinin nedeni sıklık değil, **kapsam**. Bu, konunun ilk dersinde beş kalem olarak
görülen şeyin bir dönem boyunca ne kadar büyüdüğünü gösteriyor.

#### Tespit Gecikmesi ve Düzeltmenin Bedeli

Tarama tablosu ayrı bir kararı ölçüyor. Tarama uygulamadan bağımsızdır: bir sapmayı **kapatmadan**
görünür kılar. Günlük tarama gecikmeyi sıfırlıyor, üç günde bir tarama ortalama bir güne, haftalık
tarama üç virgül bir güne, aylık tarama on üç virgül yedi güne çıkarıyor — ve aylık taramada en kötü
gecikme yirmi yedi gün. Yirmi yedi gün, bir kalemin kaydın söylemediği değerde koştuğu ve kimsenin
bilmediği süredir. Tarama sıklığını seçmek, bu sayıyı seçmektir.

Son tablo bu dersin en ters sayısını taşıyor. Yirmi beşinci güne kadar sekiz sapma geri alındı ve
ikisi **canlı düzeltme**ydi: bir olayı kapatmak için elle yapılmış, sorunu gerçekten gideren
değişiklikler. Geri alındıklarında kapattıkları sorun geri geldi ve ikisi de sonraki günlerde
yeniden yapıldı. Geri almanın kalıcı olarak kapattığı sapma altı, geri geleni iki — yüzde yirmi
beş.

Bu iki kalem sapmanın tek yönlü okunamayacağını gösteriyor. Bir sapma "kayıt haklı, gerçek yanlış"
demek değildir; yalnız ikisinin **ayrıldığı** demektir. Ayrılmanın iki yorumu vardır ve hangisinin
doğru olduğu farkın kendisinden okunamaz: ya kayıt güncellenmeli ve düzeltme kayda işlenmeli, ya
gerçek geri alınmalı. Otomatik uygulama her zaman ikinciyi seçer, çünkü başka bir seçeneği yoktur.
Kaydın bir kalem hakkında sessiz kalmasının bedeli görünmezlikti; kaydın bir kalem hakkında **eski**
kalmasının bedeli, çalışan bir şeyin bozulmasıdır.

#### Özet

- Tespit 26 kalemi karşılaştırdı ve 12 sapma buldu: 5 elle değişiklik, 1 eksik kalem, 3 kayıt dışı
  kaynak, 3 sağlayıcı varsayılanı; bunların yalnız 6'sı bir uygulamayla kapanabilir.
- Otuz günde 21 sapma doğdu; 9'u kaydın kapsamındaydı, 12'si dönem sonunda hâlâ açıktı ve hiçbir
  rejimde kapanmadı.
- Tetiklenen uygulama kapsamdaki sapmayı 45 gün-kalem açık bıraktı, sürekli uzlaşma 0; fark
  sıklıktan, kapanmayan 12 kalem ise kapsamdan geliyor.
- Tarama sıklığı gecikmeyi doğrudan belirliyor: günlük 0,0 gün, haftalık 3,1 gün, aylık 13,7 gün
  ortalama ve 27 gün en kötü.
- Geri alınan 8 sapmanın 2'si canlı düzeltmeydi ve geri alınca çalışan bir şeyi bozdu; geri almanın
  kalıcı kapattığı 6, geri gelen 2 kalemdir.

#### Sonraki Adım

Sapma bu noktada sayılabilir bir şey. Kaydın kapsamı biliniyor, kapsam dışındaki kalem adıyla
yazılabiliyor, farkın kaynağı dörde ayrılabiliyor, tespit gecikmesi tarama sıklığına bağlanabiliyor
ve düzeltmenin bir bedeli olduğu ölçülmüş durumda. Ölçünün tamamı tek bir varsayıma yaslanıyor:
ortada okunabilir bir kayıt var.

O kaydın kendisi ise hiç ele alınmadı. Beş derste kayıt hep hazır bulundu — altı satırlık bir
dosya, kalemlerini değerleriyle sayan bir liste. Nasıl yazıldığı, hangi yapılarla kurulduğu, bir
kaynağın ötekine nasıl bağlandığı, aynı tanımın üç ortam için nasıl tekrar edilmeden yazılacağı,
bir değerin nereden geleceği ve bir değişiklik uygulanmadan önce ne göstereceği — bunların hiçbiri
sorulmadı. Sonraki konu kaydın kendisini yazmakla başlıyor.

## Bildirimsel Sağlama

İstenen altyapının yazılması ve uygulanması: yapılandırma dilinin blok, ifade ve tip yapıları, hedef sisteme bağlanan sağlayıcılar ve kaynaklar, var olan altyapıyı okuyan veri kaynakları, değişken, yerel değer ve çıktılarla parametreleştirme, örtük ve açık bağımlılık sıralaması, sayı ve küme temelli yineleme, değişikliği önceden gösteren planlama akışı ve var olan kaynağın yönetime alınması.

### Yapılandırma Dili

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/yapilandirma-dili

Kendi küçük yapılandırma dilimiz tanımlanır ve ayrıştırıcısı yazılır; dilin taşıdığı bildirim, ifade ve tip yapıları ayrı ayrı sayılır. Tip bildirimi olmayan kayıtta on bir kusurun kaçının ancak uygulamada göründüğü, tip eklenince kaçının ayrıştırma anında yakalandığı ve aynı hedefin üç yazımında satır sayısıyla okuma sıçramasının ters yönde hareket ettiği ölçülür.

Bir önceki konu sapmayı sayılabilir kıldı; ölçülmeden kalan tek şey kaydın kendisiydi. Bu ders kendi
küçük **yapılandırma dilimizi** tanımlar, `node` ile bir **ayrıştırıcı** yazar ve üç şeyi ölçer:
dilin taşıdığı bildirim, ifade ve tip yapıları; tipsiz kayıt ile tipli kaydın aynı kusurları nerede
yakaladığı; aynı hedefin üç yazımının bedeli. Ölçüm **bölgesel ölçüm ağının** batı ortamı
üzerindedir ve **kurgudur**; dil, ayrıştırıcı ve hedef sistem `node` ile kurulmuş bir **modeldir**.

**BS1.** Kurgu kayıt batı ortamının iki kaynağını taşır: bir makine kümesi ve bir kuyruk.
**BS2.** Tip bildirimi dilin yapısıdır, hedef sistemin değil; uygulamadan önce koşar.
**BS3.** Tip denetimi ifadenin sonucunu hesaplamaz, yalnız sabit yazılmış değeri sınar.
**BS4.** Hedef sistem modeli kayıttaki değeri olduğu gibi yazar, yalnız sayı beklenen yerde sayı
olmayanı reddeder.
**BS5.** Hedefe yazılan değer doğru kayıttakinden farklıysa ve hiçbir aşama bildirmiyorsa kusur
**sessiz** sayılır.
**BS6.** Sıçrama, bir alanın değerini bulmak için izlenen blok başvurusu sayısıdır.

#### Dilin Üç Yapısı

```js
// dil.mjs — dilimizin sozcuk ayiricisi ve ayristiricisi.
const TUR = ["dize", "sayi", "ad", "imce", "yorum", "bilinmeyen"];
const IM = /"([^"]*)"|(-?\d+)|([a-z_][a-z0-9_]*)|([{}=,.()])|(#[^\n]*)|(\S)/g;

export function sozcukle(k) {
  const t = [];
  for (const m of k.matchAll(IM)) {
    const i = m.slice(1).findIndex((x) => x !== undefined);
    if (i === 5) throw new Error(`cozulemeyen im "${m[0]}"`);
    if (i !== 4) t.push({ tur: TUR[i], deger: m[i + 1] });
  }
  return t;
}

export function ayristir(kaynak) {
  const t = sozcukle(kaynak); let i = 0;
  const bak = () => t[i], al = () => t[i++];
  const bekle = (d) => { const x = al();
    if (!x || x.deger !== d) throw new Error(`"${d}" bekleniyordu, "${x ? x.deger : "son"}" bulundu`); };
  const govde = () => { const h = {};                // blok govdesi ve harita ayni yol
    while (bak().deger !== "}") { const a = al().deger; bekle("="); h[a] = ifade();
      if (bak().deger === ",") al(); }
    al(); return h; };
  function ifade() {
    const x = al();
    if (!x) throw new Error("ifade bitmeden kayit bitti");
    if (x.tur === "sayi") return { d: "sayi", deger: Number(x.deger) };
    if (x.tur === "dize") return { d: "dize", deger: x.deger };
    if (x.deger === "{") return { d: "harita", alanlar: govde() };
    if (x.tur !== "ad") throw new Error(`beklenmeyen "${x.deger}"`);
    if (bak() && bak().deger === "(") { al(); const arg = [];
      while (bak().deger !== ")") { arg.push(ifade()); if (bak().deger === ",") al(); }
      al(); return { d: "cagri", ad: x.deger, arg }; }
    const yol = [x.deger];
    while (bak() && bak().deger === ".") { al(); yol.push(al().deger); }
    return { d: "yol", yol };
  }
  const b = [];
  while (i < t.length) {
    const tur = al().deger, etiket = [];
    while (bak() && bak().tur !== "imce") etiket.push(al().deger);
    bekle("{");
    b.push({ tur, etiket, alanlar: govde(), anahtar: tur === "kaynak" ? etiket.join(".") : tur });
  }
  return b;
}
```

```js
// kayit.mjs — bati ortaminin kurgu kaydi.
export const ORTAM = `ortam "bati" { bolge = "bati"  toplayici = 6  esik_ms = 900  saklama = 30 }`;
export const GOVDE = `
kaynak makine "toplayici" {
  ad        = birlestir(ortam.bolge, "-toplayici")
  sayi      = ortam.toplayici
  cekirdek  = 4
  bellek_gb = 8
  etiket    = { gorev = "sayac-okuma", bolge = ortam.bolge }
}
kaynak kuyruk "sayac" {
  ad         = birlestir(ortam.bolge, "-sayac-kuyrugu")
  bolme      = carp(ortam.toplayici, 2)
  saklama_gn = ortam.saklama
  bicim      = "ikili"
  esik_ms    = ortam.esik_ms
}`;
export const TIP = `
tip ortam  { bolge = dize  toplayici = sayi  esik_ms = sayi  saklama = sayi }
tip makine { ad = dize  sayi = aralik(1, 64)  cekirdek = aralik(1, 64)
             bellek_gb = aralik(1, 256)  etiket = harita }
tip kuyruk { ad = dize  bolme = aralik(1, 128)  saklama_gn = aralik(1, 365)
             bicim = kume("ikili", "metin")  esik_ms = sayi }`;
```

Dilde tek bildirim biçimi vardır: tür sözcüğü, etiketler, ayraç içinde alan atamaları. Sağ taraf
sabit, başka bir bloğun alanına **başvuru**, bir **çağrı** ya da bir **harita** olabilir; işleç
yoktur, çarpma da bir çağrıdır. Ölçüyü taşıyan en küçük biçim budur. Tip bildirimi de aynı söz
dizimiyle yazılır.

#### Tipsiz Kayıt, Tipli Kayıt

```js
// denet.mjs — ad cozumu, tip denetimi, uygulama modeli.
import { ayristir } from "./dil.mjs";

const ISLEV = { birlestir: (...a) => a.join(""), carp: (a, b) => a * b, sec: (h, k) => h[k] };
const alt = (e) => e.d === "cagri" ? e.arg : e.d === "harita" ? Object.values(e.alanlar) : [];
export const yaz = (g, ...s) => console.log(s.map((x) => String(x).padEnd(g)).join("").trimEnd());
export const dugum = (e) => [e, ...alt(e).flatMap(dugum)];
export const derin = (e) => 1 + Math.max(0, ...alt(e).map(derin));
export const sabit = (e) => e.d === "sayi" || e.d === "dize";
export const cins = (b) => b.tur === "kaynak" ? b.etiket[0] : b.tur;
export const dizinle = (b) => new Map(b.map((x) => [x.anahtar, x]));

export const semaCikar = (m) => Object.fromEntries(ayristir(m).map((b) => [b.etiket[0],
  Object.fromEntries(Object.entries(b.alanlar).map(([a, e]) => [a, e.d === "yol" ? { taban: e.yol[0] }
    : e.ad === "aralik" ? { taban: "sayi", aralik: e.arg.map((x) => x.deger) }
    : { taban: "dize", kume: e.arg.map((x) => x.deger) }]))]));

export function coz(yol, dizin) {
  for (let n = yol.length - 1; n >= 1; n--) {
    const b = dizin.get(yol.slice(0, n).join(".")), a = yol.slice(n).join(".");
    if (!b) continue;
    if (!(a in b.alanlar)) throw new Error(`${b.anahtar} icinde ${a} yok`);
    return { blok: b, alan: a };
  }
  throw new Error(`${yol.join(".")} bir bloga denk gelmiyor`);
}

export function tipDenet(bloklar, sema) {            // yalniz tipli kayitta kosar
  const h = [];
  for (const b of bloklar) {
    const t = sema[cins(b)];
    for (const a of Object.keys(t)) if (!(a in b.alanlar)) h.push(`${b.anahtar}.${a} eksik`);
    for (const [a, e] of Object.entries(b.alanlar)) {
      const k = t[a], y = `${b.anahtar}.${a}`;
      if (!k) h.push(`${y} tipsiz`);
      else if (!sabit(e)) continue;                  // hesaplanan deger kayittan okunamaz
      else if (e.d !== k.taban) h.push(`${y} ${k.taban} degil`);
      else if (k.aralik && (e.deger < k.aralik[0] || e.deger > k.aralik[1])) h.push(`${y} aralik disi`);
      else if (k.kume && !k.kume.includes(e.deger)) h.push(`${y} kume disi`);
    }
  }
  return h;
}

export function degerle(e, d) {
  if (sabit(e)) return e.deger;
  if (e.d === "harita") return Object.fromEntries(Object.entries(e.alanlar).map(([a, v]) => [a, degerle(v, d)]));
  if (e.d === "cagri") return ISLEV[e.ad](...e.arg.map((x) => degerle(x, d)));
  const c = coz(e.yol, d);
  return degerle(c.blok.alanlar[c.alan], d);
}

export function uygula(bloklar, sema) {              // hedef sistem modeli
  const d = dizinle(bloklar);
  return Object.fromEntries(bloklar.filter((x) => x.tur === "kaynak").flatMap((b) =>
    Object.entries(b.alanlar).map(([a, e]) => {
      const v = degerle(e, d), y = `${b.anahtar}.${a}`;
      if (sema[cins(b)][a]?.taban === "sayi" && !Number.isFinite(v)) throw new Error(`${y} sayi degil`);
      return [y, typeof v === "object" ? JSON.stringify(v) : v];
    })));
}
```

```js
// hata.mjs — uc yapi ve on bir kusur sayilir.
import { ayristir } from "./dil.mjs";
import { ORTAM, GOVDE, TIP } from "./kayit.mjs";
import { dugum, sabit, dizinle, coz, tipDenet, uygula, semaCikar, yaz } from "./denet.mjs";

const SEMA = semaCikar(TIP), KAYIT = ORTAM + GOVDE;
const bloklar = ayristir(KAYIT), TABAN = uygula(bloklar, SEMA);
const atama = bloklar.flatMap((x) => Object.values(x.alanlar));
const ifade = atama.flatMap(dugum), tipAlan = Object.values(SEMA).flatMap(Object.values);
const say = (t) => `${ifade.filter((e) => e.d === t).length} ${t}`;
console.log(`bildirim ${bloklar.length} blok, ${atama.length} atama, ${atama.filter((e) => !sabit(e)).length} hesaplanan`);
console.log(`ifade    ${ifade.length} dugum = ` + ["sayi", "dize", "yol", "cagri", "harita"].map(say).join(" + "));
console.log(`tip      ${Object.keys(SEMA).length} cins, ${tipAlan.length} alan bildirimi, ` +
  `${tipAlan.filter((k) => k.aralik || k.kume).length} kisitli\n`);

const boz = (alan, yeni) => yeni === null            // tek satirlik kusur
  ? KAYIT.replace(new RegExp(`\n *${alan} *=[^\n]*`), "")
  : KAYIT.replace(new RegExp(`(\n *)${alan}( *=)[^\n]*`),
    yeni[0] === "@" ? `$1${yeni.slice(1)}` : `$1${alan}$2 ${yeni}`);
const KUSUR = [
  ["tur uyusmazligi", "cekirdek", '"4"'], ["alan eksik", "bellek_gb", null],
  ["alan adi yanlis", "saklama_gn", "@saklama = 30"],
  ["kume disi deger", "bicim", '"ham"'],
  ["aralik disi sabit", "cekirdek", "128"], ["basvuru yazimi", "sayi", "ortam.toplayic"],
  ["olmayan alan", "esik_ms", "makine.toplayici.cekirdekler"],
  ["soz dizimi", "bicim", 'ikili"'], ["yanlis alana basvuru", "sayi", "ortam.esik_ms"],
  ["hesaplanan asim", "saklama_gn", "carp(ortam.saklama, 20)"],
  ["hesaplanan sifir", "esik_ms", "carp(ortam.esik_ms, 0)"],
];

function asama(metin, tipVar) {                      // kusur hangi asamada goruluyor
  let b;
  try { b = ayristir(metin); const d = dizinle(b);
    for (const x of b) for (const e of Object.values(x.alanlar))
      dugum(e).filter((y) => y.d === "yol").forEach((y) => coz(y.yol, d));
  } catch (e) { return ["ayristirma", e.message]; }
  if (tipVar) { const t = tipDenet(b, SEMA); if (t.length) return ["tip", t[0]]; }
  let g; try { g = uygula(b, SEMA); } catch (e) { return ["uygulama: hata", e.message]; }
  const f = Object.keys(TABAN).filter((k) => TABAN[k] !== g[k]);
  return f.length ? ["uygulama: sessiz", `${f[0]} ${TABAN[f[0]]} yerine ${g[f[0]]}`] : ["gecti", "-"];
}

console.log(`taban kayit ${asama(KAYIT, true)[0]}, hedefe yazilan ${Object.keys(TABAN).length} alan`);
yaz(22, "kusur", "tipsiz kayit", "tipli kayit", "tipli kayitta uygulamadaki iz");
const sayac = { A: {}, B: {} };
for (const [ad, alan, yeni] of KUSUR) {
  const m = boz(alan, yeni), [a] = asama(m, false), [b, iz] = asama(m, true);
  sayac.A[a] = (sayac.A[a] || 0) + 1; sayac.B[b] = (sayac.B[b] || 0) + 1;
  yaz(22, ad, a, b, b[0] === "u" ? iz : "");
}
console.log();
yaz(22, "asama", "tipsiz", "tipli");
for (const a of ["ayristirma", "tip", "uygulama: hata", "uygulama: sessiz"])
  yaz(22, a, sayac.A[a] || 0, sayac.B[a] || 0);
```

```
bildirim 3 blok, 14 atama, 7 hesaplanan
ifade    22 dugum = 6 sayi + 5 dize + 7 yol + 3 cagri + 1 harita
tip      3 cins, 14 alan bildirimi, 6 kisitli

taban kayit gecti, hedefe yazilan 10 alan
kusur                 tipsiz kayit          tipli kayit           tipli kayitta uygulamadaki iz
tur uyusmazligi       uygulama: hata        tip
alan eksik            uygulama: sessiz      tip
alan adi yanlis       uygulama: sessiz      tip
kume disi deger       uygulama: sessiz      tip
aralik disi sabit     uygulama: sessiz      tip
basvuru yazimi        ayristirma            ayristirma
olmayan alan          ayristirma            ayristirma
soz dizimi            ayristirma            ayristirma
yanlis alana basvuru  uygulama: sessiz      uygulama: sessiz      makine.toplayici.sayi 6 yerine 900
hesaplanan asim       uygulama: sessiz      uygulama: sessiz      kuyruk.sayac.saklama_gn 30 yerine 600
hesaplanan sifir      uygulama: sessiz      uygulama: sessiz      kuyruk.sayac.esik_ms 900 yerine 0

asama                 tipsiz                tipli
ayristirma            3                     3
tip                   0                     5
uygulama: hata        1                     0
uygulama: sessiz      7                     3
```

Üç blok on dört atama taşır; yedisi hesaplanan ifadedir, yirmi iki ifade düğümünün on biri sabittir.
Tip tarafı üç cins için on dört alan bildirimi yazar — aynı cinsten on kaynak yazılsaydı aynı on
dört bildirim yüz kırk atamayı denetlerdi. **Tipin bedeli cins başına ödenir, kaynak başına değil.**

Üç kusur tipten bağımsız görünür: bozuk söz dizimi, hiçbir bloğa denk gelmeyen başvuru, var olan
bloğun olmayan alanı; bunlar için ad çözümü yeter. **Kalan sekiz kusur tipsiz kayıtta uygulamaya
iner** ve yedisi hata vermeden hedefe farklı bir değer yazar. Tip bildirimi beşini ayrıştırma anına
çeker; uygulamaya inen kusur üçe, sessiz kusur üçe iner. Kazanç mesafedir: ayrıştırmada yakalanan
kusur hiçbir kaynağa dokunmaz.

Sona kalan üçünde değer sabit değil **ifadedir**: altı yerine dokuz yüz makine, otuz gün yerine altı
yüz gün, dokuz yüz milisaniyelik eşik yerine sıfır. Tip üçünün de tabanını doğru bulur, çünkü hepsi
sayıdır; aralığı sınayamaz, çünkü değer kayıt okunurken henüz yoktur. **Kaydın kapsamadığı şey
budur.**

#### İfade Gücü ile Okunabilirlik

```js
// bicem.mjs — ayni hedefin uc yazimi olculur.
import { ayristir } from "./dil.mjs";
import { GOVDE, TIP } from "./kayit.mjs";
import { semaCikar, dugum, derin, sabit, coz, dizinle, cins, uygula, yaz } from "./denet.mjs";

const SEMA = semaCikar(TIP), O = ["bati", "dogu", "merkez"], ADET = { bati: 6, dogu: 6, merkez: 4 };
const ORTA = GOVDE.split("\n}\n")[0] + "\n}";        // kayittaki makine blogu, oldugu gibi
const ortaOrtam = (o) => `ortam "${o}" { bolge = "${o}"  toplayici = ${ADET[o]} }`;
const sikiOrtam = (o) => `ortam "${o}" { bolge = "${o}" }`;
const DUZ = `
kaynak makine "toplayici" {
  ad        = "bati-toplayici"
  sayi      = 6
  cekirdek  = 4
  bellek_gb = 8
  etiket    = { gorev = "sayac-okuma", bolge = "bati" }
}`;
const SIKI = `
yerel "olcek" {
  taban    = 2
  kat      = { bati = 3, dogu = 3, merkez = 2 }
  cekirdek = { bati = 4, dogu = 4, merkez = 4 }
}
kaynak makine "toplayici" {
  ad        = birlestir(ortam.bolge, "-toplayici")
  sayi      = carp(yerel.taban, sec(yerel.kat, ortam.bolge))
  cekirdek  = sec(yerel.cekirdek, ortam.bolge)
  bellek_gb = carp(yerel.taban, carp(sec(yerel.cekirdek, ortam.bolge), 1))
  etiket    = { gorev = "sayac-okuma", bolge = ortam.bolge }
}`;

const satir = (m) => m.split("\n").filter((s) => s.trim()).length;
const sicrama = (e, d) => dugum(e).filter((x) => x.d === "yol")
  .reduce((t, x) => { const c = coz(x.yol, d); return t + 1 + sicrama(c.blok.alanlar[c.alan], d); }, 0);
const es = (m) => JSON.stringify(uygula(ayristir(m), SEMA));

const olc = (ad, metinler, sat, per) => {
  const a = metinler.flatMap((m) => { const b = ayristir(m), d = dizinle(b);
    return b.filter((x) => x.tur === "kaynak").flatMap((k) => Object.entries(k.alanlar).map(([n, e]) =>
      [derin(e), sicrama(e, d), (SEMA[cins(k)][n].aralik || SEMA[cins(k)][n].kume) && !sabit(e) ? 1 : 0])); });
  const en = (i) => Math.max(...a.map((x) => x[i]));
  yaz(14, ad, sat, per, en(0), en(1), a.reduce((s, x) => s + x[2], 0));
};

console.log(`uc yazim ayni hedefi mi uretiyor: ${es(DUZ) === es(ortaOrtam("bati") + ORTA) &&
  O.every((o) => es(sikiOrtam(o) + SIKI) === es(ortaOrtam(o) + ORTA)) ? "evet" : "hayir"}\n`);
yaz(14, "yazim", "satir", "ortam basina", "en derin", "sicrama", "tip goremez");
olc("duz", [DUZ], satir(DUZ) * 3, satir(DUZ));
olc("orta", O.map((o) => ortaOrtam(o) + ORTA), satir(ORTA) + 3, 1);
olc("siki", O.map((o) => sikiOrtam(o) + SIKI), satir(SIKI) + 3, 1);
```

```
uc yazim ayni hedefi mi uretiyor: evet

yazim         satir         ortam basina  en derin      sicrama       tip goremez
duz           21            7             2             0             0
orta          10            1             2             1             3
siki          15            1             4             3             9
```

Üç yazım üç ortamda aynı on beş alanı aynı değerlerle üretir. Düz yazımda sıçrama yoktur ve tipin
göremediği alan sıfırdır; bedeli üç ortam için yirmi bir satır ve **her yeni ortam için yedi
satır**dır: on iki ortamda seksen dört.

Orta yazım aynı hedefi on satıra indirir ve yeni ortamın maliyetini bire düşürür; on iki ortamda on
dokuz satır. Karşılığı bir sıçrama ve tipin göremediği üç alandır. Sıkı yazım ters yöne gider: satır
sayısı on beşe **çıkar**, çünkü haritaları taşıyan ek blok da yazılmıştır; en derin ifade dörde,
sıçrama üçe, tipin göremediği alan dokuza çıkar — on beş alanın dokuzu kayıt okunurken sınanamaz. **İfade gücü tekrarı azaltır, satırı azaltmaz, denetlenebilirliği kesin olarak azaltır.**

#### Özet

- Kurgu kayıt 3 blok, 14 atama, 22 ifade düğümü taşıdı; 7 atama hesaplanandı ve tip 3 cins için 14
  alan bildirimi yazdı.
- Tipsiz kayıtta 11 kusurun 3'ü ayrıştırmada görüldü, **8'i uygulamaya indi**; 7'si hata vermeden
  hedefe farklı değer yazdı.
- Tip bildirimi o 8'in 5'ini ayrıştırma anına çekti; uygulamaya inen kusur 3'e, sessiz kusur 3'e indi.
- Kalan 3 kusurun değeri ifadeydi; 6 yerine 900, 30 gün yerine 600 gün yazıldı, 900 ms eşik 0 oldu.
- Üç yazım 21, 10 ve 15 satır tuttu; sıçrama 0, 1 ve 3, tipin göremediği alan 0, 3 ve 9 oldu; yeni
  ortamın maliyeti 7 satıra karşı 1 satır.

#### Sonraki Adım

Buradaki hedef sistem beş satırlık bir modeldi. Gerçekte bir kaynağın yaratılması, okunması,
güncellenmesi ve silinmesi ayrı davranışlardır ve bunları kayda bağlayan katmanın adı
**sağlayıcı**dır. Bir sonraki ders sağlayıcıyı bir arayüz olarak modeller: hangi alan değişince
kaynak yerinde güncellenemeyip **yeniden oluşturuluyor**, sağlayıcı sürümü değiştiğinde kaç alanın
varsayılanı kayıyor.

### Sağlayıcılar ve Kaynaklar

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/saglayicilar-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.

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

```js
// 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ı

```js
// 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

```js
// 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.

### Veri Kaynakları

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/veri-kaynaklari

Kaydın dışında duran altyapıya okuyarak bağlanmanın bedeli sayılır: kaç veri kaynağı, günde kaç okuma, ne kadar süre ve okunan bir değer değiştiğinde kaç kaynağın yeniden hesaplanıp kaçının yeniden oluşturulduğu. Sapma taramasının kaydın yönettiği kalemlerle sınırlı kalması ve kayıt dışı bir değişikliğin taramada neden hiç görünmediği ölçülür.

Bir önceki ders hedefteki her kaynağı kaydın kurduğunu varsaydı. Gerçek bir ortamda bu doğru
değildir: ağın omurgası, temel imaj, kimlik tanımları, sertifikalar ve bölge kotaları çoğunlukla
başka ekiplerin elindedir. Kayıt onları yaratmaz, silmez, güncellemez — yalnız **okur**. Okuyan
yapının adı **veri kaynağı**dır ve bedeli üç ayrı yerde birikir: her plan koşumunda yapılan okuma
sayısı ve süresi, okunan bir değer değiştiğinde tetiklenen yeniden hesaplama ve sapma taramasının
bu kalemleri hiç görmemesi.

Ölçüm **bölgesel ölçüm ağının** batı ortamı üzerindedir ve **kurgudur**; kayıt dışı altyapı, veri
kaynakları, okuma süreleri ve bağımlılık yayılımı `node` ile kurulmuş bir **modeldir**.

**BS13.** Kayıt sekiz kaynağı yönetir; beş veri kaynağı kaydın dışındaki kalemleri okur ve bu
kalemlerin sahibi başka ekiplerdir.
**BS14.** Bir veri kaynağı okunduğunda bütün alanlarını getirir; alan tek tek seçilemez.
**BS15.** Her plan koşumu her veri kaynağını bir kez okur ve sonuç önbelleğe alınmaz.
**BS16.** Hat günde altı, elle plan günde dört kez koşar.
**BS17.** Okunan bir değer değiştiğinde onu girdi alan her kaynak yeniden hesaplanır; değer o
kaynağın zorlayan listesindeyse kaynak yeniden oluşturulur ve yeniden oluşturma aşağıya yayılır.
**BS18.** Sapma taraması yalnız kaydın yönettiği kaynakları hedefteki karşılıklarıyla karşılaştırır.

#### Kaydın Dışındaki Altyapı

```js
// dis.mjs — kaydin disinda duran kurgu altyapi ve onu okuyan veri kaynaklari (model).
export const DIS = {                                // baska ekiplerin yonettigi kalemler
  ag: { alt_ag: "alt-ag-7", mtu: 1500, gecit: "gecit-a" },
  imaj: { surum: "temel-2", yas_gun: 34 },
  kimlik: { izin: 12 },
  sertifika: { bitis_gun: 41 },
  kota: { makine_ust: 24, kuyruk_ust: 16 },
};
export const OKUMA_MS = { ag: 120, imaj: 340, kimlik: 90, sertifika: 210, kota: 60 };
export const SAHIP = { ag: "ag ekibi", imaj: "imaj ekibi", kimlik: "guvenlik ekibi",
  sertifika: "guvenlik ekibi", kota: "bulut yonetimi" };

export function okuyucuYap() {                      // her okuma sayilir, suresi toplanir
  const sayac = {}, sure = { ms: 0 };
  return { sayac, sure,
    oku(ad) { sayac[ad] = (sayac[ad] || 0) + 1; sure.ms += OKUMA_MS[ad]; return { ...DIS[ad] }; },
    toplam: () => Object.values(sayac).reduce((a, b) => a + b, 0) };
}
```

```js
// kayit.mjs — kaydin yonettigi kaynaklar; girdi listesi neyi okuduklarini, zorlayan hangi
// girdinin degismesinin kaynagi yeniden olusturdugunu soyler.
export const KAYNAK = {
  "makine.toplayici": { girdi: ["ag.alt_ag", "imaj.surum", "kota.makine_ust"],
    zorlayan: ["ag.alt_ag", "imaj.surum"] },
  "makine.dogrulayici": { girdi: ["ag.alt_ag", "imaj.surum"], zorlayan: ["ag.alt_ag", "imaj.surum"] },
  "kuyruk.sayac": { girdi: ["kota.kuyruk_ust", "ag.alt_ag"], zorlayan: ["ag.alt_ag"] },
  "kuyruk.uyari": { girdi: ["kota.kuyruk_ust"], zorlayan: [] },
  "veri_deposu.olcum": { girdi: ["ag.alt_ag", "makine.toplayici"], zorlayan: ["ag.alt_ag"] },
  "ag_kurali.toplayici": { girdi: ["ag.gecit", "makine.toplayici"],
    zorlayan: ["ag.gecit", "makine.toplayici"] },
  "uc_nokta.olcum": { girdi: ["sertifika.bitis_gun", "veri_deposu.olcum"], zorlayan: ["veri_deposu.olcum"] },
  "kimlik_baglama.toplayici": { girdi: ["kimlik.izin", "makine.toplayici"], zorlayan: ["makine.toplayici"] },
};

export function yayil(kok) {                        // bir deger degisince kim yeniden hesaplanir
  const hesap = new Set(), yeni = new Set(), sira = [[kok, true]];
  while (sira.length) {
    const [x, xYeni] = sira.shift();
    for (const [k, r] of Object.entries(KAYNAK)) {
      if (!r.girdi.includes(x) || hesap.has(k)) continue;
      hesap.add(k);
      const kYeni = xYeni && r.zorlayan.includes(x);
      if (kYeni) yeni.add(k);
      sira.push([k, kYeni]);
    }
  }
  return { hesap, yeni };
}
```

İki dosya iki ayrı dünyayı tanımlar. Birincisi kaydın yönetmediği kalemleri ve her birinin sahibini
tutar; ikincisi kaydın yönettiği kaynakları ve her birinin hangi dış değeri okuduğunu. Aradaki tek
bağ okuma yönündedir: kayıt dış dünyadan değer alır, dış dünyaya değer yazmaz. Bu tek yönlülük
dersin bütün ölçülerinin kaynağıdır.

#### Okumanın Bedeli ve Yayılması

```js
// olc.mjs — okumanin bedeli, bir dis degerin yayilmasi ve sapma taramasinin kapsami.
import { DIS, OKUMA_MS, SAHIP, okuyucuYap } from "./dis.mjs";
import { KAYNAK, yayil } from "./kayit.mjs";

const disAlan = new Set(Object.entries(DIS).flatMap(([a, v]) => Object.keys(v).map((f) => `${a}.${f}`)));
const kullanilan = new Set(Object.values(KAYNAK).flatMap((r) => r.girdi).filter((g) => disAlan.has(g)));
const yaz = (...s) => console.log(s.map((x, i) => String(x).padEnd(i ? 13 : 26)).join("").trimEnd());

console.log(`kayit ${Object.keys(KAYNAK).length} kaynak yonetiyor; ` +
  `${Object.keys(DIS).length} veri kaynagi ${disAlan.size} dis alan getiriyor, ` +
  `${kullanilan.size} tanesi gercekten kullaniliyor`);
yaz("veri kaynagi", "alan", "kullanilan", "okuma ms", "duran kaynak", "sahip");
for (const [a, v] of Object.entries(DIS)) {
  const alan = Object.keys(v).filter((f) => kullanilan.has(`${a}.${f}`));
  const duran = new Set(alan.flatMap((f) => [...yayil(`${a}.${f}`).hesap]));
  yaz(a, Object.keys(v).length, alan.length, OKUMA_MS[a], duran.size, SAHIP[a]);
}

const PLAN_GUNLUK = 10;                             // gunde 6 hat kosumu + 4 elle plan
const o = okuyucuYap();
for (let i = 0; i < PLAN_GUNLUK; i++) for (const a of Object.keys(DIS)) o.oku(a);
console.log(`\ngunde ${PLAN_GUNLUK} plan: ${o.toplam()} dis okuma, ${(o.sure.ms / 1000).toFixed(1)} sn; ` +
  `plan basina ${Object.keys(DIS).length} dis + ${Object.keys(KAYNAK).length} kaynak okumasi`);
console.log(`yilda ${o.toplam() * 365} dis okuma, ${((o.sure.ms * 365) / 60000).toFixed(0)} dakika`);
const tekSure = Object.values(OKUMA_MS).reduce((a, b) => a + b, 0);
console.log(`gunde bir kez onbelleklense: ${Object.keys(DIS).length} okuma, ${(tekSure / 1000).toFixed(2)} sn; ` +
  `dis degisikligin gorunmesi icin beklenen sure ${(24 / PLAN_GUNLUK).toFixed(1)} saat yerine 24 saat`);

console.log(`\ndegisen dis deger        okuyan  yeniden hesaplanan  yeniden olusturulan`);
for (const alan of [...kullanilan].sort()) {
  const { hesap, yeni } = yayil(alan);
  const dogrudan = Object.values(KAYNAK).filter((r) => r.girdi.includes(alan)).length;
  console.log(`  ${alan.padEnd(23)}${String(dogrudan).padStart(4)}` +
    `${String(hesap.size).padStart(18)}${String(yeni.size).padStart(20)}`);
}

const SAPAN = ["ag.alt_ag", "kota.makine_ust"];     // dis kalemde birileri degisiklik yapti
const etki = SAPAN.reduce((s, a) => { const { hesap, yeni } = yayil(a);
  hesap.forEach((x) => s.hesap.add(x)); yeni.forEach((x) => s.yeni.add(x)); return s; },
  { hesap: new Set(), yeni: new Set() });
const kalem = Object.keys(KAYNAK).length + Object.keys(DIS).length;
console.log(`\nsapma taramasi ${Object.keys(KAYNAK).length}/${kalem} kalemi karsilastiriyor ` +
  `(kapsam %${Math.round((Object.keys(KAYNAK).length / kalem) * 100)}); ` +
  `${Object.keys(DIS).length} veri kaynagi hic karsilastirilmiyor`);
console.log(`${SAPAN.length} dis alan degisti -> tarama sonucu "sapma yok"; ` +
  `sonraki plan ${etki.hesap.size} kaynagi yeniden hesaplar, ${etki.yeni.size} kaynagi yeniden olusturur`);
```

```
kayit 8 kaynak yonetiyor; 5 veri kaynagi 9 dis alan getiriyor, 7 tanesi gercekten kullaniliyor
veri kaynagi              alan         kullanilan   okuma ms     duran kaynak sahip
ag                        3            2            120          7            ag ekibi
imaj                      2            1            340          6            imaj ekibi
kimlik                    1            1            90           1            guvenlik ekibi
sertifika                 1            1            210          1            guvenlik ekibi
kota                      2            2            60           7            bulut yonetimi

gunde 10 plan: 50 dis okuma, 8.2 sn; plan basina 5 dis + 8 kaynak okumasi
yilda 18250 dis okuma, 50 dakika
gunde bir kez onbelleklense: 5 okuma, 0.82 sn; dis degisikligin gorunmesi icin beklenen sure 2.4 saat yerine 24 saat

degisen dis deger        okuyan  yeniden hesaplanan  yeniden olusturulan
  ag.alt_ag                 4                 7                   7
  ag.gecit                  1                 1                   1
  imaj.surum                2                 6                   4
  kimlik.izin               1                 1                   0
  kota.kuyruk_ust           2                 2                   0
  kota.makine_ust           1                 5                   0
  sertifika.bitis_gun       1                 1                   0

sapma taramasi 8/13 kalemi karsilastiriyor (kapsam %62); 5 veri kaynagi hic karsilastirilmiyor
2 dis alan degisti -> tarama sonucu "sapma yok"; sonraki plan 7 kaynagi yeniden hesaplar, 7 kaynagi yeniden olusturur
```

Birinci tablo bağın genişliğini verir. Beş veri kaynağı toplam dokuz dış alan getirir, ama kaydın
gerçekten kullandığı alan yedidir: ağın azami paket boyutu ile imajın yaşı okunur, hiçbir kaynağa
girmez. Bu, veri kaynağının alan seçemeyen yapısından gelir — okuma kalem başınadır, alan başına
değil. Okuma süreleri de eşit değildir: imaj kaynağı üç yüz kırk milisaniye, kota kaynağı altmış
milisaniye sürer; en pahalı kaynak en az kullanılan alanı getirendir.

İkinci ölçü tekrar sıklığıdır. Günde on plan koşumu **elli dış okuma** ve 8,2 saniye eder;
yılda on sekiz bin iki yüz elli okuma ve elli dakika. Bu süre tek başına küçüktür, ama iki şeyi
gizler. Birincisi, her plan aynı zamanda sekiz yönetilen kaynağı da okur; plan üretmenin maliyeti
kaynak sayısıyla değil, kaynak artı veri kaynağı sayısıyla büyür. İkincisi, okumanın ucuzlatılması
tazeliğe mal olur: günde bir kez önbelleğe alınırsa okuma elliden beşe, süre 8,2 saniyeden
sekiz yüz milisaniyeye iner, ama dış bir değişikliğin fark edilmesi için beklenen süre iki buçuk
saatten yirmi dört saate çıkar. **Okuma sıklığı doğrudan bir tazelik ölçüsüdür ve düşürülmesi
sapmanın görünmezlik penceresini uzatır.**

Üçüncü tablo yayılmayı verir ve dersin en sert sayısı buradadır. Alt ağ kimliğini dört kaynak
doğrudan okur, ama o değer değiştiğinde **yedi kaynak yeniden hesaplanır ve yedisi de yeniden
oluşturulur** — sekiz kaynaklı bir kayıtta neredeyse tamamı. Yayılmanın nedeni zincirdir: alt ağ
değişince toplayıcı makineler yeniden oluşturulur, onların kimliği değişince ağ kuralı ve kimlik
bağlaması yeniden oluşturulur, veri deposu yeniden oluşturulunca uç nokta da yeniden oluşturulur.
İmaj sürümü altı kaynağı yeniden hesaplatır, dördünü yeniden oluşturur. Buna karşılık kota üst
sınırı beş kaynağı yeniden hesaplatır ama **hiçbirini yeniden oluşturmaz**; sertifika ile kimlik
izni birer kaynakta kalır. Aynı tabloda okunan bir değerin ucuz mu pahalı mı olduğu görünür:
belirleyici olan kaç kaynağın okuduğu değil, okunan alanın zorlayan listelerinde geçip geçmediğidir.

Dördüncü sütun kesinti riskini verir. Ağ ya da kota kaynağı okunamaz hâle gelirse yedişer kaynak
planlanamaz; imaj kaynağı için altı, kimlik ve sertifika için birer kaynak. **Kayıt, yönetmediği
beş kalemin erişilebilirliğine bağımlıdır ve bu bağımlılığın sahibi kayıt değildir.** Sahipler
sütunu bunu adıyla yazar: ağ ekibi, imaj ekibi, güvenlik ekibi, bulut yönetimi. Kaydı değiştirmeye
yetkili ekip ile kaydın okuduğu değeri değiştirmeye yetkili ekip aynı değildir.

#### Taramanın Görmediği Yer

Son iki satır kaydın kapsamını sayar. Sapma taraması on üç kalemin sekizini karşılaştırır; kapsam
yüzde altmış ikidir. Karşılaştırılmayan beş kalem, kaydın okuduğu ama yönetmediği kalemlerdir ve
karşılaştırılmamalarının nedeni basittir: **tarama, kaydın söylediği ile gerçeği karşılaştırır;
kayıt bu beş kalem hakkında hiçbir şey söylemez.** Kayıtta istenen bir değer yoksa, gerçeğin ondan
saptığı da söylenemez.

Bunun sonucu son satırdadır. Ağ ekibi alt ağ kimliğini, bulut yönetimi de makine üst sınırını
değiştirir. Sapma taraması koşar ve **sapma yok** der; yönettiği sekiz kaynağın hepsi kayıttaki
hâliyle uyumludur, çünkü hiçbiri değişmemiştir. Aynı anda bir sonraki plan koşumu yedi kaynağı
yeniden hesaplayacak ve yedisini birden yeniden oluşturacaktır. İki cümle aynı ortam için aynı anda
doğrudur: sapma yoktur ve altyapının yedi kalemi değişmek üzeredir. Aradaki fark, sapmanın kaydın
kapsamı içinde tanımlı olmasıdır — **kaydın kapsamadığı bir değişiklik sapma değildir, sürprizdir.**

Bu ölçünün pratik karşılığı, dış kalemlerin sayısını ve yayılma çapını bilmektir. Yedi kullanılan
dış alandan ikisi tek başına yedi kaynağı yeniden oluşturabilecek durumdadır; üçü hiçbir yeniden
oluşturma tetiklemez. Okunan her yeni dış değer taramanın kapsamını düşürür ve sürpriz yüzeyini
büyütür; okunmayan her dış değer ise kayda elle kopyalanmak zorundadır ve bu kez kopyanın kendisi
sapar.

#### Okumak mı, Kopyalamak mı

```js
// kopya.mjs — dis degeri okumak ile kayda kopyalamak: 90 gunluk kurgu takvim (model).
import { DIS } from "./dis.mjs";
import { KAYNAK, yayil } from "./kayit.mjs";

const TAKVIM = [                                    // dis kalemlerde kim ne zaman degisiklik yapti
  [12, "imaj.surum"], [30, "kota.makine_ust"], [40, "ag.alt_ag"],
  [55, "imaj.surum"], [70, "kota.makine_ust"], [78, "ag.gecit"],
];
const TAZELEME = [30, 60, 90], GUN = 90;            // kopyayi elle gozden gecirme gunleri
const disAlan = new Set(Object.entries(DIS).flatMap(([a, v]) => Object.keys(v).map((f) => `${a}.${f}`)));
const kopya = new Set(Object.values(KAYNAK).flatMap((r) => r.girdi).filter((g) => disAlan.has(g)));

let okuYeni = 0, kopyaYeni = 0, yanlisGun = 0, gecikme = 0;
for (const [gun, alan] of TAKVIM) {
  const { hesap, yeni } = yayil(alan), t = TAZELEME.find((g) => g >= gun) ?? GUN;
  okuYeni += yeni.size; kopyaYeni += yeni.size;
  gecikme += t - gun; yanlisGun += (t - gun) * hesap.size;
}
console.log(`${GUN} gunde ${TAKVIM.length} dis degisiklik; kopyalanacak alan ${kopya.size}`);
console.log(`okuyarak    : gecikme 0 gun, ${okuYeni} yeniden olusturma, kayit-gercek uyumsuz 0 kaynak-gun`);
console.log(`kopyalayarak: ortalama gecikme ${(gecikme / TAKVIM.length).toFixed(1)} gun, ` +
  `${kopyaYeni} yeniden olusturma, uyumsuz ${yanlisGun} kaynak-gun`);
console.log(`her iki yolda da sapma taramasinin karsilastirdigi kalem sayisi ${Object.keys(KAYNAK).length}; ` +
  `${Object.keys(DIS).length} dis kalem iki yolda da disarida kalir`);
```

```
90 gunde 6 dis degisiklik; kopyalanacak alan 7
okuyarak    : gecikme 0 gun, 16 yeniden olusturma, kayit-gercek uyumsuz 0 kaynak-gun
kopyalayarak: ortalama gecikme 12.5 gun, 16 yeniden olusturma, uyumsuz 390 kaynak-gun
her iki yolda da sapma taramasinin karsilastirdigi kalem sayisi 8; 5 dis kalem iki yolda da disarida kalir
```

Okumanın alternatifi, dış değerleri kayda elle yazmaktır. Kurgu takvim doksan günde altı dış
değişiklik içerir ve iki yol da sonunda aynı on altı yeniden oluşturmayı üretir — dış dünya
değiştiyse altyapı er ya da geç değişecektir. Fark zamanlamadadır. Okuyan kayıt değişikliği bir
sonraki plan koşumunda alır; kopyalayan kayıt ayda bir gözden geçirmeye bağlıdır ve ortalama **on
iki buçuk gün** geriden gelir. Bu gecikmenin karşılığı **üç yüz doksan kaynak-gündür**: kaydın
gerçeği yanlış anlattığı süre boyunca kaç kaynağın etkilendiğinin toplamı.

Kopyalamanın görünen kazancı okuma yükünün sıfırlanmasıdır; görünmeyen bedeli, yedi alanın kayıtta
donmuş bir kopyasının tutulmasıdır. Bu kopya sapma taramasına girer, ama taramanın karşılaştırdığı
şey kaydın kendi kopyasıyla ondan kurulmuş kaynaktır — dış kalemin gerçek değeri iki yolda da
karşılaştırılmaz. **Kopyalamak taramanın kapsamını genişletmez, yalnız sapmanın adını değiştirir:**
okuyan kayıtta sürpriz, kopyalayan kayıtta eskimiş kayıt.

#### Özet

- Kayıt 8 kaynak yönetti; 5 veri kaynağı 9 dış alan getirdi, bunların yalnız 7'si kullanıldı — 2
  alan her plan koşumunda okundu ama hiçbir kaynağa girmedi.
- Günde 10 plan koşumu 50 dış okuma ve 8,2 saniye tuttu; yılda 18.250 okuma ve 50 dakika. Günde bir
  önbellekleme okumayı 5'e indirir, görünmezlik penceresini 2,4 saatten 24 saate çıkarır.
- Alt ağ kimliği değişince 8 kaynağın 7'si yeniden hesaplandı ve **7'si yeniden oluşturuldu**; imaj
  sürümü 6 kaynağı hesaplattı, 4'ünü yeniden oluşturdu. Kota üst sınırı 5 kaynağı hesaplattı ama
  hiçbirini yeniden oluşturmadı: belirleyici olan okuyan sayısı değil, alanın zorlayan listesinde
  geçip geçmediğidir.
- Ağ ya da kota kaynağı okunamazsa 7'şer kaynak planlanamaz; kayıt, sahibi başka ekipler olan 5
  kalemin erişilebilirliğine bağımlıdır.
- Sapma taraması 13 kalemin 8'ini karşılaştırdı (kapsam %62). 2 dış alan değiştiğinde tarama "sapma
  yok" dedi, aynı anda sonraki plan 7 kaynağı yeniden oluşturacak durumdaydı.
- 90 günde 6 dış değişiklik iki yolda da 16 yeniden oluşturma üretti; kopyalayan kayıt ortalama 12,5
  gün geriden geldi ve 390 kaynak-gün boyunca gerçeği yanlış anlattı.

#### Sonraki Adım

Buraya kadar kayıt tek bir ortamı anlattı ve okuduğu değerler dışarıdan geldi. Aynı kaydın üç
ortamda koşması gerektiğinde soru değişir: hangi değer ortama göre değişmeli, hangisi her ortamda
aynı kalmalı ve bu ayrımı kim yazacak. Bir sonraki ders parametreleştirmeyi ölçer: aynı tanım üç
ortama uygulanınca kaç değer ortama göre değişiyor, kaçı değişmemesi gerektiği hâlde değişebilir
durumda duruyor, girdi yüzeyi büyüdükçe yanlış değer verme olasılığı nasıl artıyor ve çıktılar
başka tanımlara kaç bağımlılık yaratıyor.

### Değişkenler, Yerel Değerler ve Çıktılar

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/degiskenler-yerel-degerler-ve-ciktilar

Aynı tanım üç ortama uygulanır ve parametreleştirmenin bilançosu sayılır: kaç değer ortama göre gerçekten değişiyor, kaçı her ortamda aynı yazılıyor, kaç değişken hiç kullanılmıyor. Girdi yüzeyi büyüdükçe yanlış değer verme olasılığı, doğrulamanın yakaladığı ile yakalayamadığı yanlış sayısıyla ölçülür ve çıktıların başka tanımlarda yarattığı bağımlılık sayılır.

Önceki dersler tek bir ortamın kaydını ele aldı. Aynı tanım üç ortamda koşacaksa iki soru doğar:
hangi değer ortama göre değişmeli, hangisi her ortamda aynı kalmalı. Bunu yazan yapı üç parçalıdır —
dışarıdan değer alan **değişkenler**, değişkenlerden türetilen **yerel değerler** ve tanımın
dışarıya verdiği **çıktılar**. Bu ders üçünü de sayar: girdi yüzeyinin ne kadarının gerçekten
ortama özgü olduğunu, yüzey büyüdükçe yanlış değer verme olasılığının nereye gittiğini ve
çıktıların başka tanımlarda kaç bağımlılık yarattığını.

Ölçüm **bölgesel ölçüm ağının** üç ortamı üzerindedir ve **kurgudur**; tanım, doğrulama ve tüketen
tanımlar `node` ile kurulmuş bir **modeldir**.

**BS19.** Tanım on dört değişken, üç yerel değer, dört kaynak ve beş çıktı taşır.
**BS20.** Varsayılanı olmayan değişken zorunludur; bir ortam değer vermezse varsayılan sessizce
kullanılır.
**BS21.** Doğrulama yalnız türü ve değer aralığını ya da sıralı küme üyeliğini sınar; değerin o
ortam için doğru olup olmadığını sınamaz.
**BS22.** Bir değişken, doğrudan ya da bir yerel değer üzerinden okunuyorsa kullanılmış sayılır;
hiç okunmayan değişken **ölü**dür.
**BS23.** On yanlış girdi önceden seçilmiştir; her biri gerçekte sık yapılan bir yazım ya da birim
karışıklığıdır.
**BS24.** Bir çıktı değişirse onu okuyan her tanım yeniden planlanır.

#### Tanım ve Üç Ortam

```js
// tanim.mjs — uc ortama uygulanan kurgu tanim: degiskenler, yerel degerler, kaynaklar, ciktilar.
const D = (tur, dogrula, varsayilan) => ({ tur, dogrula, varsayilan });
const kume = (...k) => (v) => k.includes(v);
const arasi = (a, b) => (v) => v >= a && v <= b;

export const DEGISKEN = {                           // varsayilani olmayan degisken zorunludur
  bolge: D("dize", kume("bati", "dogu", "merkez")),
  toplayici: D("sayi", arasi(1, 64)),
  kuyruk_bolme: D("sayi", arasi(1, 128)),
  disk_gb: D("sayi", arasi(20, 4000)),
  saklama_gn: D("sayi", arasi(1, 365), 30),
  esik_ms: D("sayi", arasi(50, 5000), 900),
  cekirdek: D("sayi", arasi(1, 64), 4),
  bellek_gb: D("sayi", arasi(1, 256), 8),
  bicim: D("dize", kume("ikili", "metin"), "ikili"),
  izleme: D("dize", kume("acik", "kapali"), "acik"),
  yedek_gn: D("sayi", arasi(0, 35), 1),
  etiket_sahip: D("dize", kume("olcum-ekibi", "ag-ekibi"), "olcum-ekibi"),
  sertifika_gun: D("sayi", arasi(1, 365), 90),
  eski_bayrak: D("dize", kume("acik", "kapali"), "kapali"),
};

export const YEREL = {                              // degiskenlerden turetilen ara degerler
  ad_onek: { girdi: ["bolge"], hesap: (d) => `${d.bolge}-olcum` },
  toplam_cekirdek: { girdi: ["toplayici", "cekirdek"], hesap: (d) => d.toplayici * d.cekirdek },
  kuyruk_adi: { girdi: ["bolge"], hesap: (d) => `${d.bolge}-sayac-kuyrugu` },
};

export const KAYNAK = {                             // her kaynagin okudugu degisken ve yerel deger
  "makine.toplayici": ["bolge", "toplayici", "cekirdek", "bellek_gb", "disk_gb", "izleme",
    "etiket_sahip", "yerel.toplam_cekirdek"],
  "kuyruk.sayac": ["kuyruk_bolme", "saklama_gn", "bicim", "etiket_sahip", "yerel.kuyruk_adi"],
  "veri_deposu.olcum": ["esik_ms", "yedek_gn", "disk_gb", "yerel.ad_onek"],
  "nesne_deposu.arsiv": ["saklama_gn", "yerel.ad_onek"],
};

export const ORTAM = {                              // uc ortamin verdigi degerler
  bati: { bolge: "bati", toplayici: 6, kuyruk_bolme: 12, disk_gb: 200, saklama_gn: 30,
    esik_ms: 900, cekirdek: 4, bellek_gb: 8 },
  dogu: { bolge: "dogu", toplayici: 6, kuyruk_bolme: 12, disk_gb: 200, saklama_gn: 30,
    esik_ms: 900, cekirdek: 4, bellek_gb: 8 },
  merkez: { bolge: "merkez", toplayici: 2, kuyruk_bolme: 4, disk_gb: 60, saklama_gn: 7,
    esik_ms: 900, cekirdek: 4, bellek_gb: 8 },
};

export const CIKTI = {                              // baska tanimlarin okudugu degerler
  kuyruk_adi: "yerel.kuyruk_adi", depo_onek: "yerel.ad_onek",
  toplam_cekirdek: "yerel.toplam_cekirdek", sahip: "etiket_sahip", arsiv_gun: "saklama_gn",
};
export const TUKETEN = {                            // hangi tanim hangi ciktiyi okuyor
  "hat-tanimi": ["kuyruk_adi", "depo_onek"],
  "uyari-tanimi": ["kuyruk_adi", "arsiv_gun"],
  "rapor-tanimi": ["depo_onek", "sahip"],
};

export function coz(veren) {                        // etkin deger: verilen ya da varsayilan
  const d = {};
  for (const [ad, t] of Object.entries(DEGISKEN)) d[ad] = ad in veren ? veren[ad] : t.varsayilan;
  for (const [ad, y] of Object.entries(YEREL)) d[`yerel.${ad}`] = y.hesap(d);
  return d;
}
```

Değişken bildirimi üç şey taşır: tür, doğrulama ve varsayılan. Yerel değer bir hesaptır ve girdisini
adıyla yazar; kaynaklar hem değişkenleri hem yerel değerleri okur. Çıktı ise yalnız bir yönlendirme
tablosudur — tanımın içindeki bir değeri dışarıya adlandırarak açar.

#### Girdi Yüzeyinin Bilançosu

```js
// olc.mjs — girdi yuzeyi, ortama gore degisen ve degismeyen degerler, olu degisken, cikti bagimliligi.
import { DEGISKEN, YEREL, KAYNAK, ORTAM, CIKTI, TUKETEN, coz } from "./tanim.mjs";

const ORT = Object.keys(ORTAM), cozum = Object.fromEntries(ORT.map((o) => [o, coz(ORTAM[o])]));
const zorunlu = Object.entries(DEGISKEN).filter(([, t]) => t.varsayilan === undefined).map(([a]) => a);
const kullanilan = new Set([...Object.values(KAYNAK).flat(),
  ...Object.values(YEREL).flatMap((y) => y.girdi)]);
const olu = Object.keys(DEGISKEN).filter((a) => !kullanilan.has(a));
const verilen = Object.keys(DEGISKEN).filter((a) => ORT.every((o) => a in ORTAM[o]));
const farkli = verilen.filter((a) => new Set(ORT.map((o) => ORTAM[o][a])).size > 1);

console.log(`degisken ${Object.keys(DEGISKEN).length}: ${zorunlu.length} zorunlu, ` +
  `${Object.keys(DEGISKEN).length - zorunlu.length} varsayilanli, ${olu.length} olu (${olu.join(", ")})`);
console.log(`uc ortam ${verilen.length * ORT.length} deger yaziyor: ` +
  `${farkli.length * ORT.length} tanesi ortama gore farkli (${farkli.join(", ")}), ` +
  `${(verilen.length - farkli.length) * ORT.length} tanesi uc ortamda ayni ` +
  `(${verilen.filter((a) => !farkli.includes(a)).join(", ")})`);
console.log(`hic verilmeyip varsayilanda kalan degisken: ` +
  `${Object.keys(DEGISKEN).length - verilen.length}\n`);

const yaz = (...s) => console.log(s.map((x, i) => String(x).padEnd(i ? 15 : 20)).join("").trimEnd());
yaz("kaynak", "girdi", "ortama gore", "hep ayni");
for (const [k, girdiler] of Object.entries(KAYNAK)) {
  const degisen = girdiler.filter((g) => new Set(ORT.map((o) => cozum[o][g])).size > 1);
  yaz(k, girdiler.length, degisen.length, girdiler.length - degisen.length);
}

const girdiler = [...new Set(Object.values(KAYNAK).flat())];
const degisenGirdi = girdiler.filter((g) => new Set(ORT.map((o) => cozum[o][g])).size > 1);
console.log(`  toplam ${girdiler.length} benzersiz girdi, ${degisenGirdi.length} tanesi ortama gore degisiyor`);

const eksik = { ...ORTAM.merkez };                  // merkez ortami bir degeri vermeyi unutursa
delete eksik.saklama_gn;
const c2 = coz(eksik), kayan = Object.keys(cozum.merkez).filter((k) => cozum.merkez[k] !== c2[k]);
console.log(`\nmerkez saklama_gn vermezse varsayilan ${DEGISKEN.saklama_gn.varsayilan} gecerli olur ` +
  `(ortamin degeri ${ORTAM.merkez.saklama_gn}): ${kayan.length} deger kayar, ` +
  `${Object.entries(KAYNAK).filter(([, g]) => g.some((x) => kayan.includes(x))).length} kaynak etkilenir, ` +
  `dogrulama hicbir sey soylemez`);

console.log(`\ncikti ${Object.keys(CIKTI).length}, tuketen tanim ${Object.keys(TUKETEN).length}`);
for (const c of Object.keys(CIKTI)) {
  const t = Object.entries(TUKETEN).filter(([, l]) => l.includes(c)).map(([a]) => a);
  console.log(`  ${c.padEnd(17)}${t.length} tuketen  ${t.join(", ") || "(olu cikti)"}`);
}
const bagli = Object.values(TUKETEN).flat().length;
console.log(`toplam bagimlilik ${bagli}; bir ciktinin degeri degisirse ortalama ` +
  `${(bagli / Object.keys(CIKTI).length).toFixed(1)} tanim yeniden planlanir`);
```

```
degisken 14: 4 zorunlu, 10 varsayilanli, 2 olu (sertifika_gun, eski_bayrak)
uc ortam 24 deger yaziyor: 15 tanesi ortama gore farkli (bolge, toplayici, kuyruk_bolme, disk_gb, saklama_gn), 9 tanesi uc ortamda ayni (esik_ms, cekirdek, bellek_gb)
hic verilmeyip varsayilanda kalan degisken: 6

kaynak              girdi          ortama gore    hep ayni
makine.toplayici    8              4              4
kuyruk.sayac        5              3              2
veri_deposu.olcum   4              2              2
nesne_deposu.arsiv  2              2              0
  toplam 15 benzersiz girdi, 8 tanesi ortama gore degisiyor

merkez saklama_gn vermezse varsayilan 30 gecerli olur (ortamin degeri 7): 1 deger kayar, 2 kaynak etkilenir, dogrulama hicbir sey soylemez

cikti 5, tuketen tanim 3
  kuyruk_adi       2 tuketen  hat-tanimi, uyari-tanimi
  depo_onek        2 tuketen  hat-tanimi, rapor-tanimi
  toplam_cekirdek  0 tuketen  (olu cikti)
  sahip            1 tuketen  rapor-tanimi
  arsiv_gun        1 tuketen  uyari-tanimi
toplam bagimlilik 6; bir ciktinin degeri degisirse ortalama 1.2 tanim yeniden planlanir
```

İlk satır parametreleştirmenin sessiz maliyetini verir. On dört değişkenin dördü zorunlu, onunun
varsayılanı var; ikisi ise **ölü** — hiçbir kaynak ve hiçbir yerel değer onları okumuyor. Ölü
değişken zararsız görünür ama girdi yüzeyinin bir parçasıdır: belgede durur, gözden geçirmede
okunur, yeni bir ortam eklenirken doldurulup doldurulmayacağı tartışılır ve hiçbir şeyi
değiştirmez.

İkinci satır dersin ana ölçüsüdür. Üç ortam toplam **yirmi dört değer** yazar. Bunların **on beşi
ortama göre gerçekten farklıdır** — bölge, toplayıcı sayısı, kuyruk bölmesi, disk boyutu, saklama
süresi. Kalan **dokuzu üç ortamda da aynıdır**: eşik süresi, çekirdek ve bellek her ortamda aynı
değerle yazılmıştır. Bu dokuz değer değişken olmak zorunda değildir; tanımın içinde sabit ya da
varsayılan olarak durabilirdi. Aralarındaki fark tek bir şeyde görünür: **her biri üç kez elle
yazılmıştır ve üçünün de yanlış yazılma ihtimali vardır.** Altı değişken ise hiçbir ortamda
verilmez ve varsayılanında kalır; bu altısı zaten sabittir, ama kayıtta değişken olarak
görünürler.

Üçüncü tablo yükün kaynaklara dağılımını verir. Toplayıcı makinelerin sekiz girdisinin dördü ortama
göre değişir, dördü hep aynıdır; arşiv nesne deposunun iki girdisinin ikisi de ortama göre değişir.
Bir kaynağın ortamlar arasında ne kadar farklılaştığı, okuduğu girdilerin kaçının gerçekten
değiştiğine bağlıdır — kaynak sayısına değil. Toplamda dört kaynak on beş benzersiz girdi okur ve
bunların **sekizi** ortama göre değişir; yani üç ortam arasındaki bütün fark sekiz noktada
toplanmıştır. Parametreleştirmenin gerçek genişliği budur, on dört değişken değil.

Sonraki satır varsayılanın sessiz yüzünü gösterir. Merkez ortamı saklama süresini vermeyi unutursa
değer boş kalmaz: tanımdaki varsayılan otuz gün sessizce yürürlüğe girer, oysa o ortamın istediği
yedi gündür. Bir değer kayar, **iki kaynak yanlış kurulur** ve doğrulama hiçbir şey söylemez, çünkü
otuz gün tür ve aralık bakımından kusursuzdur. Varsayılan, eksik girdiyi hataya çevirmek yerine
sessiz bir karara çevirir; bu yüzden ortama göre değişmesi gereken bir değişkene varsayılan vermek,
o değişkeni zorunlu olmaktan çıkarmakla aynı şeydir.

Son bölüm çıktıları sayar. Beş çıktıdan dördü tüketiliyor, biri **ölü çıktı**: toplam çekirdek
sayısını hiçbir tanım okumuyor. Üç tüketen tanım toplam altı bağımlılık kuruyor; kuyruk adı ile
depo öneki ikişer tanım tarafından okunuyor. Bunun anlamı şudur: kuyruk adını değiştiren bir
değişiklik kendi tanımını değil, **iki başka tanımı da** yeniden planlanmaya zorlar. Çıktı, tanımın
dışarıya verdiği bir söz haline gelir ve sözün maliyeti onu okuyan tanım sayısı kadardır.

#### Yanlış Değer Verme

```js
// girdi.mjs — girdi yuzeyi buyudukce yanlis deger verme: on kurgu yanlis, dogrulamadan kacı geciyor.
import { DEGISKEN, YEREL, KAYNAK, ORTAM } from "./tanim.mjs";

const YANLIS = [                                    // onceden secilmis, rastgele degil
  ["bolge yazimi", "bolge", "bat"], ["sayi yerine dize", "toplayici", "6"],
  ["fazla sifir", "toplayici", 600], ["gun yerine saat", "saklama_gn", 720],
  ["eksik hane", "kuyruk_bolme", 2], ["eksik sifir", "esik_ms", 90],
  ["birim karisikligi", "disk_gb", 20], ["fazla hane", "cekirdek", 40],
  ["kume disi bicim", "bicim", "ham"], ["isaret", "bellek_gb", -8],
];

const dogrula = (ad, v) => {                        // tip ve deger dogrulamasi
  const t = DEGISKEN[ad];
  if (typeof v !== (t.tur === "sayi" ? "number" : "string")) return `${t.tur} bekleniyordu`;
  return t.dogrula(v) ? null : "deger kabul edilmiyor";
};
const etkilenen = (ad) => Object.entries(KAYNAK).filter(([, g]) => g.includes(ad) ||
  g.some((x) => x.startsWith("yerel.") && YEREL[x.slice(6)].girdi.includes(ad))).map(([k]) => k);

console.log(`girdi yuzeyi: ${Object.keys(ORTAM).length} ortam x ` +
  `${Object.keys(ORTAM.bati).length} verilen degisken = ` +
  `${Object.keys(ORTAM).length * Object.keys(ORTAM.bati).length} elle yazilan deger\n`);
console.log("yanlis                degisken       deger    dogrulama              etkilenen kaynak");
let gecen = 0;
for (const [ad, degisken, deger] of YANLIS) {
  const h = dogrula(degisken, deger);
  if (!h) gecen++;
  console.log(`  ${ad.padEnd(20)}${degisken.padEnd(15)}${String(deger).padEnd(9)}` +
    `${(h ?? "gecti").padEnd(23)}${h ? "" : etkilenen(degisken).length}`);
}
console.log(`\n${YANLIS.length} yanlisin ${YANLIS.length - gecen} tanesi dogrulamada yakalandi, ` +
  `${gecen} tanesi uygulamaya indi`);
const zorunlu = Object.entries(DEGISKEN).filter(([, t]) => t.varsayilan === undefined).length;
console.log(`yakalanmayanlarin hepsi tur ve araliga uygun; yanlis olduklari yalnizca ortamin ` +
  `beklentisinden anlasilir`);
console.log(`girdi yuzeyi ${Object.keys(ORTAM).length} ortamdan ${Object.keys(ORTAM).length * 2} ortama ` +
  `cikarsa elle yazilan deger ${Object.keys(ORTAM.bati).length * Object.keys(ORTAM).length} yerine ` +
  `${Object.keys(ORTAM.bati).length * Object.keys(ORTAM).length * 2}, zorunlu deger ` +
  `${zorunlu * Object.keys(ORTAM).length} yerine ${zorunlu * Object.keys(ORTAM).length * 2} olur`);
```

```
girdi yuzeyi: 3 ortam x 8 verilen degisken = 24 elle yazilan deger

yanlis                degisken       deger    dogrulama              etkilenen kaynak
  bolge yazimi        bolge          bat      deger kabul edilmiyor  
  sayi yerine dize    toplayici      6        sayi bekleniyordu      
  fazla sifir         toplayici      600      deger kabul edilmiyor  
  gun yerine saat     saklama_gn     720      deger kabul edilmiyor  
  eksik hane          kuyruk_bolme   2        gecti                  1
  eksik sifir         esik_ms        90       gecti                  1
  birim karisikligi   disk_gb        20       gecti                  2
  fazla hane          cekirdek       40       gecti                  1
  kume disi bicim     bicim          ham      deger kabul edilmiyor  
  isaret              bellek_gb      -8       deger kabul edilmiyor  

10 yanlisin 6 tanesi dogrulamada yakalandi, 4 tanesi uygulamaya indi
yakalanmayanlarin hepsi tur ve araliga uygun; yanlis olduklari yalnizca ortamin beklentisinden anlasilir
girdi yuzeyi 3 ortamdan 6 ortama cikarsa elle yazilan deger 24 yerine 48, zorunlu deger 12 yerine 24 olur
```

Girdi yüzeyi üç ortam için yirmi dört elle yazılan değerdir. On kurgu yanlıştan altısı doğrulamada
yakalanır: bölge adının yanlış yazılması, sayı yerine dize verilmesi, altı yerine altı yüz
yazılması, gün yerine saat girilmesi, küme dışı bir biçim, eksi bellek. Dördü ise **geçer** —
kuyruk bölmesinin on iki yerine iki, eşiğin dokuz yüz yerine doksan, diskin iki yüz yerine yirmi,
çekirdeğin dört yerine kırk yazılması. Dördünün ortak yanı, tür ve aralık bakımından kusursuz
olmalarıdır. Doğrulama bir değerin **biçimini** sınar, o ortam için **doğru** olup olmadığını
sınayamaz; çünkü doğruluk ölçütü tanımın içinde değil, ortamın beklentisindedir.

Geçen dördünün etkisi de sayılıdır: üçü birer kaynağı, biri iki kaynağı yanlış değerle kurar. Bir
girdinin kaç kaynağa dokunduğu, o girdinin doğrudan okunduğu kaynaklara ve onu okuyan yerel
değerlerin geçtiği kaynaklara bağlıdır — disk boyutu iki kaynağa birden girdiği için tek bir yanlış
hane iki kaynağı birlikte kaydırır.

Son satır yüzeyin nasıl büyüdüğünü gösterir. Ortam sayısı üçten altıya çıkarsa elle yazılan değer
yirmi dörtten kırk sekize, zorunlu değer on ikiden yirmi dörde çıkar. **Yanlış yazma fırsatı ortam
sayısıyla doğrusal artar** ve bu artışın sekizde üçü, üç ortamda da aynı yazılan değerlerdendir —
yani gereksiz yere yüzeyde duran değerlerden. Girdi yüzeyini küçültmenin en ucuz yolu yeni bir
doğrulama yazmak değil, ortama göre değişmeyen değeri değişken olmaktan çıkarmaktır.

#### Özet

- Tanım 14 değişken taşıdı: 4 zorunlu, 10 varsayılanlı, **2 ölü**. 6 değişken hiçbir ortamda
  verilmedi ve varsayılanında kaldı.
- Üç ortam 24 değer yazdı; **15'i ortama göre gerçekten farklı**, 9'u üç ortamda da aynıydı. O 9
  değer değişken olmak zorunda değildi ama üç kez elle yazıldı.
- Dört kaynak 15 benzersiz girdi okudu; üç ortam arasındaki bütün fark bu girdilerin **8'inde**
  toplandı. Merkez ortamı tek bir değeri vermeyi unuttuğunda varsayılan sessizce devreye girdi ve 2
  kaynağı yanlış kurdu.
- 10 yanlış girdiden 6'sı doğrulamada yakalandı, **4'ü geçti**; geçenlerin hepsi tür ve aralık
  bakımından kusursuzdu ve 3 kaynağı 5 yerde yanlış değerle kurdu.
- 5 çıktının 4'ü tüketildi, 1'i ölü çıktıydı; 3 tüketen tanım 6 bağımlılık kurdu ve iki çıktı
  ikişer tanım tarafından okundu.
- Ortam sayısı 3'ten 6'ya çıkarsa elle yazılan değer 24'ten 48'e, zorunlu değer 12'den 24'e çıkar.

#### Sonraki Adım

Değişkenler tanıma dışarıdan değer verdi, yerel değerler onları türetti, çıktılar sonucu başka
tanımlara açtı. Ama bu üç yapı bir şeyi hiç söylemedi: **hangi kaynak hangisinden önce
yaratılmalı.** Kuyruk adı bir makineye, uç nokta bir veri deposuna, ağ kuralı bir alt ağa bağlıysa,
uygulama bunları rastgele bir sırayla deneyemez. Bir sonraki ders bu sıralamayı ele alır: bir
kaynağın başka bir kaynağın değerini okumasından doğan örtük bağımlılık ile elle yazılan açık
bağımlılığın farkı, sıralamanın nasıl çözüldüğü ve sıra yanlış olduğunda uygulamanın nerede
durduğu.

### Bağımlılıklar ve Yaşam Döngüsü

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/bagimliliklar-ve-yasam-dongusu

Kayıttan bağımlılık grafiği çıkarılır ve topolojik sıra çözülür: kaç kenar örtük, kaç kenar yalnız açık bildirimle var, kaydın hiç görmediği gereklilik kaç yarış üretiyor. Yaşam döngüsü kuralları eklenince planın kalem sayısı, uygulamanın adım sayısı ve kesinti saniyesi ayrı ayrı sayılır.

Bir önceki ders tanımı parametreleştirdi: aynı kayıt üç ortama, ayrı değişken kümeleriyle
uygulanabilir hâle geldi. Kayıt artık hangi kaynağın hangi değerlerle var olacağını söylüyor.
Söylemediği tek bir şey var ve o şey uygulamanın ilk saniyesinde sorulur: bu on dört kaynak
**hangi sırayla** yaratılacak.

Bildirimsel bir kayıtta sıra yazılmaz. Yazılmaması, sıranın olmadığı anlamına gelmez — bir alt ağ
kendi ağından önce var olamaz. Sıra, kaydın içinden **çıkarılır**: bir kaynağın alanı başka bir
kaynağın çıktısını kullanıyorsa aralarında bir kenar vardır. Bu dersin ölçtüğü şey o çıkarımın
kapsamıdır: kayıt gerçek gerekliliklerin kaçını görüyor, göremediklerinde ne oluyor ve görmesi
için elle yazılan her satır neye mal oluyor.

Ölçüm yine **bölgesel ölçüm ağının** kaydı üzerindedir ve **kurgudur**; koşan şey `node` ile
kurulmuş bir **modeldir**, gerçek bir sağlayıcıya hiçbir çağrı gitmez.

**BS25.** Bir bölgenin kaydı on dört kaynaktan oluşur: ağ, alt ağ, güvenlik kümesi, rol, izin,
nesne deposu, ayar nesnesi, veri deposu, kuyruk, iki toplayıcı makine, dengeleyici, sağlık
denetimi ve DNS kaydı.
**BS26.** Yapılandırma dilinin söz dizimi bu dersin kendi küçük biçimidir:
`kaynak <tür> "<ad>" { alan = ifade }`. Bir alanın ifadesinde `<tür>.<ad>.<çıktı>` geçerse **örtük
bağımlılık** doğar; `bagli = [...]` listesi **açık bildirimdir**.
**BS27.** Bağımlılığı çözülmüş kaynaklar aynı turda, yani paralel yaratılır; bir tur bitmeden
sonraki tur başlamaz.
**BS28.** Makine açılışta ayar dosyasını nesne deposundan çeker ve bunun için izne muhtaçtır. Bu
iki gereklilik kayıtta yalnız serbest metinde geçer, hiçbir ifadeye girmez.
**BS29.** Süreler: makine yaratma 90, yok etme 10, dengeleyiciye ekleme ve çıkarma 5 saniyedir.
İki makine tam kapasitedir, bir makine yarım.
**BS30.** Veri deposunun yeniden oluşturulması geri alınamaz: içindeki 41 günlük sayaç okuması
silinir.

#### Kaydın Kendi Kurduğu Sıra

```js
// graf.mjs — kayit metninden bagimlilik grafi kurar ve topolojik sira cozer. Model;
// gercek altyapi yaratilmaz. Yapilandirma dilinin soz dizimi bu dersin kendi bicimidir.

export const KAYIT = `
kaynak ag "olcum" {
  bolge = "kuzey"
  blok  = "10.20.0.0/16"
}
kaynak nesne_deposu "yapilandirma" {
  ad = "olcum-yapilandirma"
}
kaynak rol "toplayici" {
  ad = "toplayici-rolu"
}
kaynak izin "makine_okuma" {
  hedef = nesne_deposu.yapilandirma.ad
  kime  = rol.toplayici.kimlik
}
kaynak altag "toplayici" {
  ag   = ag.olcum.kimlik
  blok = "10.20.1.0/24"
}
kaynak guvenlik_kumesi "toplayici" {
  ag    = ag.olcum.kimlik
  giris = "10.20.1.0/24"
}
kaynak veri_deposu "sayac" {
  altag    = altag.toplayici.kimlik
  guvenlik = guvenlik_kumesi.toplayici.kimlik
  boyut    = "orta"
}
kaynak kuyruk "parti" {
  altag = altag.toplayici.kimlik
}
kaynak nesne "toplayici_ayari" {
  depo         = nesne_deposu.yapilandirma.ad
  veri_adresi  = veri_deposu.sayac.adres
  kuyruk_adresi = kuyruk.parti.adres
}
kaynak makine "toplayici_1" {
  altag    = altag.toplayici.kimlik
  guvenlik = guvenlik_kumesi.toplayici.kimlik
  rol      = rol.toplayici.kimlik
  acilis   = "ayar dosyasini nesne deposundan cek"
}
kaynak makine "toplayici_2" {
  altag    = altag.toplayici.kimlik
  guvenlik = guvenlik_kumesi.toplayici.kimlik
  rol      = rol.toplayici.kimlik
  acilis   = "ayar dosyasini nesne deposundan cek"
}
kaynak dengeleyici "toplayici" {
  altag    = altag.toplayici.kimlik
  hedefler = [makine.toplayici_1.kimlik, makine.toplayici_2.kimlik]
  bagli    = [guvenlik_kumesi.toplayici]
}
kaynak saglik_denetimi "toplayici" {
  hedef = dengeleyici.toplayici.kimlik
}
kaynak dns_kaydi "toplayici" {
  adres = dengeleyici.toplayici.adres
  bagli = [saglik_denetimi.toplayici]
}
`;

// BS28: kaydin hic bilmedigi dort gereklilik. Makine acilista ayar dosyasini ceker ve
// bunun icin izne muhtactir; ikisi de yalniz "acilis" metninde geciyor, ifadede degil.
export const GIZLI = [
  ["makine.toplayici_1", "nesne.toplayici_ayari"],
  ["makine.toplayici_1", "izin.makine_okuma"],
  ["makine.toplayici_2", "nesne.toplayici_ayari"],
  ["makine.toplayici_2", "izin.makine_okuma"],
];

export function ayristir(metin) {
  const dugum = [], kenar = new Map();                    // kenar: "a->b" -> {ortuk, acik}
  const blok = /kaynak\s+(\w+)\s+"(\w+)"\s*\{([\s\S]*?)\n\}/g;
  let m;
  while ((m = blok.exec(metin)) !== null) {
    const kimlik = `${m[1]}.${m[2]}`;
    dugum.push(kimlik);
    const acik = m[3].match(/bagli\s*=\s*\[([^\]]*)\]/);
    const kalan = m[3].replace(/bagli\s*=\s*\[[^\]]*\]/, "");
    const ekle = (hedef, tur) => {
      const a = `${kimlik}->${hedef}`;
      if (!kenar.has(a)) kenar.set(a, { ortuk: false, acik: false });
      kenar.get(a)[tur] = true;
    };
    for (const r of kalan.matchAll(/\b([a-z_]+)\.([a-z_0-9]+)\.[a-z_]+\b/g))
      ekle(`${r[1]}.${r[2]}`, "ortuk");                   // ifade baska kaynagin ciktisini kullaniyor
    if (acik)
      for (const r of acik[1].matchAll(/\b([a-z_]+)\.([a-z_0-9]+)\b/g))
        ekle(`${r[1]}.${r[2]}`, "acik");                  // elle yazilmis bildirim
  }
  return { dugum, kenar };
}

// Bagimliligi cozulmus kaynaklar ayni turda, yani paralel yaratilir.
export function duzeyle({ dugum, kenar }) {
  const bagim = new Map(dugum.map((d) => [d, []]));
  for (const a of kenar.keys()) {
    const [k, h] = a.split("->");
    if (bagim.has(k) && bagim.has(h)) bagim.get(k).push(h);
  }
  const duzey = new Map();
  let kalan = [...dugum], tur = 0;
  while (kalan.length) {
    const hazir = kalan.filter((d) => bagim.get(d).every((h) => duzey.has(h)));
    if (!hazir.length) return { dongu: kalan, duzey };
    hazir.forEach((d) => duzey.set(d, tur));
    kalan = kalan.filter((d) => !hazir.includes(d));
    tur++;
  }
  return { dongu: null, duzey };
}

export function olc(metin, gizli = GIZLI) {
  const g = ayristir(metin), d = duzeyle(g);
  const say = { ortuk: 0, acikYalniz: 0, ikisi: 0 };
  for (const v of g.kenar.values())
    v.ortuk && v.acik ? say.ikisi++ : v.ortuk ? say.ortuk++ : say.acikYalniz++;
  if (d.dongu) return { g, d, say, dongu: d.dongu };
  const yaris = gizli.filter(([a, b]) => d.duzey.get(a) <= d.duzey.get(b));
  const tur = Math.max(...d.duzey.values()) + 1;
  const genislik = Math.max(...[...Array(tur).keys()].map((t) =>
    [...d.duzey.values()].filter((x) => x === t).length));
  return { g, d, say, yaris, tur, genislik, dongu: null, satir: metin.trim().split("\n").length };
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const yaz = (baslik, r) => {
    console.log(`\n${baslik}`);
    console.log(`  kaynak ${r.g.dugum.length}, kenar ${r.g.kenar.size}, kayit ${r.satir} satir`);
    console.log(`  ortuk (ifadeden) ${r.say.ortuk} | yalniz acik bildirim ${r.say.acikYalniz}` +
      ` | ikisi birden ${r.say.ikisi}`);
    console.log(`  ${r.tur} tur, en genis tur ${r.genislik} kaynak`);
    for (const [a, b] of GIZLI)
      console.log(`  ${a.padEnd(18)} <- ${b.padEnd(21)} tur ${r.d.duzey.get(a)} vs ` +
        `${r.d.duzey.get(b)}  ${r.d.duzey.get(a) <= r.d.duzey.get(b) ? "YARIS" : "sira dogru"}`);
    console.log(`  yaris: ${r.yaris.length}/${GIZLI.length}`);
  };
  yaz("A) kayit oldugu gibi — sira yalniz ortuk bagimliliktan cikiyor", olc(KAYIT));

  const BILDIRIMLI = KAYIT.replace(/ {2}acilis   = "ayar dosyasini nesne deposundan cek"/g,
    '  acilis   = "ayar dosyasini nesne deposundan cek"\n' +
    "  bagli    = [nesne.toplayici_ayari, izin.makine_okuma]");
  yaz("B) iki makineye acik bildirim eklendi (2 satir)", olc(BILDIRIMLI));

  const DONGULU = BILDIRIMLI.replace(/ {2}depo         = nesne_deposu.yapilandirma.ad/,
    "  depo         = nesne_deposu.yapilandirma.ad\n  bagli        = [makine.toplayici_1]");
  const c = olc(DONGULU);
  console.log("\nC) ayar nesnesine 'bagli = [makine.toplayici_1]' eklendi");
  const karsilikli = [...c.g.kenar.keys()]                 // birbirini bekleyen cift
    .filter((a) => c.g.kenar.has(a.split("->").reverse().join("->")));
  console.log(`  cozulemeyen kalan: ${c.dongu.length} kaynak`);
  console.log(`  halka: ${karsilikli[0].split("->").join(" ve ")} birbirini bekliyor`);
}
```

```
A) kayit oldugu gibi — sira yalniz ortuk bagimliliktan cikiyor
  kaynak 14, kenar 23, kayit 59 satir
  ortuk (ifadeden) 21 | yalniz acik bildirim 2 | ikisi birden 0
  6 tur, en genis tur 4 kaynak
  makine.toplayici_1 <- nesne.toplayici_ayari tur 2 vs 3  YARIS
  makine.toplayici_1 <- izin.makine_okuma     tur 2 vs 1  sira dogru
  makine.toplayici_2 <- nesne.toplayici_ayari tur 2 vs 3  YARIS
  makine.toplayici_2 <- izin.makine_okuma     tur 2 vs 1  sira dogru
  yaris: 2/4

B) iki makineye acik bildirim eklendi (2 satir)
  kaynak 14, kenar 27, kayit 61 satir
  ortuk (ifadeden) 21 | yalniz acik bildirim 6 | ikisi birden 0
  8 tur, en genis tur 3 kaynak
  makine.toplayici_1 <- nesne.toplayici_ayari tur 4 vs 3  sira dogru
  makine.toplayici_1 <- izin.makine_okuma     tur 4 vs 1  sira dogru
  makine.toplayici_2 <- nesne.toplayici_ayari tur 4 vs 3  sira dogru
  makine.toplayici_2 <- izin.makine_okuma     tur 4 vs 1  sira dogru
  yaris: 0/4

C) ayar nesnesine 'bagli = [makine.toplayici_1]' eklendi
  cozulemeyen kalan: 6 kaynak
  halka: nesne.toplayici_ayari ve makine.toplayici_1 birbirini bekliyor
```

İlk satır oranı verir: kayıt yirmi üç kenarın **yirmi birini** hiç kimse yazmadan buldu. Alt ağın
`ag.olcum.kimlik` yazması, ağın alt ağdan önce yaratılması gerektiğini söylemeye yeter; kimse
"önce ağı kur" demedi. Sıralamanın ana kaynağı bildirimin kendisidir ve bu **bedavadır** —
zaten yazılması gereken ifadenin yan ürünüdür.

Yalnız açık bildirimle var olan iki kenar da anlamlıdır. Dengeleyici güvenlik kümesinin
çıktısını hiçbir alanında kullanmaz, ama güvenlik kuralı yokken açılan bir dengeleyici trafiği
düşürür; DNS kaydı da sağlık denetiminin çıktısını kullanmaz, ama denetim kurulmadan isim
yayınlanırsa istek bozuk düğüme gider. İkisi de **veri akışı olmayan ama zaman ilişkisi olan**
bağlardır ve tek yolu elle yazmaktır. Örtük çıkarımın kör noktası tam olarak budur.

#### Kaydın Görmediği Gereklilik

Alt bölüm oranın öbür yüzünü verir. Kayıt on dört kaynağı ve yirmi üç kenarı biliyor; gerçekte
**yirmi yedi** gereklilik var. Dördü kayda hiç girmemiş, çünkü ifadeye değil serbest metne
yazılmışlar: makine açılışta ayar dosyasını çekiyor ve bunun için izne muhtaç. Kayıt gerçeğin
23/27'sini, yani yüzde 85'ini görüyor.

Görmediği dördün ikisi **tesadüfen** doğru sırada çözüldü. İzin, nesne deposu ve rolden türediği
için birinci turda yaratılıyor; makineler ikinci turda. Kimse bunu istemedi, öyle denk geldi.
Kalan ikisi denk gelmedi: ayar nesnesi veri deposunun ve kuyruğun adresini kullandığı için
**üçüncü** tura düşüyor, makineler ise ikinci turda açılıyor. İki makine de var olmayan bir
dosyayı çekmeye çalışıyor. Bu bir hata değil, bir **yarıştır** — kayıt iki kaynağın sırasını
serbest bıraktı, sağlayıcı da onları sırasız yarattı.

Onarım iki satırdır ve B bölümü bedelini gösterir: yarış sıfırlandı, kenar sayısı 23'ten 27'ye
çıktı, ama tur sayısı **altıdan sekize**, en geniş turdaki paralellik **dörtten üçe** düştü.
Makineler artık ayar nesnesini beklediği için grafiğin en uzun yolu iki tur uzadı. Doğru sıranın
fiyatı, uygulamanın uzaması ve paralelliğin daralmasıdır.

C bölümü açık bildirimin öbür riskini sayar. Ayar nesnesine yanlış yönde tek bir `bagli` satırı
yazıldığında halka kapanır: ayar nesnesi makineyi, makine ayar nesnesini bekler. Çözücü hiçbir
tur üretemez ve **altı kaynak** — halkadaki ikisi ve onlara bağlı dört kaynak — hiç yaratılamaz.
Örtük bağımlılık halka kuramaz, çünkü bir ifade henüz var olmayan bir çıktıyı okuyamaz; halka
ancak elle yazılan bildirimle kurulur. Açık bildirim, örtük çıkarımın göremediğini kapatır ve
karşılığında yeni bir hata sınıfı açar.

#### Yaşam Döngüsü Kuralları Planı Nasıl Değiştiriyor

```js
// yasam.mjs — yasam dongusu kurallari plani ve uygulamayi nasil degistiriyor. Model.
const OP = { cikar: 5, yoket: 10, yarat: 90, ekle: 5 };   // BS29: saniye

function senaryo({ onceYarat, paralel }) {
  const adim = [], hizmet = [], varlik = [];
  let imlec = 0;
  for (const m of ["toplayici_1", "toplayici_2"]) {
    let t = paralel ? 0 : imlec;
    if (onceYarat) {
      adim.push([m, "yarat (yeni ad)", t, OP.yarat]); varlik.push([t, +1]); t += OP.yarat;
      adim.push([m, "hedefe ekle", t, OP.ekle]); hizmet.push([t + OP.ekle, +1]); t += OP.ekle;
      adim.push([m, "cikar (eski)", t, OP.cikar]); hizmet.push([t + OP.cikar, -1]); t += OP.cikar;
      adim.push([m, "yok et (eski)", t, OP.yoket]); varlik.push([t + OP.yoket, -1]); t += OP.yoket;
    } else {
      adim.push([m, "hedeften cikar", t, OP.cikar]); hizmet.push([t + OP.cikar, -1]); t += OP.cikar;
      adim.push([m, "yok et", t, OP.yoket]); varlik.push([t, -1]); t += OP.yoket;
      adim.push([m, "yarat", t, OP.yarat]); varlik.push([t, +1]); t += OP.yarat;
      adim.push([m, "hedefe ekle", t, OP.ekle]); hizmet.push([t + OP.ekle, +1]); t += OP.ekle;
    }
    imlec = t;
  }
  const sure = Math.max(...adim.map(([, , t, s]) => t + s));
  let sifir = 0, yarim = 0, enCok = 2;
  for (let t = 0; t <= sure; t++) {
    const h = 2 + hizmet.filter(([a]) => a <= t).reduce((s, [, d]) => s + d, 0);
    const v = 2 + varlik.filter(([a]) => a <= t).reduce((s, [, d]) => s + d, 0);
    if (h === 0) sifir++; else if (h === 1) yarim++;
    enCok = Math.max(enCok, v);
  }
  return { adim: adim.length, sure, sifir, yarim, enCok };
}

console.log("iki toplayici makinesi yeniden olusturuluyor (makine tipi degisti)");
console.log("kural                              adim  sure  sifir kapasite  yarim  en cok makine");
for (const [ad, k] of [
  ["varsayilan, sirayla", { onceYarat: false, paralel: false }],
  ["varsayilan, paralel", { onceYarat: false, paralel: true }],
  ["once_yarat, sirayla", { onceYarat: true, paralel: false }],
  ["once_yarat, paralel", { onceYarat: true, paralel: true }],
]) {
  const r = senaryo(k);
  console.log(`${ad.padEnd(35)}${String(r.adim).padStart(4)}${String(r.sure).padStart(6)}` +
    `${String(r.sifir).padStart(16)}${String(r.yarim).padStart(7)}${String(r.enCok).padStart(15)}`);
}

// BS30: ikinci degisiklik — veri deposunun bolgesi kayitta degisti; yerinde guncellenemez.
const DEGISIKLIK = [
  ["ag.olcum", "degistir", false],
  ["veri_deposu.sayac", "yeniden olustur", true],        // uzerinde yok_etmeyi_engelle var
  ["makine.toplayici_1", "yeniden olustur", false],
  ["makine.toplayici_2", "yeniden olustur", false],
  ["dengeleyici.toplayici", "degistir", false],
  ["dns_kaydi.toplayici", "degistir", false],
];

const engelli = DEGISIKLIK.filter(([, k, e]) => e && k === "yeniden olustur");
console.log(`\nplan: ${DEGISIKLIK.length} kalem — ` +
  `${DEGISIKLIK.filter(([, k]) => k === "yeniden olustur").length} yeniden olustur, ` +
  `${DEGISIKLIK.filter(([, k]) => k === "degistir").length} degistir`);
console.log(`yok_etmeyi_engelle kurali tasiyan kalem: ${engelli.length} ` +
  `(${engelli.map(([a]) => a).join(", ")})`);
console.log(`kural yokken uygulanan: ${DEGISIKLIK.length}/${DEGISIKLIK.length} kalem, ` +
  `geri alinamayan 1 (41 gunluk sayac okumasi)`);
console.log(`kural varken uygulanan: 0/${DEGISIKLIK.length} kalem — plan tumden reddedilir; ` +
  `bekleyen zararsiz kalem ${DEGISIKLIK.length - engelli.length}`);
```

```
iki toplayici makinesi yeniden olusturuluyor (makine tipi degisti)
kural                              adim  sure  sifir kapasite  yarim  en cok makine
varsayilan, sirayla                   8   220               0    210              2
varsayilan, paralel                   8   110             105      0              2
once_yarat, sirayla                   8   220               0      0              3
once_yarat, paralel                   8   110               0      0              4

plan: 6 kalem — 3 yeniden olustur, 3 degistir
yok_etmeyi_engelle kurali tasiyan kalem: 1 (veri_deposu.sayac)
kural yokken uygulanan: 6/6 kalem, geri alinamayan 1 (41 gunluk sayac okumasi)
kural varken uygulanan: 0/6 kalem — plan tumden reddedilir; bekleyen zararsiz kalem 5
```

Bu tablonun asıl bulgusu, planın gösterdiği ile uygulamanın yaptığı arasındaki mesafedir. Plan
iki kalem yazar: iki makine yeniden oluşturulacak. Uygulama **sekiz adım** koşar ve ilk satırda
**210 saniye** yarım kapasiteyle çalışır. Ne sekiz adım ne 210 saniye planda görünür; plan
kalemin kararını söyler, kalemin bedelini söylemez.

Dört satır iki ekseni birden tarar. Paralellik süreyi yarıya indirir — 220 saniyeden 110'a — ama
varsayılan kuralla birlikte hizmeti tamamen düşürür: 105 saniye sıfır kapasite, yani sayaç
okumalarını kimse toplamaz. `once_yarat` kuralı ikisini de kurtarır: yeni makine hazır olup hedefe
girmeden eski çıkarılmaz, sıfır ve yarım kapasite sütunları boş kalır. Faturası son sütundadır:
aynı anda ayakta duran makine sayısı ikiden **üçe**, paralel koşumda **dörde** çıkar. Kesintisiz
yeniden oluşturmanın fiyatı, geçici olarak iki kat kaynaktır — ve kaynak adı çakışmasın diye yeni
makinenin adı üretilmek zorundadır.

Alt blok ikinci kuralı ölçer. Veri deposunun bölgesi kayıtta değiştiğinde plan altı kalem çıkarır;
üçü yeniden oluşturma, biri veri deposunun kendisi. O kalem uygulanırsa 41 günlük sayaç okuması
geri dönmemek üzere silinir. `yok_etmeyi_engelle` kuralı bunu durdurur, ama tanesini değil
**planın tamamını**: altı kalemin sıfırı uygulanır, zararsız beş kalem de bekler. Kaydın koruması, kaydın akışkanlığından düşülür.

#### Özet

- Kayıt 14 kaynak ve 23 kenar biliyor; 23 kenarın 21'i örtük, yani hiç kimsenin yazmadığı,
  ifadenin yan ürünü olan bağımlılıktır. Sıralamanın ana kaynağı bedavadır.
- Yalnız açık bildirimle var olan 2 kenar veri akışı olmayan zaman ilişkileridir; örtük çıkarımın
  kör noktası budur ve tek yolu elle yazmaktır.
- Gerçek gereklilik 27, kaydın gördüğü 23: yüzde 85. Görülmeyen 4 gerekliliğin 2'si tesadüfen
  doğru sıraya düştü, 2'si yarış üretti — makineler var olmayan ayar dosyasını çekti.
- 2 satır açık bildirim yarışı sıfırladı; karşılığında tur sayısı 6'dan 8'e, en geniş turdaki
  paralellik 4'ten 3'e gitti. Yanlış yönde yazılan tek bir bildirim halka kurdu ve 6 kaynak hiç
  yaratılamadı.
- Plan 2 kalem der, uygulama 8 adım koşar ve 210 saniye yarım kapasiteyle çalışır; ikisi de planda
  yazmaz. `once_yarat` kesintiyi sıfırlar, bedeli aynı anda 3 (paralelde 4) makinedir.
- `yok_etmeyi_engelle` 41 günlük veriyi kurtarır ama planı tümden reddeder: 6 kalemin 0'ı uygulanır,
  zararsız 5 kalem de bekler.

#### Sonraki Adım

Bu ders sırayı ve yaşam döngüsünü ölçtü, ama kaydın kendisine bakıldığında bir şey göze çarpar:
iki toplayıcı makine, dört satırı harfi harfine aynı olan iki ayrı blok olarak yazıldı. Bölge
büyüyüp makine sayısı ona çıktığında bu blok on kez mi kopyalanacak? Sonraki ders aynı on kaynağı
üç ayrı biçimde yazar — elle, sayı temelli yinelemeyle, küme temelli yinelemeyle — ve üçünü aynı
kalemlerde karşılaştırır: kaç satır, ortadan bir eleman çıkarıldığında kaç kaynak yeniden
oluşturuluyor, okunabilirlik ne kaybediyor.

### Yineleme Yapıları

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/yineleme-yapilari

Aynı on kaynak üç kayıt biçiminde yazılır: elle on blok, sayı temelli yineleme, küme temelli yineleme. Satır sayısı, istisnanın maliyeti ve adres kararlılığı sayılır; ortadan bir bölge çıkarıldığında sayı temelli biçimin altı kalemlik plan ürettiği, kayan beş adresin ölçüm ağında otuz beş kaçan sayaç partisine karşılık geldiği ölçülür.

Bir önceki dersin kaydında iki toplayıcı makine vardı ve ikisi ayrı ayrı, dört satırı harfi harfine
aynı olan iki blok olarak yazılmıştı. Bölge büyüdü: ölçüm ağı artık on ayrı alt bölgede sayaç
okuyor. Aynı bloğu on kez kopyalamak bir seçenek, kaydın kendisine "bunu on kez yap" demek başka
bir seçenek. Bu ders üçüncü bir seçeneği de ekler ve üçünü aynı kalemlerde karşılaştırır.

Karşılaştırmanın çıkış noktası şudur: **üç biçim de aynı gerçeği üretir.** On makine, aynı alt
ağlar, aynı boyutlar, aynı etiketler. Aralarındaki fark gerçekte değil, kayıtta ve kaydın kaynağa
verdiği **adreste**dir. O adres, kaydın bir sonraki uygulamada hangi gerçek kaynağı kastettiğini
belirleyen tek şeydir — ve bu dersin bütün sayıları oradan çıkar.

Ölçüm **bölgesel ölçüm ağı** üzerindedir ve **kurgudur**; koşan şey `node` ile kurulmuş bir
**modeldir**.

**BS31.** Ölçüm ağı on alt bölgede sayaç okur; bölge adları listede yazılı sıradadır.
**BS32.** Üç kayıt biçimi de aynı on makineyi tanımlar; nitelikleri birebir aynıdır.
**BS33.** Adres, kaynağın durumda bulunduğu anahtardır: elle biçimde blok adı, sayı temelli
biçimde sıra numarası, küme temelli biçimde harita anahtarı.
**BS34.** Bir bölge iki kat sayaç taşır ve makinesi büyük olmalıdır; bu, biçimlerin istisnayı nasıl
taşıdığını ölçmek için konmuş tek sapmadır.
**BS35.** `altag` ve `etiket` alanları yerinde güncellenemez; değişirlerse kaynak yeniden
oluşturulur. `boyut` yerinde güncellenir.
**BS36.** Her toplayıcı 15 saniyede bir sayaç partisi çeker. Yeniden oluşturulan bir makine, önceki
dersin süreleriyle 105 saniye hizmet dışıdır.

#### Aynı On Kaynak, Üç Kayıt

```js
// yineleme.mjs — on kaynak uc bicimde yazilir; satir, adres kararliligi ve plan sayilir. Model.
export const BOLGE = ["kuzey_1", "kuzey_2", "kuzey_3", "dogu_1", "dogu_2",
  "dogu_3", "bati_1", "bati_2", "bati_3", "bati_4"];
const ISTISNA = { dogu_2: "buyuk" };            // BS34: bir bolge iki kat sayac tasiyor
const nitelik = (b, ist) => ({ altag: `altag.${b}`, boyut: ist[b] ?? "orta",
  etiket: `toplayici-${b}` });

export function elle(bolgeler, ist = ISTISNA) {
  const metin = bolgeler.map((b) => `kaynak makine "toplayici_${b}" {\n` +
    `  altag  = altag.${b}.kimlik\n  boyut  = "${ist[b] ?? "orta"}"\n` +
    `  etiket = "toplayici-${b}"\n}`).join("\n");
  const cozum = new Map(bolgeler.map((b) => [`makine.toplayici_${b}`, nitelik(b, ist)]));
  return { metin, cozum, dal: 0 };               // deger kendi blogunda, kosul yok
}

export function sayiTemelli(bolgeler, ist = ISTISNA) {
  const dal = Object.entries(ist).map(([k, v]) =>
    `bolgeler[sayi.indeks] == "${k}" ? "${v}" : `).join("");
  const metin = `yerel bolgeler = [${bolgeler.map((b) => `"${b}"`).join(", ")}]\n` +
    `kaynak makine "toplayici" {\n  sayi   = ${bolgeler.length}\n` +
    `  altag  = altag[bolgeler[sayi.indeks]].kimlik\n  boyut  = ${dal}"orta"\n` +
    `  etiket = "toplayici-" + bolgeler[sayi.indeks]\n}`;
  const cozum = new Map(bolgeler.map((b, i) => [`makine.toplayici[${i}]`, nitelik(b, ist)]));
  return { metin, cozum, dal: Object.keys(ist).length };
}

export function kumeTemelli(bolgeler, ist = ISTISNA) {
  const metin = "yerel bolgeler = {\n" +
    bolgeler.map((b) => `  ${b} = "${ist[b] ?? "orta"}"`).join("\n") + "\n}\n" +
    `kaynak makine "toplayici" {\n  kume   = bolgeler\n` +
    "  altag  = altag[kume.anahtar].kimlik\n  boyut  = kume.deger\n" +
    '  etiket = "toplayici-" + kume.anahtar\n}';
  const cozum = new Map(bolgeler.map((b) => [`makine.toplayici["${b}"]`, nitelik(b, ist)]));
  return { metin, cozum, dal: 0 };               // istisna haritanin degerinde
}

const YENIDEN = ["altag", "etiket"];            // BS35: bu alanlar yerinde guncellenemez

export function planla(once, sonra) {
  const p = { ekle: 0, degistir: 0, yeniden: 0, sil: 0 };
  for (const [adres, y] of sonra.cozum) {
    const e = once.cozum.get(adres);
    if (!e) { p.ekle++; continue; }
    const fark = Object.keys(y).filter((k) => e[k] !== y[k]);
    if (fark.length) fark.some((k) => YENIDEN.includes(k)) ? p.yeniden++ : p.degistir++;
  }
  for (const adres of once.cozum.keys()) if (!sonra.cozum.has(adres)) p.sil++;
  return p;
}

export const BICIM = [["elle on blok", elle], ["sayi temelli", sayiTemelli],
  ["kume temelli", kumeTemelli]];
const satir = (m) => m.split("\n").length;
const UC = { dogu_2: "buyuk", bati_1: "buyuk", kuzey_3: "kucuk" };

if (import.meta.url === `file://${process.argv[1]}`) {
  // Uc bicim ayni on gercek kaynagi mi uretiyor: adresi degil, nitelikleri karsilastir.
  const imza = (c) => [...c.values()].map((v) => JSON.stringify(v)).sort().join("|");
  const imzalar = BICIM.map(([, f]) => imza(f(BOLGE).cozum));
  console.log("uc bicim de ayni gercegi uretiyor mu: " +
    `${imzalar.every((x) => x === imzalar[0]) ? "evet" : "hayir"} ` +
    `(${elle(BOLGE).cozum.size} kaynak, ayni nitelikler)`);

  console.log("\nkaydin bedeli (10 kaynak)");
  console.log("bicim          satir  kosul dali  3 istisnada satir/dal  adreste bolge adi");
  for (const [ad, f] of BICIM) {
    const r = f(BOLGE), u = f(BOLGE, UC);
    const adresli = [...r.cozum.keys()].some((a) => a.includes("dogu_2"));
    console.log(`${ad.padEnd(15)}${String(satir(r.metin)).padStart(5)}${String(r.dal).padStart(12)}` +
      `${`${satir(u.metin)}/${u.dal}`.padStart(23)}${(adresli ? "var" : "yok").padStart(19)}`);
  }

  const SENARYO = [
    ["ortadan bir bolge cikti (dogu_2 kapandi)", BOLGE.filter((b) => b !== "dogu_2")],
    ["ortaya bir bolge girdi (dogu_1b acildi)", [...BOLGE.slice(0, 4), "dogu_1b", ...BOLGE.slice(4)]],
    ["sona bir bolge eklendi (bati_5 acildi)", [...BOLGE, "bati_5"]],
  ];

  for (const [ad, yeni] of SENARYO) {
    console.log(`\n${ad}`);
    console.log("bicim          ekle  degistir  yeniden olustur  sil  toplam kalem  kayan adres");
    for (const [bAd, f] of BICIM) {
      const once = f(BOLGE), sonra = f(yeni), p = planla(once, sonra);
      const kayan = [...sonra.cozum].filter(([a, v]) =>
        once.cozum.has(a) && JSON.stringify(once.cozum.get(a)) !== JSON.stringify(v)).length;
      console.log(`${bAd.padEnd(15)}${String(p.ekle).padStart(4)}${String(p.degistir).padStart(10)}` +
        `${String(p.yeniden).padStart(17)}${String(p.sil).padStart(5)}` +
        `${String(p.ekle + p.degistir + p.yeniden + p.sil).padStart(14)}${String(kayan).padStart(13)}`);
    }
  }
}
```

```
uc bicim de ayni gercegi uretiyor mu: evet (10 kaynak, ayni nitelikler)

kaydin bedeli (10 kaynak)
bicim          satir  kosul dali  3 istisnada satir/dal  adreste bolge adi
elle on blok      50           0                   50/0                var
sayi temelli       7           1                    7/3                yok
kume temelli      18           0                   18/0                var

ortadan bir bolge cikti (dogu_2 kapandi)
bicim          ekle  degistir  yeniden olustur  sil  toplam kalem  kayan adres
elle on blok      0         0                0    1             1            0
sayi temelli      0         0                5    1             6            5
kume temelli      0         0                0    1             1            0

ortaya bir bolge girdi (dogu_1b acildi)
bicim          ekle  degistir  yeniden olustur  sil  toplam kalem  kayan adres
elle on blok      1         0                0    0             1            0
sayi temelli      1         0                6    0             7            6
kume temelli      1         0                0    0             1            0

sona bir bolge eklendi (bati_5 acildi)
bicim          ekle  degistir  yeniden olustur  sil  toplam kalem  kayan adres
elle on blok      1         0                0    0             1            0
sayi temelli      1         0                0    0             1            0
kume temelli      1         0                0    0             1            0
```

İlk satır karşılaştırmanın zeminidir: üç biçim de on kaynağı aynı niteliklerle üretiyor. Kayıt
biçimi gerçeği değiştirmiyor.

Satır sütunu ilk bedeli verir. Elle yazım 50 satır, küme temelli 18, sayı temelli 7. Elle yazımın
50 satırının 40'ı tekrardır: aynı üç alan on kez, yalnız bölge adı değişerek. Sayı temelli biçim
en kısasıdır çünkü listeyi tek satıra sıkıştırır. Küme temelli biçim aradadır ve fazlalığı
rastlantı değildir — haritanın her satırı bir bölgeyi adıyla ve değeriyle yazar.

Koşul dalı sütunu istisnanın fiyatını verir. Bir bölgenin büyük makine istemesi, elle yazımda o
bloğun kendi satırına yazılan bir değerdir; küme temelli biçimde haritanın o anahtarındaki
değerdir; ikisinde de sıfır koşul. Sayı temelli biçimde ise ifadeye bir dal girer. Son sütundan
bir öncekine bakıldığında bunun nasıl büyüdüğü görülür: istisna sayısı üçe çıkınca elle ve küme
temelli biçimlerde satır ve dal sayısı değişmez, sayı temelli biçimde tek satırda **üç iç içe
koşul** birikir. İstisnayı taşıyan yer, sayı temelli biçimde kaydın veri kısmı değil ifade kısmıdır.

Son sütun ise bu dersin asıl kalemidir. Elle ve küme temelli biçimlerde adres bölge adını taşır:
`makine.toplayici_dogu_2` ya da `makine.toplayici["dogu_2"]`. Sayı temelli biçimde adres
`makine.toplayici[4]`tür ve dördüncü sıranın hangi bölge olduğu adreste yazmaz — o bilgi listenin
**uygulama anındaki hâlindedir**. Durumda bir kaynağa bakıp hangi bölgeye ait olduğunu söylemek
için kaydın o günkü sürümünü bulmak gerekir.

Adresin ne olduğu bir ayrıntı gibi görünür, ama bu kursun ölçüsünde merkezî bir kalemdir. Kayıt
uygulandığında her kaynağın gerçek kimliği bir adresin altına yazılır; bir sonraki uygulamada kayıt
ile gerçeği eşleştiren tek şey o adrestir. Adres kaynağın kimliğini taşıyorsa eşleşme kaydın
kendisinden okunur; sıra numarası taşıyorsa eşleşme kaydın **o günkü sırasına** bağlıdır. İkinci
durumda kayıt ile gerçek arasındaki bağ, kayıtta yazılı olmayan bir şeye — listenin geçmişteki
hâline — dayanır.


#### Ortadan Bir Eleman Çıkınca

Üç senaryo aynı çıktının alt bölümlerindedir ve birlikte okunduğunda kural nettir.

`dogu_2` bölgesi kapandığında elle ve küme temelli biçimler **tek kalemlik** plan üretir: bir kaynak
silinir, diğer dokuzu hiç dokunulmadan yerinde kalır. Sayı temelli biçim aynı gerçek değişiklik
için **altı kalem** üretir: beş yeniden oluşturma ve bir silme. Sebep aritmetiktir. Liste dördüncü
elemanını kaybedince beşinci eleman dördüncü sıraya, altıncı beşinciye kayar; `makine.toplayici[4]`
adresi dün `dogu_2`yi gösteriyordu, bugün `dogu_3`ü gösteriyor. Kayıt "dördüncü makinenin alt ağı
ve etiketi değişti" der; bu iki alan yerinde güncellenemediği için karar yeniden oluşturmadır.
Kayan adres sütunu bunu doğrudan sayar: beş adres artık başka bir gerçek kaynağa işaret ediyor.

Ortaya bir bölge **girmesi** daha da pahalıdır: yedi kalem, altı kayan adres. Ekleme noktasından
sonraki her adres bir kayar.

Üçüncü senaryo sınırı çizer. Bölge listenin **sonuna** eklendiğinde üç biçim de tek kalem üretir;
sayı temelli biçim de temizdir, çünkü hiçbir adres kaymaz. Yani sayı temelli yineleme yanlış değil,
**sırası anlam taşımayan ve yalnız sondan büyüyen** kümeler için doğru biçimdir. Bölge listesi öyle
bir küme değildir: bölgeler kapanır, araya yenisi girer.

Planın öngörme oranı burada tersine bir soru sorar. Plan yanılmıyor — sayı temelli biçimde "beş
kaynak yeniden oluşturulacak" diyor ve uygulamada tam beşi yeniden oluşturuluyor, oran 5/5. Yanılan
plan değil, planı okuyan kişinin beklentisidir: bir bölge kapandı, bir makine gitmeli. Beklenti bir
kalem, plan altı kalem. Aradaki beş kalem gerçeklikten değil, **kayıt biçiminden** doğuyor.

Kaydın bunu kendi başına fark edememesi de ölçülebilir bir sınırdır. Kayıt "beşinci makinenin alt
ağı değişti" ile "beşinci sıra artık başka bir bölge" arasındaki farkı göremez, çünkü elinde yalnız
adres ve nitelikler vardır. İkisi de aynı şeye benzer: bir adres, değişmiş nitelikler. Ayrımı ancak
kaydı yazan kişi bilir ve o kişi de planı okurken beş kalemi gerçek bir değişiklik sanabilir.
Sayı temelli biçimin riski gizli değildir — planda açıkça yazar — ama planın altı kalemi ile
gerçeğin bir kalemi arasındaki farkı ayırt etmek okuyucuya bırakılmıştır.


#### Kayan Adresin Ölçüm Ağındaki Karşılığı

```js
// bedel.mjs — plan kalemini olcum agi biriminde sayar: kacan sayac partisi. Model.
import { BOLGE, BICIM, planla } from "./yineleme.mjs";
const DISI = 105, PERIYOT = 15, YENIDEN_SN = 110, SIL_SN = 10;   // BS36, onceki dersin sureleri
const yeni = BOLGE.filter((b) => b !== "dogu_2");

console.log("dogu_2 kapandi — ayni gercek degisiklik, uc kayit bicimi");
console.log("bicim          yeniden  hizmet disi makine-sn  kacan parti  uygulama sn");
for (const [ad, f] of BICIM) {
  const p = planla(f(BOLGE), f(yeni));
  const sn = p.yeniden * DISI;
  console.log(`${ad.padEnd(15)}${String(p.yeniden).padStart(7)}${String(sn).padStart(22)}` +
    `${String(p.yeniden * Math.floor(DISI / PERIYOT)).padStart(13)}` +
    `${String(p.yeniden * YENIDEN_SN + p.sil * SIL_SN).padStart(13)}`);
}
console.log("\nkapanan bolgenin kendi partileri zaten toplanmiyor;");
console.log("yukaridaki kayip dokuz saglam bolgenin kaybidir.");
```

```
dogu_2 kapandi — ayni gercek degisiklik, uc kayit bicimi
bicim          yeniden  hizmet disi makine-sn  kacan parti  uygulama sn
elle on blok         0                     0            0           10
sayi temelli         5                   525           35          560
kume temelli         0                     0            0           10

kapanan bolgenin kendi partileri zaten toplanmiyor;
yukaridaki kayip dokuz saglam bolgenin kaybidir.
```

Sayılar ölçüm ağı biriminde okunduğunda karar netleşir. Kapanan bölgenin makinesi zaten gidecekti;
üstteki kayıp **sağlam duran dokuz bölgenin** kaybıdır. Sayı temelli biçimde beş sağlam makine 105'er
saniye hizmet dışı kalır: 525 makine-saniye ve **35 kaçan sayaç partisi**. Elle ve küme temelli
biçimlerde bu kalemler sıfırdır. Uygulama süresi de 560 saniyeye karşı 10 saniyedir.

Kaçan parti geri gelmez. Sayaçlar okumayı sürdürür ama o pencerede kimse çekmediği için ölçüm ağının
o beş bölgesinde 105 saniyelik bir boşluk oluşur — ve bu boşluğun kaydında hiçbir iz yoktur. Kayıt
doğru, plan doğru, uygulama doğru; kaybolan yalnız veridir.

Bu bölümün kapanışında kaydın kapsamadığı bir kalem adıyla anılmalıdır: **bölge listesinin
kendisi**. Kayıtta on bölge adı yazılıdır, ama hangi bölgede sayaç olduğu kaydın bildiği bir şey
değildir — saha ekibinin tuttuğu bir listeden elle kopyalanmıştır. Bir bölge kapandığında kayıt
bunu kendiliğinden öğrenmez; biri gidip listeden siler. Kaydın en pahalı kalemi olan bu değişikliğin
tetikleyicisi, kaydın tamamen dışındadır.


#### Kayıt Biçimi Kararı

| Biçim | Satır (10 kaynak) | Ortadan çıkarmada kalem | Kayan adres | İstisna dalı | Ne zaman |
|---|---|---|---|---|---|
| Elle on blok | 50 | 1 | 0 | 0 | Kaynaklar birbirinden gerçekten farklıysa |
| Sayı temelli | 7 | 6 | 5 | istisna başına 1 | Sırası anlamsız, yalnız sondan büyüyen küme |
| Küme temelli | 18 | 1 | 0 | 0 | Elemanların kimliği olan küme |

Tablo tek bir ölçüyle özetlenir: **satırdan tasarruf etmenin fiyatı adres kararlılığıdır.** Sayı
temelli biçim elle yazımın 50 satırını 7'ye indirir — yedi kat — ve karşılığında ortadaki her
değişiklikte beş ile altı arasında gereksiz yeniden oluşturma üretir. Küme temelli biçim 18 satırla
elle yazımın üçte birine iner ve adres kararlılığını hiç kaybetmez; ödediği bedel, bölge adlarının
kayıtta ikinci kez, harita anahtarı olarak yazılmasıdır.

Karar üç biçim arasında değil, iki soru arasındadır. Birincisi: bu on kaynak birbirinin kopyası mı,
yoksa her birinin kendi kimliği mi var? Ölçüm ağında ikincisi doğrudur — `dogu_2` bir sıra numarası
değil, sahada duran bir bölgedir. İkincisi: bu küme ortasından değişir mi? Bölge listesi kapanmalar
ve açılmalarla değişir, yani değişir. İki cevap birlikte küme temelli biçimi işaret eder; sayı
temelli biçim buradaki 7 satırı 18 satıra tercih ettirecek kadar ucuz değildir.

#### Özet

- Üç kayıt biçimi de aynı 10 gerçek kaynağı aynı niteliklerle üretiyor; fark gerçekte değil, kaydın
  kaynağa verdiği adrestedir.
- Satır: elle 50, küme temelli 18, sayı temelli 7. İstisna sayısı 3'e çıkınca sayı temelli biçimde
  tek ifadede 3 iç içe koşul birikir, diğer ikisinde dal sayısı 0 kalır.
- `dogu_2` kapandığında elle ve küme temelli biçim 1 kalem, sayı temelli biçim 6 kalem üretti;
  5 adres başka bir gerçek kaynağa kaydı. Ortaya bölge girmesinde sayılar 1'e karşı 7 ve 6 oldu.
- Sona ekleme üç biçimde de 1 kalem: sayı temelli yineleme, sırası anlam taşımayan ve yalnız sondan
  büyüyen kümeler için doğru biçimdir.
- Plan yanılmadı — "5 yeniden oluşturulacak" dedi ve 5'i yeniden oluşturuldu, oran 5/5. Beklenti ile
  plan arasındaki 5 kalemlik fark kayıt biçiminden doğdu.
- Ölçüm ağı biriminde: 5 sağlam makine 105'er saniye hizmet dışı, 525 makine-saniye, 35 kaçan sayaç
  partisi, 560 saniyelik uygulama — küme temelli biçimde hepsi 0 ve 10 saniye.

#### Sonraki Adım

Bu ders plan kalemlerini saydı ama planı hep hazır kabul etti: kalemler bir yerden geliyordu.
Sonraki ders o yeri kurar — kayıt ile durumu karşılaştırıp kalem kalem ekle, değiştir, yeniden
oluştur ve sil kararını üreten **planlayıcının** kendisini yazar. Ölçülecek şey planın kendi
doğruluğudur: kaç kalem planda yok ama uygulamada oluyor, kaç kalem planda olup gerçekleşmiyor ve
"yeniden oluşturulacak" denilen kalemlerin kaçı gerçekten yeniden oluşturuluyor.

### Planlama ve Uygulama Akışı

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/planlama-ve-uygulama-akisi

Kayıt ile durumu karşılaştırıp kalem kalem ekle, değiştir, yeniden oluştur ve sil kararı üreten bir planlayıcı yazılır, sonra planın kendisi ölçülür: kaç kalem planda yok ama uygulamada oluyor, kaç kalem planda olup gerçekleşmiyor, yeniden oluşturulacak denilen kalemlerin kaçı gerçekten yeniden oluşturuluyor ve planı gözden geçirmek kaç satır kaç dakika tutuyor.

Bir önceki ders plan kalemlerini kayıt biçimine göre saydı, ama planın kendisini hep hazır kabul
etti: kalemler bir yerden geliyordu. Bu ders o yeri kurar. Planlayıcı, bildirimsel sağlamanın en
somut vaadidir — değişiklik uygulanmadan önce ne olacağını gösteren bir liste. Vaadi ölçmenin yolu
da bellidir: listeyi üret, sonra uygula, sonra ikisini karşılaştır.

Daha önce sürekli koşan bir uzlaşma döngüsü ölçülmüştü: istenen durumla gerçek durumu her turda
karşılaştırıp farkı kendiliğinden kapatan bir denetleyici. Buradaki akış onun tersidir. Uygulama
**tetiklenir**; iki tetikleme arasında geçen sürede gerçek, kimseye sormadan kendi başına değişir.
Plan da işte o arada üretilir ve bu dersin bütün sapmaları o boşluktan çıkar.

Ölçüm **bölgesel ölçüm ağının** kaydı üzerindedir ve **kurgudur**; koşan şey `node` ile yazılmış
bir **planlayıcı modelidir**, hiçbir sağlayıcıya çağrı gitmez.

**BS37.** Planlayıcı kaydı **durumla** karşılaştırır, gerçekle değil. Durum geçen uygulamanın
yazdığı defterdir; gerçek sağlayıcıda o an durandır. İkisi ayrı kaynaktır.
**BS38.** Durum on üç kaynak taşır ve geçen uygulamadan kalmıştır.
**BS39.** Kayıt durumdan on bir noktada ayrılır: bir yeni kaynak, bir silinen kaynak, üç imaj
yükseltmesi ve altı alan değişikliği.
**BS40.** Gerçek durumdan üç noktada sapmıştır; üçü de kayıt dışı, elle yapılmış değişikliktir.
**BS41.** Planlayıcının şeması ile koşan sağlayıcının davranışı üç noktada ayrılır: planlayıcı
`boyut` değişikliğini yeniden oluşturma sayar ama sağlayıcı o alanı yerinde günceller; sağlayıcı
dizeleri büyük-küçük harf ayırmadan saklar; yeni yaratılan makineye kayıtta olmayan bir alan ekler.
Hesaplanan alanın değeri uygulamadan önce bilinmez, yalnız baktığı kaynağın yeniden oluşturulup
oluşturulmayacağından tahmin edilir.
**BS42.** Plan kalemi bir başlık satırı yazar; ekle, sil ve yeniden oluştur kalemlerinde kaynağın
bütün alanları, değiştir kaleminde yalnız değişen alanlar listelenir. Dikkatli okuma hızı dakikada
25 satırdır.


#### Planlayıcı

```js
// plan.mjs — kayit ile durumu karsilastirip plan ureten planlayici, ve plani gercek
// uygulamayla karsilastiran olcum. Model; hicbir saglayiciya cagri gitmez.

const REF = (kaynak) => ({ ref: kaynak });      // hesaplanan alan: degeri uygulamadan once bilinmez

// BS38: durum — gecen uygulamanin yazdigi kayit. On uc kaynak.
const DURUM = {
  "ag.olcum": { blok: "10.20.0.0/16", bolge: "kuzey" },
  "altag.toplayici": { ag: "ag-01", blok: "10.20.1.0/24" },
  "guvenlik_kumesi.toplayici": { ag: "ag-01", giris: "10.20.1.0/24" },
  "veri_deposu.sayac": { altag: "altag-01", boyut: "orta", surum: "4.2" },
  "kuyruk.parti": { altag: "altag-01", saklama: 3 },
  "kuyruk.eski_parti": { altag: "altag-01", saklama: 3 },
  "makine.toplayici_kuzey_1": { altag: "altag-01", imaj: "temel-7", boyut: "orta" },
  "makine.toplayici_kuzey_2": { altag: "altag-01", imaj: "temel-7", boyut: "orta" },
  "makine.toplayici_dogu_1": { altag: "altag-01", imaj: "temel-7", boyut: "orta" },
  "nesne.toplayici_ayari": { depo: "olcum-yapilandirma", veri_adresi: "sayac-01.veri" },
  "dengeleyici.toplayici": { altag: "altag-01", algoritma: "sirayla" },
  "saglik_denetimi.toplayici": { hedef: "dengeleyici-01", aralik: 30 },
  "dns_kaydi.toplayici": { adres: "dengeleyici-01.adres", ttl: 300 },
};

// BS39: kayit — istenen. Durumdan on bir noktada ayriliyor.
const KAYIT = {
  ...DURUM,
  "veri_deposu.sayac": { altag: "altag-01", boyut: "buyuk", surum: "4.2" },
  "kuyruk.parti": { altag: "altag-01", saklama: 7 },
  "makine.toplayici_kuzey_1": { altag: "altag-01", imaj: "temel-9", boyut: "orta" },
  "makine.toplayici_kuzey_2": { altag: "altag-01", imaj: "temel-9", boyut: "orta" },
  "makine.toplayici_dogu_1": { altag: "altag-01", imaj: "temel-9", boyut: "orta" },
  "makine.toplayici_dogu_2": { altag: "altag-01", imaj: "temel-9", boyut: "orta" },
  "nesne.toplayici_ayari": { depo: "olcum-yapilandirma", veri_adresi: REF("veri_deposu.sayac") },
  "dengeleyici.toplayici": { altag: "altag-01", algoritma: "SIRAYLA" },
  "saglik_denetimi.toplayici": { hedef: "dengeleyici-01", aralik: 10 },
  "dns_kaydi.toplayici": { adres: "dengeleyici-01.adres", ttl: 60 },
};
delete KAYIT["kuyruk.eski_parti"];

// BS40: gercek — saglayicida gercekten duran. Durumdan uc noktada sapmis; plan bunlari gormez.
const GERCEK = {
  ...DURUM,
  "ag.olcum": { blok: "10.20.0.0/17", bolge: "kuzey" },          // elle daraltilmis
  "guvenlik_kumesi.toplayici": { ag: "ag-01", giris: "0.0.0.0/0" },  // elle acilmis
  "saglik_denetimi.toplayici": { hedef: "dengeleyici-01", aralik: 5 },  // elle dusurulmus
};

const YENIDEN_PLAN = ["imaj", "boyut", "altag"];   // BS41: planlayicinin elindeki sema
const YENIDEN_GERCEK = ["imaj", "altag"];          // saglayici boyutu yerinde guncelliyor
const NORM = (v) => (typeof v === "string" ? v.toLowerCase() : v);
const VARSAYILAN = { makine: { izleme: "acik" } };  // saglayici yeni kaynaga kendi ekliyor
const K = (v) => JSON.stringify(v);

// Ortak karsilastirici. gercekMi=false ise planlayici, true ise uygulama gozuyle bakar.
function karsilastir(kayit, taban, { gercekMi }) {
  const yeniden = gercekMi ? YENIDEN_GERCEK : YENIDEN_PLAN;
  const esit = gercekMi ? (a, b) => K(NORM(a)) === K(NORM(b)) : (a, b) => K(a) === K(b);
  const kalem = new Map();
  for (const [ad, y] of Object.entries(kayit)) {
    const e = taban[ad];
    const tum = Object.keys(y).length;
    if (!e) { kalem.set(ad, { karar: "ekle", alan: Object.keys(y), tum }); continue; }
    const alan = Object.keys(y).filter((k) => !y[k]?.ref && !esit(y[k], e[k]));
    const karar = alan.length
      ? (alan.some((k) => yeniden.includes(k)) ? "yeniden olustur" : "degistir") : null;
    kalem.set(ad, { karar, alan, tum });
  }
  for (const ad of Object.keys(taban))
    if (!kayit[ad]) kalem.set(ad, { karar: "sil", alan: [], tum: Object.keys(taban[ad]).length });
  // Hesaplanan alan: baktigi kaynak yeniden olusturuluyorsa deger degisir.
  for (const [ad, y] of Object.entries(kayit))
    for (const [k, v] of Object.entries(y))
      if (v?.ref && kalem.get(v.ref)?.karar === "yeniden olustur") {
        const kl = kalem.get(ad);
        kl.alan.push(k);
        kl.karar = kl.alan.some((a) => yeniden.includes(a)) ? "yeniden olustur" : "degistir";
      }
  for (const [ad, kl] of kalem) if (!kl.karar) kalem.delete(ad);
  return kalem;
}

const PLAN = karsilastir(KAYIT, DURUM, { gercekMi: false });
const UYGULAMA = karsilastir(KAYIT, GERCEK, { gercekMi: true });
// Saglayici varsayilani: yeni kaynak yaratilinca plan disi alan eklenir.
let varsayilanAlan = 0;
for (const [ad, kl] of UYGULAMA)
  if (kl.karar === "ekle") varsayilanAlan += Object.keys(VARSAYILAN[ad.split(".")[0]] ?? {}).length;

const say = (m, k) => [...m.values()].filter((v) => v.karar === k).length;
console.log("karar          plan  uygulama");
const kutuk = (ad, a, b) =>
  console.log(`${ad.padEnd(15)}${String(a).padStart(4)}${String(b).padStart(10)}`);
for (const k of ["ekle", "degistir", "yeniden olustur", "sil"])
  kutuk(k, say(PLAN, k), say(UYGULAMA, k));
kutuk("toplam", PLAN.size, UYGULAMA.size);

const planDisi = [...UYGULAMA.keys()].filter((a) => !PLAN.has(a));
const olmayan = [...PLAN.keys()].filter((a) => !UYGULAMA.has(a));
const ortak = [...PLAN.keys()].filter((a) => UYGULAMA.has(a));
const kararUyusmaz = ortak.filter((a) => PLAN.get(a).karar !== UYGULAMA.get(a).karar);
const planYeniden = [...PLAN].filter(([, v]) => v.karar === "yeniden olustur").map(([a]) => a);
const gercekYeniden = planYeniden.filter((a) => UYGULAMA.get(a)?.karar === "yeniden olustur");
const eskiYanlis = ortak.filter((a) => DURUM[a] && GERCEK[a] && K(DURUM[a]) !== K(GERCEK[a]));

console.log("\nplanin uygulamayi ongorme orani");
console.log(`  planda yok, uygulamada oldu : ${planDisi.length}  (${planDisi.join(", ")})`);
console.log(`  planda var, uygulamada yok  : ${olmayan.length}  (${olmayan.join(", ")})`);
console.log(`  ikisinde de var             : ${ortak.length}, karari uyusmayan ` +
  `${kararUyusmaz.length} (${kararUyusmaz.join(", ")})`);
console.log(`  yeniden olusturma orani     : ${gercekYeniden.length}/${planYeniden.length}`);
console.log(`  planin gosterdigi eski deger yanlis: ${eskiYanlis.length} kalem ` +
  `(${eskiYanlis.join(", ")})`);
console.log(`  planda hic gorunmeyen alan  : ${varsayilanAlan} (saglayici varsayilani)`);
const kesisim = ortak.length - kararUyusmaz.length;
const birlesim = new Set([...PLAN.keys(), ...UYGULAMA.keys()]).size;
console.log(`  tam isabet ${kesisim}/${birlesim} = %${Math.round((kesisim / birlesim) * 100)}`);

// BS42: plan kalemi 1 baslik satiri yazar; ekle/sil/yeniden olustur kaleminde kaynagin butun
// alanlari, degistir kaleminde yalniz degisen alanlar listelenir. Dikkatli okuma 25 satir/dakika.
const TUMU = ["ekle", "sil", "yeniden olustur"];
const satirSay = (m) => [...m.values()].reduce((s, v) =>
  s + 1 + (TUMU.includes(v.karar) ? v.tum : v.alan.length), 0);
const kararSatiri = (m) => [...m.values()]
  .filter((v) => v.karar === "yeniden olustur" || v.karar === "sil")
  .reduce((s, v) => s + 1 + v.tum, 0);

// Ayni kaydin N bolgede yinelenmis hali: adresler ve hesaplanan alan atiflari onekleniyor.
const olcekle = (obj, n) => Object.fromEntries(Array.from({ length: n }, (_, i) =>
  Object.entries(obj).map(([ad, y]) => [`b${i}_${ad}`, Object.fromEntries(
    Object.entries(y).map(([k, v]) => [k, v?.ref ? REF(`b${i}_${v.ref}`) : v]))])).flat());

console.log("\nplani okumanin bedeli");
console.log("bolge  kalem  satir  dakika  karar isteyen satir   %");
for (const n of [1, 3, 10, 30]) {
  const pl = karsilastir(olcekle(KAYIT, n), olcekle(DURUM, n), { gercekMi: false });
  const t = satirSay(pl), kr = kararSatiri(pl);
  console.log(`${String(n).padStart(5)}${String(pl.size).padStart(7)}${String(t).padStart(7)}` +
    `${(t / 25).toFixed(1).padStart(8)}${String(kr).padStart(21)}` +
    `${String(Math.round((kr / t) * 100)).padStart(4)}`);
}
```

```
karar          plan  uygulama
ekle              1         1
degistir          5         6
yeniden olustur   4         3
sil               1         1
toplam           11        11

planin uygulamayi ongorme orani
  planda yok, uygulamada oldu : 2  (ag.olcum, guvenlik_kumesi.toplayici)
  planda var, uygulamada yok  : 2  (nesne.toplayici_ayari, dengeleyici.toplayici)
  ikisinde de var             : 9, karari uyusmayan 1 (veri_deposu.sayac)
  yeniden olusturma orani     : 3/4
  planin gosterdigi eski deger yanlis: 1 kalem (saglik_denetimi.toplayici)
  planda hic gorunmeyen alan  : 1 (saglayici varsayilani)
  tam isabet 8/13 = %62

plani okumanin bedeli
bolge  kalem  satir  dakika  karar isteyen satir   %
    1     11     33     1.3                   19  58
    3     33     99     4.0                   57  58
   10    110    330    13.2                  190  58
   30    330    990    39.6                  570  58
```

Üst tablo ilk bakışta rahatlatıcıdır: plan on bir kalem, uygulama on bir kalem. Sayı tutuyor.
Alt bölüm o rahatlığı kaldırır — aynı sayıdaki iki liste **aynı liste değildir**. On üç ayrı adresin
sekizinde plan ile uygulama hem kalemde hem kararda birleşiyor. Tam isabet oranı yüzde 62.

Sapmanın kaynağı tek bir yapısal seçimdir: planlayıcı kaydı durumla karşılaştırır, uygulama ise
gerçeğe dokunur. Durum ile gerçek arasındaki her fark, plana **tanım gereği** giremez. Ölçüm ağında
üç kalem böyle: ağ bloğu biri tarafından daraltılmış, güvenlik kümesinin giriş kuralı sonuna kadar
açılmış, sağlık denetiminin aralığı elle düşürülmüş. Plan hiçbirini yazmaz. Uygulama ilk ikisini
sessizce geri alır — planda görünmeyen iki kalem — üçüncüsünde ise kalemi doğru gösterir ama **eski
değeri yanlış** gösterir: plan "30'dan 10'a" der, gerçekte 5'ten 10'a çıkılır.

Bu üçüncü tür özellikle sinsi olandır, çünkü kalem sayısını bozmaz. Planı okuyan kişi doğru sayıda
kalem görür, doğru kararları görür, yalnız her kalemin **nereden** başladığı konusunda yanıltılır.
Güvenlik kümesinin sonuna kadar açılmış olması da böyle geri alınır: kimse istemeden düzeltilir,
kimse fark etmeden kapanır ve o kuralın ne kadar süre açık kaldığı hiçbir yere yazılmaz.

#### Planda Olup Gerçekleşmeyen Kalemler

Ters yöndeki iki kalem başka bir mekanizmadan gelir. `dengeleyici.toplayici` kalemi plandadır
çünkü kayıt algoritma değerini büyük harfle yazmıştır ve planlayıcı dizeleri birebir karşılaştırır.
Sağlayıcı aynı değeri harf ayrımı gözetmeden sakladığı için uygulamada hiçbir şey olmaz. Plan bir
değişiklik vaat etti, uygulama sıfır iş yaptı.

Bu kalem zararsız görünür ama bedeli vardır: her plan koşumunda yeniden çıkar. Kayıt ile durum
arasında kapanmayan bir fark, uzlaşmayan bir kalem olarak kalıcı hâle gelir ve planı okuyan kişi
onu her seferinde gözden geçirmek zorunda kalır. Zamanla böyle kalemler "her zaman çıkar, boş ver"
diye öğrenilir — ve o alışkanlık plandaki gerçek bir kalemi de aynı refleksle geçiştirir.

İkinci kalem daha derindir. `nesne.toplayici_ayari` kaydında veri deposunun adresi vardır ve bu
adres **hesaplanan bir alandır**: değeri ancak uygulamadan sonra bilinir. Planlayıcı bu alanı
tahmin edemez, yalnız şunu bilir — baktığı kaynak yeniden oluşturulacaksa adres de değişecektir.
Veri deposu planda yeniden oluşturma kalemidir, dolayısıyla ayar nesnesi de plana girer. Uygulamada
veri deposu yeniden oluşturulmaz, adresi değişmez ve ayar nesnesine dokunulmaz. Planlayıcı burada
yanlış bir veri değil, **yanlış bir varsayımın sonucunu** yazdı.

#### Yeniden Oluşturulacak Denilen Kalemler

Yeniden oluşturma satırı bu kursun en pahalı kararıdır: yok etmek ve yeniden yaratmak geri alınamaz
ve önceki derste ölçüldüğü gibi kesinti üretir. Planın bu kalemdeki isabeti dörtte üçtür.

Kaçırılan kalem veri deposudur ve sebebi bir kayıt hatası değildir. Planlayıcının elindeki şema
`boyut` alanının yeniden oluşturma gerektirdiğini söylüyor; koşan sağlayıcı ise o alanı yerinde
güncelleyebiliyor. Şema ile davranış arasındaki bu tek satırlık fark, plana bakan kişiye kırk bir
günlük sayaç verisinin silineceğini gösterdi. Yanılma yönü şanslıdır — plan gerçekten olandan daha
kötüsünü vaat etti — ama yön bir güvence değildir; ters yöndeki bir şema farkı, planda "yerinde
değişecek" yazan bir kalemi uygulamada yeniden oluşturmaya çevirir.

Bu tek kalemin dalga etkisi de sayıya girer: ayar nesnesinin plana girmesinin **tek sebebi** veri
deposunun yeniden oluşturulacak sanılmasıydı. Yanlış bir yeniden oluşturma kararı, kendisi dışında
en az bir kalem daha üretti. Hesaplanan alana bakan kaynak sayısı arttıkça bu çarpan büyür.

Bir de hiç görünmeyen bir kalem var. Yeni yaratılan makineye sağlayıcı, kayıtta yazmayan bir alan
ekliyor. Plan üç alanlı bir kaynak yaratacağını söylüyor, ortaya dört alanlı bir kaynak çıkıyor.
Dördüncü alan bir sonraki uygulamada da plana girmez, çünkü kayıt o alanı hiç bilmez. **Kaydın
kapsamadığı altyapı** burada tam olarak bir alan genişliğindedir ve o alanı kimse yönetmez.

#### Planı Okumanın Bedeli

Alt tablo planın kendi maliyetini verir. Tek bölge için plan 33 satır ve dikkatli okumayla 1,3
dakika. Bu, gözden geçirilebilir bir metindir. Ölçüm ağı otuz bölgeye çıktığında aynı plan 990
satıra ve 39,6 dakikaya çıkar — ve bu, tek bir imaj yükseltmesinin planıdır.

Oranın sabit kalması dikkate değer: karar isteyen satırlar, yani yeniden oluşturma ve silme
kalemleri, her ölçekte planın yüzde 58'idir. Plan büyüdükçe sinyal oranı düşmez, ama **mutlak
gözden geçirme yükü** doğrusal büyür. Otuz bölgelik bir planda 570 satır geri alınamaz bir karar
taşır ve bunların hepsi aynı ekranda, aynı biçimde, aynı renkte akar.

Buradan çıkan işletim kuralı sayıya dayanır. Kırk dakikalık bir planı satır satır okuyan kimse
yoktur; okunmayan plan ise vaadini yerine getirmez. Planın gözden geçirilebilir kalması için ya
kaydın uygulama birimi küçültülür — otuz bölge tek seferde değil, bölge bölge uygulanır — ya da
plandan yalnız yeniden oluşturma ve silme kalemleri süzülüp önce onlar okunur. İkisi de kaydın
bölünmesiyle ilgilidir ve o konu bu dersin sınırının dışındadır.

Son olarak planın öngörme oranı ile okuma bedeli birlikte okunmalıdır. Yüzde 62 isabetli bir liste,
otuz dakika okunduğunda bile uygulamanın tamamını anlatmaz. Planın değeri kesinlik değil, **karar
verilebilirliktir**: geri alınamaz kalemleri uygulamadan önce göstermesi. O işi dörtte üç isabetle
yapıyor ve kaçırdığı çeyreğin sebebi kayıtta değil, planlayıcının şeması ile sağlayıcının
davranışı arasındaki farktadır.

#### Özet

- Planlayıcı kaydı durumla karşılaştırır, uygulama gerçeğe dokunur; durum ile gerçek arasındaki her
  fark plana tanım gereği giremez. Sapmaların çoğunun kaynağı bu tek yapısal seçimdir.
- Plan 11 kalem, uygulama 11 kalem, ama 13 adresin yalnız 8'inde hem kalem hem karar uyuşuyor:
  tam isabet yüzde 62.
- Planda yok, uygulamada oldu: 2 kalem (elle daraltılmış ağ bloğu, sonuna kadar açılmış giriş
  kuralı). 1 kalem daha doğru göründü ama eski değeri yanlış gösterdi: plan 30'dan der, gerçek 5'tir.
- Planda var, uygulamada yok: 2 kalem — biri harf normalizasyonundan (her koşumda yeniden çıkar),
  biri hesaplanan alandan (yanlış yeniden oluşturma varsayımının dalga etkisi).
- Yeniden oluşturma isabeti 3/4. Kaçırılan kalem, planlayıcının şeması ile sağlayıcının davranışı
  arasındaki bir alanlık farktan geldi ve tek başına bir kalem daha üretti.
- Plan 1 bölgede 33 satır ve 1,3 dakika, 30 bölgede 990 satır ve 39,6 dakika; karar isteyen satır
  oranı her ölçekte yüzde 58 kalıyor, yani gözden geçirme yükü doğrusal büyüyor.

#### Sonraki Adım

Bu ders planı, kayıtta yazılı olan kaynaklar için ölçtü. Ölçüm ağında ise kayıtta hiç yazmayan
kaynaklar da var: ilk kurulumda elle açılmış bir nesne deposu, bir DNS bölgesi, iki eski makine.
Onlar plana hiç girmez, çünkü planlayıcı yalnız kaydın ve durumun bildiğini karşılaştırır. Sonraki
ders bu kaynakları yönetime alır — **içe aktarma** — ve bedelini sayar: kalem başına kaç alan elle
eşleştiriliyor, içe aktarma sonrası ilk planda kaç kalem gereksiz değişiklik gösteriyor, kaynağı
kayıt dışı bırakmakla yönetime almak arasındaki fark ne.

### Kaynak Değiştirme ve İçe Aktarma

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/kaynak-degistirme-ve-ice-aktarma

Kayıt dışı duran yedi kaynak yönetime alınır ve içe aktarmanın bedeli sayılır: kalem başına kaç alan elle eşleştiriliyor, kaç alan kayıt dışı kalıyor, içe aktarma sonrası ilk plan kaç kalem gereksiz değişiklik gösteriyor. Kaynak değiştirmenin kesintisi ve kısmi içe aktarmada yeniden oluşturmayla kaybolan alan ölçülür.

Bir önceki ders planı, kayıtta yazılı olan kaynaklar için ölçtü. Ölçüm ağında ise kayıtta hiç
yazmayan kaynaklar da var: ilk kurulumda elle açılmış bir nesne deposu, bir DNS bölgesi, iki eski
toplayıcı makine, varsayılan bir güvenlik kümesi, eski bir veri deposu ve bir kuyruk. Bu yedi
kaynak hiçbir plana girmez, hiçbir sapması görünmez ve hiçbir gözden geçirmeden geçmez. Planlayıcı
onları görmez, çünkü planlayıcı yalnız kaydın ve durumun bildiğini karşılaştırır.

Bu dersin işi o yediyi yönetime almaktır. İşlem iki parçadan oluşur ve ikisi de ayrı ayrı ölçülür:
kaynağın gerçek kimliğini bir adresle eşleştirmek ve o kaynağın kayıttaki karşılığını **elle
yazmak**. Birincisi mekaniktir. İkincisi bir kopya işidir ve kopya işinin hata oranı bu dersin
merkez sayısıdır.

Ölçüm **bölgesel ölçüm ağı** üzerindedir ve **kurgudur**; koşan şey `node` ile kurulmuş bir
**modeldir**.

**BS43.** Sağlayıcıda duran yedi kaynak hiçbir kaydın bilmediği kaynaklardır.
**BS44.** Kayıt elle yazılır; değerler sağlayıcı arayüzünden okunur. Dört noktada tutmuyor: iki
değer yanlış okunmuş, bir değer varsayılan sanılmış, bir değer farklı harf biçiminde yazılmıştır.
**BS45.** İçe aktarma, kaynağın gerçek alanlarının **tamamını** duruma yazar; kayda ise yalnız elle
yazılan alanlar girer. İkisi arasındaki fark kayıt dışı kalan alandır.
**BS46.** Planlayıcı ve sağlayıcı önceki dersin kurallarını sürdürür: `imaj`, `boyut` ve `altag`
yeniden oluşturma gerektirir, sağlayıcı dizeleri harf ayrımı gözetmeden saklar.
**BS47.** Yeniden oluşturmanın kesintisi makinede 105, veri deposunda 900 saniyedir; her 15 saniyede
bir sayaç partisi kaçar.
**BS48.** Yeniden oluşturulan kaynak, kayıtta yazılı olmayan alanlarını kaybeder ve sağlayıcı
varsayılanına döner. Sağlayıcının kendi ürettiği hesaplanan alanlar bunun dışındadır.

#### Yedi Kaynağı Yönetime Almak

```js
// ice.mjs — kayit disi kaynaklarin yonetime alinmasi ve ice aktarma sonrasi ilk planin
// olcumu. Model; gercek bir saglayiciya cagri gitmez.

// BS43: saglayicida duran, hicbir kaydin bilmedigi yedi kaynak. Ilk kurulumda elle acilmislar.
const GERCEK = {
  "nesne_deposu.eski_olcumler": { ad: "eski-olcumler", bolge: "kuzey",
    surum_koruma: "acik", sifreleme: "saglayici" },
  "dns_bolgesi.olcum": { ad: "olcum.ic", ttl: 3600, kayit_sayisi: 41 },
  "makine.eski_toplayici_1": { altag: "altag-01", imaj: "temel-4", boyut: "buyuk",
    etiket: "toplayici-eski-1" },
  "makine.eski_toplayici_2": { altag: "altag-01", imaj: "temel-4", boyut: "buyuk",
    etiket: "toplayici-eski-2" },
  "guvenlik_kumesi.varsayilan": { ag: "ag-01", giris: "10.20.0.0/16", cikis: "0.0.0.0/0" },
  "veri_deposu.eski_sayac": { altag: "altag-01", boyut: "orta", surum: "3.9", yedek: "gunluk" },
  "kuyruk.eski_akis": { altag: "altag-01", saklama: 3, tip: "fifo" },
};

// BS44: kayit elle yazilir; degerler saglayici arayuzunden okunur. Dort noktada tutmuyor.
const ELLE = {
  "nesne_deposu.eski_olcumler": { ad: "eski-olcumler", bolge: "kuzey" },
  "dns_bolgesi.olcum": { ad: "olcum.ic", ttl: 300 },                    // varsayilan sanildi
  "makine.eski_toplayici_1": { altag: "altag-01", imaj: "temel-4-x86", boyut: "buyuk",
    etiket: "toplayici-eski-1" },                                       // yanlis okundu
  "makine.eski_toplayici_2": { altag: "altag-01", imaj: "temel-4", boyut: "buyuk",
    etiket: "toplayici-eski-2" },
  "guvenlik_kumesi.varsayilan": { ag: "ag-01", giris: "10.20.1.0/24" }, // yanlis okundu
  "veri_deposu.eski_sayac": { altag: "altag-01", boyut: "ORTA", surum: "3.9" },  // harf bicimi
  "kuyruk.eski_akis": { altag: "altag-01", saklama: 3, tip: "fifo" },
};

const HESAPLANAN = ["kayit_sayisi"];            // saglayici uretir, kayda yazilmaz
const YENIDEN = ["imaj", "boyut", "altag"];     // BS46: onceki dersin sema kurallari
const KESINTI = { makine: 105, veri_deposu: 900 };  // BS47: yeniden olusturma kesintisi, sn
const NORM = (v) => (typeof v === "string" ? v.toLowerCase() : v);
const K = (v) => JSON.stringify(v);

// BS45: ice aktarma gercegin butun alanlarini duruma yazar; kayda yalniz yazilanlar girer.
const DURUM = structuredClone(GERCEK);

function planla(kayit, taban, { normalle }) {
  const esit = normalle ? (a, b) => K(NORM(a)) === K(NORM(b)) : (a, b) => K(a) === K(b);
  const kalem = new Map();
  for (const [ad, y] of Object.entries(kayit)) {
    const alan = Object.keys(y).filter((k) => !esit(y[k], taban[ad][k]));
    if (alan.length)
      kalem.set(ad, { alan,
        karar: alan.some((k) => YENIDEN.includes(k)) ? "yeniden olustur" : "degistir" });
  }
  return kalem;
}

const yazilan = (ad) => Object.keys(ELLE[ad]).length;
const yonetilebilir = (ad) =>
  Object.keys(GERCEK[ad]).filter((k) => !HESAPLANAN.includes(k)).length;
const kapsanmayan = (ad) => yonetilebilir(ad) - yazilan(ad);
const adresler = Object.keys(GERCEK);

console.log("ice aktarilan kaynaklar");
console.log("adres                        gercek alan  elle yazilan  kapsanmayan  ilk plan");
const PLAN = planla(ELLE, DURUM, { normalle: false });
const UYGULAMA = planla(ELLE, GERCEK, { normalle: true });
for (const ad of adresler)
  console.log(`${ad.padEnd(28)}${String(yonetilebilir(ad)).padStart(11)}` +
    `${String(yazilan(ad)).padStart(14)}${String(kapsanmayan(ad)).padStart(13)}` +
    `  ${PLAN.get(ad) ? `${PLAN.get(ad).karar} (${PLAN.get(ad).alan.join(",")})` : "-"}`);

const top = (f) => adresler.reduce((s, a) => s + f(a), 0);
console.log(`\ntoplam: ${adresler.length} kalem, ${top(yonetilebilir)} yonetilebilir alan, ` +
  `${top(yazilan)} elle yazildi, ${top(kapsanmayan)} alan kayit disi kaldi`);

console.log(`\nice aktarma sonrasi ilk plan: ${PLAN.size} kalem ` +
  `(${[...PLAN.values()].filter((v) => v.karar === "yeniden olustur").length} yeniden olustur)`);
const sahte = [...PLAN.keys()].filter((a) => !UYGULAMA.has(a));
console.log(`  uygulamada gerceklesen: ${UYGULAMA.size} kalem; gerceklesmeyen ${sahte.length}` +
  ` (${sahte.join(", ")})`);
const yenidenler = (m) => [...m].filter(([, v]) => v.karar === "yeniden olustur").map(([a]) => a);
const kesinti = (m) => yenidenler(m).reduce((s, a) => s + (KESINTI[a.split(".")[0]] ?? 0), 0);
console.log(`  yeniden olusturma: planda ${yenidenler(PLAN).length}, uygulamada ` +
  `${yenidenler(UYGULAMA).length}; kesinti planda ${kesinti(PLAN)} sn, uygulamada ` +
  `${kesinti(UYGULAMA)} sn (${(kesinti(UYGULAMA) / 15).toFixed(0)} kacan sayac partisi)`);
const kayip = (m) => yenidenler(m).reduce((s, a) => s + kapsanmayan(a), 0);
console.log(`  yeniden olusturmada varsayilana donecek alan: planda ${kayip(PLAN)}, ` +
  `uygulamada ${kayip(UYGULAMA)}`);

// Duzeltme turu: plan gosterdigi her alani saglayicidan yeniden okuyup kayda gecir.
const DUZELTILMIS = structuredClone(ELLE);
let duzeltilen = 0;
for (const [ad, v] of PLAN)
  for (const k of v.alan) { DUZELTILMIS[ad][k] = GERCEK[ad][k]; duzeltilen++; }
const IKINCI = planla(DUZELTILMIS, DURUM, { normalle: false });
console.log(`\nduzeltme turu: ${duzeltilen} alan yeniden okundu -> ` +
  `ikinci plan ${IKINCI.size} kalem`);

console.log("\nkaydin kapsami: uc secenek");
console.log("secenek              kayda giren (sapmasi gorunur)  yonetilmeyen  " +
  "ilk plan  yeniden olusturmada kayip");
const secenek = [
  ["kayit disi birak", 0, top(yonetilebilir), 0, 0],
  ["kismi ice aktarma", top(yazilan), top(kapsanmayan), PLAN.size, top(kapsanmayan)],
  ["tam ice aktarma", top(yonetilebilir), 0, 0, 0],
];
for (const [ad, ...s] of secenek)
  console.log(ad.padEnd(21) + s.map((x, i) => String(x).padStart([29, 14, 10, 27][i])).join(""));
```

```
ice aktarilan kaynaklar
adres                        gercek alan  elle yazilan  kapsanmayan  ilk plan
nesne_deposu.eski_olcumler            4             2            2  -
dns_bolgesi.olcum                     2             2            0  degistir (ttl)
makine.eski_toplayici_1               4             4            0  yeniden olustur (imaj)
makine.eski_toplayici_2               4             4            0  -
guvenlik_kumesi.varsayilan            3             2            1  degistir (giris)
veri_deposu.eski_sayac                4             3            1  yeniden olustur (boyut)
kuyruk.eski_akis                      3             3            0  -

toplam: 7 kalem, 24 yonetilebilir alan, 20 elle yazildi, 4 alan kayit disi kaldi

ice aktarma sonrasi ilk plan: 4 kalem (2 yeniden olustur)
  uygulamada gerceklesen: 3 kalem; gerceklesmeyen 1 (veri_deposu.eski_sayac)
  yeniden olusturma: planda 2, uygulamada 1; kesinti planda 1005 sn, uygulamada 105 sn (7 kacan sayac partisi)
  yeniden olusturmada varsayilana donecek alan: planda 1, uygulamada 0

duzeltme turu: 4 alan yeniden okundu -> ikinci plan 0 kalem

kaydin kapsami: uc secenek
secenek              kayda giren (sapmasi gorunur)  yonetilmeyen  ilk plan  yeniden olusturmada kayip
kayit disi birak                                 0            24         0                          0
kismi ice aktarma                               20             4         4                          4
tam ice aktarma                                 24             0         0                          0
```

Üst tablo kopya işinin bilançosudur. Yedi kaynağın yirmi dört yönetilebilir alanı var; elle yazılan
yirmi. Dört alan hiç yazılmadı: nesne deposunun sürüm koruma ve şifreleme ayarları, güvenlik
kümesinin çıkış kuralı, veri deposunun yedek düzeni. Bunlar unutulmadı — kaydı yazan kişi onları
sağlayıcı arayüzünde ya görmedi ya da varsayılan sanıp geçti. Kaydın kapsamı burada tam olarak
yüzde 83'tür ve kalan yüzde 17 kaydın hiç bilmediği altyapıdır.

Kapsama oranı kaynaktan kaynağa değişiyor. İki makine dört alanının dördüyle, kuyruk üç alanının
üçüyle yazıldı; nesne deposu dördün ikisiyle. Fark, alanların ne kadar göze göründüğüne bağlı:
makinenin alt ağı ve imajı arayüzün ilk ekranındadır, nesne deposunun şifreleme ayarı üçüncü
sekmesinde. **Kaydın kapsamı, kaydı yazan kişinin ne kadar aşağı kaydırdığıyla belirleniyor.**

İçe aktarmanın kendisi tek bir şey yapar: sağlayıcıdaki gerçek kimliği kayıttaki bir **adrese**
bağlar ve o kaynağın bütün alanlarını duruma yazar. Yaptığı şeyin ne olmadığı daha önemlidir —
kaydı yazmaz. Durum gerçeği birebir bilir, kayıt ise yalnız insanın yazdığını. Bu kursta ilk kez
kayıt ile durum aynı andan başlayıp **ayrı içeriklerle** doğuyor ve aradaki fark hemen ilk planda
görünüyor.

Seçilen adres de bir karardır ve bu konunun daha önceki ölçümüne bağlanır. Yedi kaynak burada
kendi adlarıyla adreslendi; bir sıra numarasıyla adreslenselerdi, ilerideki her ekleme ve çıkarma
adresleri kaydırır ve içe aktarılmış kaynaklar başka gerçek kaynaklara işaret etmeye başlardı.
İçe aktarma, adres kararlılığının bedelinin en yüksek olduğu yerdir: yanlış adres yalnız gereksiz
bir yeniden oluşturma değil, **yanlış kaynağın** yeniden oluşturulması demektir.


#### İçe Aktarma Sonrası İlk Plan

İçe aktarma bittikten sonra hiçbir şeyin değişmemiş olması beklenir: kaynaklar aynı, durum
gerçeğin birebir kopyası. Buna rağmen ilk plan **dört kalem** gösteriyor ve bu dört kalemin
tamamı kaydın gerçekle tutmamasından geliyor — hiçbiri istenmiş bir değişiklik değil.

Dördünün üçü uygulamada gerçekleşiyor. Gerçekleşmeyen kalem veri deposudur: kayıt boyutu büyük
harfle yazdığı için planlayıcı bir fark görüyor, sağlayıcı harf ayrımı yapmadığı için uygulamada
hiçbir şey olmuyor. Bu kalem her plan koşumunda yeniden çıkacak ve kimse dokunmadıkça kalıcı
gürültü olarak kalacak.

Bu dört kalemin tehlikesi büyüklüklerinde değil, beklentiye ters düşmelerindedir. İçe aktarma
sonrası planın boş çıkması beklenir; boş çıkmadığında iki okuma mümkündür — ya kayıt gerçeği yanlış
tarif ediyordur, ya da gerçekten bir değişiklik isteniyordur. İkisi planda **aynı görünür**. Önceki
dersin ölçtüğü gözden geçirme yükü burada en kötü hâlindedir: yeni içe aktarılmış bir kaynağın
planını okuyan kişinin elinde, kalemin hangi türden olduğunu ayırt edecek hiçbir işaret yoktur.
Tek yol, gösterilen her değeri sağlayıcıdan tekrar okumaktır — yani içe aktarmanın kopya işini bir
kez daha yapmaktır.

Asıl sayı yeniden oluşturma satırındadır. Plan iki kaynağın yeniden oluşturulacağını söylüyor;
uygulamada bir tanesi oluyor. Aradaki fark ölçüm ağı biriminde büyüktür: planın vaat ettiği kesinti
1005 saniye, gerçekleşen 105 saniye. Bin beş saniyenin dokuz yüzü veri deposuna aittir ve o kalem
yalnız bir harf biçimi farkından doğmuştu. Kaydı yazan kişi `orta` yerine `ORTA` yazdığı için, plan
kırk bir günlük sayaç verisini taşıyan bir veri deposunun yeniden oluşturulacağını gösterdi.

Gerçekleşen 105 saniye de bedavaya gelmedi. Eski toplayıcı makinelerden birinin imajı yanlış
okunmuştu; plan bunu bir değişiklik sanıp makineyi yeniden oluşturdu, makine 105 saniye hizmet dışı
kaldı ve o bölgede yedi sayaç partisi kaçtı. Kaynak hiç değişmemişti; değişen tek şey onun kayıttaki
tarifiydi. **İçe aktarmanın kesintisi, kaynağın değil kopyanın hatasından geliyor.**

Düzeltme turu bu bölümün kapanışıdır. Planın gösterdiği dört alan sağlayıcıdan yeniden okunup kayda
geçirildiğinde ikinci plan sıfır kalem çıkıyor. Yani içe aktarma tek adımda bitmiyor: adres
bağlandıktan sonra kayıt ile gerçeği oturtmak için en az bir tur daha gerekiyor ve o turun girdisi
planın kendisidir. Plan burada bir uyarı değil, bir **düzeltme listesi** olarak çalışıyor.

#### Yeniden Oluşturmada Kaybolan Alan

Kayıt dışı kalan dört alan içe aktarmadan hemen sonra zararsız görünür. Plan onları göstermez,
uygulama onlara dokunmaz, kaynak olduğu gibi çalışır. Zararsızlıkları tek bir koşula bağlıdır:
kaynağın yeniden oluşturulmaması.

Yeniden oluşturma o koşulu kaldırır. Kaynak yok edilip yeniden yaratıldığında yeni kaynak kayıttan
kurulur ve kayıtta yazmayan her alan sağlayıcı varsayılanına döner. Ölçümde bu, planlanan iki
yeniden oluşturmada bir alan, gerçekleşen bir yeniden oluşturmada sıfır alandır — sayı küçük, ama
kaybedilecek alan veri deposunun **yedek düzenidir**. Günlük yedek alan bir veri deposu, kimse
istemeden ve hiçbir planda görünmeden yedeksiz kalabilirdi.

Buradaki tehlikenin biçimi dikkate değer. Kayıt dışı alan bir sapma olarak da görünmez, çünkü
sapma ancak kayıtla karşılaştırılabilen alanlarda ölçülebilir. Kaydın bilmediği alan, kaydın
sapmasını da bilmez. Yedi kaynağın yirmi dört alanının dördü, bu kursun ölçtüğü hiçbir farkın içine
girmiyor — ne planda, ne sapma sayısında, ne gözden geçirmede.

#### Kaydın Kapsamı: Üç Seçenek

Son tablo kararı üç seçenek arasında açıyor ve ilk sütun yanıltıcı olanı ele veriyor. Kaynağı
kayıt dışı bırakmak sıfır satır kayıt ister, sıfır kalemlik plan üretir, yeniden oluşturmada sıfır
alan kaybettirir. Bütün sütunlar sıfır — ama ilk sütun da sıfırdır: **sapması görünen alan yok.**
Yirmi dört alanın hiçbiri hiçbir zaman karşılaştırılmaz. Kayıt dışı kaynak ucuz değildir, ölçüsüzdür.

Kısmi içe aktarma yirmi alanı görünür kılar, dördünü dışarıda bırakır ve karşılığında dört kalemlik
bir ilk plan ile bir düzeltme turu ister. Tam içe aktarma yirmi dört alanı da yazar; ilk planı
sıfırdır ve yeniden oluşturmada hiçbir alan kaybolmaz, ama yazılacak alan sayısı yüzde yirmi artar
ve o alanların her biri gelecekte bakımı gereken bir satırdır.

Tabloda görünmeyen bir dördüncü seçenek daha var: içe aktarıp sonra yönetimden çıkarmak. Kaynak
kayıtta durur ama uygulama ona dokunmaz. Bu seçenek ilk üçünün kötü yanlarını birleştirir — kayıt
bakım ister, sapma yine görünmez — ve tek kazancı kaynağın varlığının yazılı olmasıdır. Ölçüm
ağında bunun tek meşru kullanımı, yok edilmesi tehlikeli olan eski veri deposudur.

Kursun ölçüsüyle söylenirse: kayıt ne kadar genişse gerçekle arasındaki fark o kadar **ölçülebilir**
olur ve fark ölçülebilir olduğu ölçüde yönetilebilir. Ölçülemeyen kalem düzelmiyor değil — kimse
bozulduğunu bilmiyor. Yedi kaynağın dört alanı bugün doğru; yarın biri değiştirdiğinde bunu
gösterecek hiçbir plan yok.

#### Özet

- Yedi kayıt dışı kaynağın 24 yönetilebilir alanının 20'si elle yazıldı, 4'ü kayıt dışı kaldı:
  kapsama yüzde 83. Kapsamayı belirleyen şey, alanın sağlayıcı arayüzünde ne kadar görünür olduğu.
- İçe aktarma sonrası ilk plan 4 kalem gösterdi ve hiçbiri istenmiş bir değişiklik değildi; 3'ü
  uygulamada gerçekleşti, 1'i harf biçimi farkından doğduğu için her koşumda yeniden çıkacak.
- Plan 2 yeniden oluşturma vaat etti, uygulamada 1 oldu: kesinti planda 1005, uygulamada 105 saniye
  ve 7 kaçan sayaç partisi. Kesintinin sebebi kaynağın değişmesi değil, kopyanın yanlış okunmasıydı.
- Düzeltme turu 4 alanı sağlayıcıdan yeniden okuyup ikinci planı 0 kaleme indirdi; içe aktarma tek
  adımda bitmiyor ve düzeltmenin girdisi planın kendisi oluyor.
- Kayıt dışı kalan 4 alan yalnız kaynak yeniden oluşturulmadığı sürece zararsız; oluşturulduğunda
  varsayılana döner. Kaybedilecek alanlardan biri veri deposunun yedek düzeniydi.
- Üç seçenek karşılaştırıldığında kayıt dışı bırakmanın bütün sütunları sıfır çıkıyor — sapması
  görünen alan sütunu dahil. Kayıt dışı kaynak ucuz değil, ölçüsüzdür.

#### Sonraki Adım

Bu konu kaydın kendisini ölçtü: sırayı, yinelemeyi, planı ve şimdi kaydın kapsamını. Dört dersin
hepsinde aynı şey bir kez daha ortaya çıktı — plan ile uygulama arasındaki fark ölçüldü, ama planın
dayandığı **durum** hep verilmiş kabul edildi. Oysa durum bir dosyadır: bir yerde durur, biri onu
yazar, biri okur. Bu dersteki içe aktarma da doğrudan ona yazdı.

Sonraki konu o dosyayı açar. Ölçülecek şeyler bellidir: durum nerede duruyor ve kim erişebiliyor,
içinde ne yazıyor — hassas değerler dahil — ve iki kişi aynı anda uyguladığında ne oluyor. Bu
sonuncusu en pahalı sorudur, çünkü iki uygulamanın birbirini ezmesi hiçbir planda görünmez.

## Durum ve Modüller

Kaydın nerede durduğu ve nasıl bölündüğü: durum dosyasının neden gerektiği ve neyi içerdiği, uzak durum ile kilitlemenin ekip çakışmasını önlemesi, büyük altyapının durum bölmeyle parçalanması, aynı tanımın çoklu örneği için çalışma alanları, girdi, çıktı ve sürümüyle modül yazımı, paylaşılan modül kayıtları ve hassas değerlerin durumda ve çıktıda görünürlüğü.

### Durum Dosyası

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/durum-dosyasi

Kaydın gerçeğe bağlandığı yer: durum dosyası olmadan eşlemenin okuma maliyeti ve hiç eşlenemeyen kalemler, durumun kalem başına tuttuğu bilgi, silinen kaynak ile kaydın kapsamadığı kaynağın ayrımı ve durumun kaybının bedeli.

Plan ile uygulamanın sonucu arasındaki fark ölçüldü: plan bir kalemi gösteriyor, uygulama başka
bir sonuç üretiyordu. O karşılaştırmanın sessiz bir dayanağı vardı. Plan, "bu kaynak zaten var"
ya da "bu kaynak yok" diyebilmek için gerçeğin bir yerde yazılı olduğunu varsayıyordu. O yazılı
hâl nerede duruyor?

Bu ders o dosyayı gerçekten yazar, gerçekten okur ve olmadığında ne yapılamadığını sayar.

#### Tanım, Uygulama ve İki Çıktı

Kurgu bir **bölgesel ölçüm ağı** kullanılacak: üç ortam, her ortamda sayaç okumalarını toplayan
iki makine, bir kuyruk, bir veri deposu, bir nesne deposu, bir alt ağ ve ağ kuralları. Ağ da,
sağlayıcı da modeldir; hiçbir gerçek altyapıya dokunulmaz.

Bir uygulama iki şey üretir: sağlayıcı tarafında **kaynaklar**, kendi tarafında **durum dosyası**.
Aşağıdaki betik üçünü de diske yazar.

```js
// uygula.mjs — kurgu "bolgesel olcum agi": tanimi kurar, uygulamayi modeller,
// saglayicidaki gercegi ve durum dosyasini diske yazar. Ucu de modeldir.
import { writeFileSync } from "node:fs";

const ortamlar = ["deneme", "hazirlik", "uretim"];
const tanim = [];
const ekle = (tur, ad, ortam, alanlar, bagimli = []) =>
  tanim.push({ ad, tur, ortam, alanlar, bagimli });

ortamlar.forEach((o, i) => {
  ekle("alt_ag", `alt_ag_${o}`, o, { blok: `10.${i}.0.0/16` });
  ekle("kuyruk", `okuma_kuyrugu_${o}`, o, { saklama_saat: 24, bolum: 4 });
  ekle("veri_deposu", `olcum_deposu_${o}`, o, { boyut_gb: 200, yedek: true });
  ekle("nesne_deposu", `arsiv_${o}`, o, { sinif: "seyrek" });
  for (const n of [1, 2])
    ekle("makine", `toplayici_${o}_${n}`, o, { cekirdek: 2, bellek_gb: 8 },
      [`alt_ag_${o}`, `okuma_kuyrugu_${o}`]);
  for (const kapi of [443, 5671, 9100])
    ekle("guvenlik_kurali", null, o, { yon: "giris", kapi }, [`alt_ag_${o}`]);
  ekle("yol_tablosu_girdisi", null, o, { hedef: "0.0.0.0/0", gecit: "kenar" }, [`alt_ag_${o}`]);
});
// Uretimde yedek toplayici icin ikinci 9100 kurali: tanimda iki kez gecen ayni kalem.
ekle("guvenlik_kurali", null, "uretim", { yon: "giris", kapi: 9100 }, ["alt_ag_uretim"]);

// Saglayicinin, tanimda verilmeyen alanlara kendiliginden koydugu degerler.
const varsayilan = {
  makine: { disk_gb: 40, izleme: "acik" }, kuyruk: { sifreleme: "acik" },
  veri_deposu: { bakim_penceresi: "pazar" }, nesne_deposu: { surum_tut: false },
  alt_ag: { ipv6: false }, guvenlik_kurali: {}, yol_tablosu_girdisi: {},
};
const adsiz = new Set(["guvenlik_kurali", "yol_tablosu_girdisi"]);

const kimlikler = tanim.map((_, i) => `kyn-${String(i + 1).padStart(4, "0")}`);
const adDizini = new Map(tanim.map((k, i) => [k.ad, kimlikler[i]]));

const gercek = [], durum = [];
tanim.forEach((k, i) => {
  const alanlar = { ...varsayilan[k.tur], ...k.alanlar };
  const ustAd = k.bagimli.find((b) => b.startsWith("alt_ag")) ?? null;
  gercek.push({
    kimlik: kimlikler[i], tur: k.tur, alanlar,
    etiket: adsiz.has(k.tur) ? {} : { ad: k.ad, ortam: k.ortam },
    ust: ustAd ? adDizini.get(ustAd) : null,
  });
  durum.push({
    kayit_adi: k.ad ?? `${k.tur}.${k.ortam}[${i}]`, tur: k.tur, ortam: k.ortam,
    kimlik: kimlikler[i], alanlar, bagimli: k.bagimli, saglayici_surumu: "3.4.0",
  });
});

writeFileSync("tanim.json", JSON.stringify(tanim, null, 1));
writeFileSync("saglayici.json", JSON.stringify(gercek, null, 1));
writeFileSync("durum.json", JSON.stringify(
  { bicim_surumu: 4, seri: 1, kalemler: durum }, null, 1));

const alanSayisi = durum.reduce((t, k) => t + Object.keys(k.alanlar).length, 0);
const tanimAlan = tanim.reduce((t, k) => t + Object.keys(k.alanlar).length, 0);
const kenar = durum.reduce((t, k) => t + k.bagimli.length, 0);
console.log(`tanim: ${tanim.length} kalem, ${tanimAlan} alan degeri`);
console.log(`durum.json: ${durum.length} kalem, ${alanSayisi} alan degeri, ` +
  `${kenar} bagimlilik kenari, ${alanSayisi - tanimAlan} alan yalniz durumda`);
console.log(`kalem basina kayit: kimlik 1, alan ${(alanSayisi / durum.length).toFixed(1)}, ` +
  `bagimlilik ${(kenar / durum.length).toFixed(1)}, saglayici surumu 1`);
console.log(`ornek kalem: ${JSON.stringify(durum[4])}`);
```

**DM1.** Sağlayıcı kimlikleri uygulama sırasına göre veriyor; modelde kimlik `kyn-0001`den
başlayarak sırayla atanır. **DM2.** Güvenlik kuralı ile yol tablosu girdisi **etiket taşımayan**
türlerdir; sağlayıcı bu kalemlere yalnız kimlik verir. **DM3.** Sağlayıcı, tanımda verilmeyen
alanlara varsayılan koyar ve bu değerler uygulamanın yanıtında geri döner.

```sh
node uygula.mjs
```

```
tanim: 31 kalem, 56 alan degeri
durum.json: 31 kalem, 80 alan degeri, 25 bagimlilik kenari, 24 alan yalniz durumda
kalem basina kayit: kimlik 1, alan 2.6, bagimlilik 0.8, saglayici surumu 1
ornek kalem: {"kayit_adi":"toplayici_deneme_1","tur":"makine","ortam":"deneme","kimlik":"kyn-0005","alanlar":{"disk_gb":40,"izleme":"acik","cekirdek":2,"bellek_gb":8},"bagimli":["alt_ag_deneme","okuma_kuyrugu_deneme"],"saglayici_surumu":"3.4.0"}
```

İlk sayı çifti ilginç: tanım 56 alan değeri yazıyor, durum 80 tutuyor. Aradaki **24 alan
yalnızca durumda var**; onları tanım yazmadı, sağlayıcı koydu. Kaydın "ne istediği" ile gerçeğin
"ne olduğu" daha uygulamanın ilk saniyesinde ayrışıyor ve durum dosyası bu ayrımın tutulduğu tek
yer.

#### Elle Müdahaleden Sonra

Uygulamanın üzerinden zaman geçti. Bir gece nöbetinde birileri sağlayıcı arayüzünden altyapıya
dokundu.

```js
// mudahale.mjs — saglayicidaki gercege elle dokunulmasini modeller: iki kalem silinir,
// dort kalem kayit disi acilir, bir alan degistirilir. Durum dosyasina dokunulmaz.
import { readFileSync, writeFileSync } from "node:fs";
const g = JSON.parse(readFileSync("saglayici.json", "utf8"));

const kimlikle = (ad) => g.find((k) => k.etiket.ad === ad).kimlik;
const altHazirlik = kimlikle("alt_ag_hazirlik"), altUretim = kimlikle("alt_ag_uretim");
const sil = (kosul) => { const i = g.findIndex(kosul); const [k] = g.splice(i, 1); return k.kimlik; };
const s1 = sil((k) => k.etiket.ad === "arsiv_deneme");
const s2 = sil((k) => k.tur === "yol_tablosu_girdisi" && k.ust === altHazirlik);

g.push({ kimlik: "kyn-9001", tur: "makine", alanlar: { cekirdek: 4, bellek_gb: 16, disk_gb: 40 },
  etiket: { ad: "gecici_toplayici", ortam: "uretim" }, ust: altUretim });
g.push({ kimlik: "kyn-9002", tur: "nesne_deposu", alanlar: { sinif: "sicak", surum_tut: false },
  etiket: { ad: "elle_arsiv", ortam: "uretim" }, ust: null });
g.push({ kimlik: "kyn-9003", tur: "guvenlik_kurali", alanlar: { yon: "giris", kapi: 22 },
  etiket: {}, ust: altUretim });
g.push({ kimlik: "kyn-9004", tur: "guvenlik_kurali", alanlar: { yon: "giris", kapi: 443 },
  etiket: {}, ust: altUretim });

const m = g.find((k) => k.etiket.ad === "toplayici_uretim_1");
m.alanlar.bellek_gb = 16;

writeFileSync("saglayici.json", JSON.stringify(g, null, 1));
console.log(`silinen: ${s1}, ${s2} | kayit disi acilan: 4 | elle degisen alan: 1`);
console.log(`saglayicidaki kalem sayisi: ${g.length}`);
```

```sh
node uygula.mjs > /dev/null   # duzenek bastan kurulur
node mudahale.mjs
```

```
silinen: kyn-0004, kyn-0020 | kayit disi acilan: 4 | elle degisen alan: 1
saglayicidaki kalem sayisi: 33
```

#### Durum Olmadan Eşleme

Şimdi soru şu: durum dosyası hiç olmasaydı, kayıt ile gerçeği eşlemek için ne gerekirdi? Tek yol
sağlayıcıyı taramak ve mantıksal adları etiketlerle eşleştirmektir.

```js
// eslesme.mjs — durum dosyasina bakmadan, kaydi gercege eslemeye calisir.
import { readFileSync } from "node:fs";
const tanim = JSON.parse(readFileSync("tanim.json", "utf8"));
const gercek = JSON.parse(readFileSync("saglayici.json", "utf8"));
const adsiz = new Set(["guvenlik_kurali", "yol_tablosu_girdisi"]);

const turler = [...new Set(gercek.map((k) => k.tur))];
let liste = turler.length;                       // her tur icin bir listeleme
let ayrinti = gercek.filter((k) => adsiz.has(k.tur)).length;  // adsizlarin her adayi okunur

// Adiyla esleme: liste yaniti etiketi tasidigi icin ek okuma gerektirmez.
const adDizini = new Map(gercek.filter((k) => k.etiket.ad).map((k) => [k.etiket.ad, k]));
const kullanilan = new Set();
let adla = 0, adlaYok = 0;
for (const k of tanim.filter((k) => k.ad)) {
  const bul = adDizini.get(k.ad);
  if (bul) { adla++; kullanilan.add(bul.kimlik); } else adlaYok++;
}

// Adsizlar: yalniz icerik ve bagli olduklari alt ag ile ayirt edilebilir.
const imza = (tur, ust, alanlar) =>
  `${tur}|${ust}|${Object.keys(alanlar).sort().map((a) => `${a}=${alanlar[a]}`).join(",")}`;
const gImza = new Map();
for (const k of gercek.filter((k) => adsiz.has(k.tur)))
  gImza.set(imza(k.tur, k.ust, k.alanlar), (gImza.get(imza(k.tur, k.ust, k.alanlar)) ?? 0) + 1);
const tImza = new Map();
for (const k of tanim.filter((k) => adsiz.has(k.tur))) {
  const ust = adDizini.get(k.bagimli.find((b) => b.startsWith("alt_ag")))?.kimlik ?? null;
  const i = imza(k.tur, ust, k.alanlar);
  tImza.set(i, (tImza.get(i) ?? 0) + 1);
}
let tekil = 0, belirsiz = 0, adsizYok = 0;
for (const [i, adet] of tImza) {
  const g = gImza.get(i) ?? 0;
  if (g === 0) adsizYok += adet;
  else if (adet === 1 && g === 1) tekil++;
  else belirsiz += adet;
}
const sahipsiz = gercek.filter((k) => !kullanilan.has(k.kimlik) && !adsiz.has(k.tur)).length;
const sahipsizAdsiz = [...gImza].reduce((t, [i, g]) => t + Math.max(0, g - (tImza.get(i) ?? 0)), 0);

console.log(`durumsuz esleme — okuma: ${liste} listeleme + ${ayrinti} ayrinti = ${liste + ayrinti}`);
console.log(`  adiyla eslesen: ${adla}/${tanim.filter((k) => k.ad).length}`);
console.log(`  icerikle tekil eslesen: ${tekil}/${tanim.filter((k) => adsiz.has(k.tur)).length}`);
console.log(`  hic eslenemeyen (adsiz, imzasi cakisiyor): ${belirsiz}`);
console.log(`  kayitta var gercekte yok: ${adlaYok + adsizYok} -> silinmis mi, hic olusturulmamis mi: ayirt edilemez`);
console.log(`  gercekte var kayitta yok: ${sahipsiz} adli + ${sahipsizAdsiz} adsiz`);
```

**DM4.** Tür başına bir listeleme bir okuma sayılır; etiket listede döner, adsız kalemin alanları
ise ancak ayrıntı okumasıyla görülür.

```sh
node eslesme.mjs
```

```
durumsuz esleme — okuma: 7 listeleme + 14 ayrinti = 21
  adiyla eslesen: 17/18
  icerikle tekil eslesen: 9/13
  hic eslenemeyen (adsiz, imzasi cakisiyor): 3
  kayitta var gercekte yok: 2 -> silinmis mi, hic olusturulmamis mi: ayirt edilemez
  gercekte var kayitta yok: 2 adli + 2 adsiz
```

Yirmi bir okumadan sonra elde kalan tablo eksiktir. Otuz bir kalemin 26'sı eşleşti, **3 kalem hiç
eşlenemedi**. Üçü de adı olmayan kaynak: üretimdeki iki 9100 kuralı birbirinin tıpatıp aynısı,
443 kuralının ise sağlayıcıda iki karşılığı var — biri tanımdan, biri elden. İçerikleri aynı olan
iki kalem arasında hangi kimliğin hangi kayıt satırına ait olduğunu söyleyecek bir bilgi yok.

İkinci kayıp daha ağırdır: eksik iki kalem için "silinmiş" ile "hiç oluşturulmamış" ayrımı
yapılamıyor. Kayıt ikisini de aynı biçimde görür ve ikisine de aynı kararı verir — oluştur.
Silinen kaynağın silindiği hiçbir zaman öğrenilmez, yalnız yeniden yaratılır.

#### Durum Dosyası Neyi Tutuyor

Örnek kalem, durumun kalem başına dört ayrı bilgi tuttuğunu gösteriyor: **kimlik**
(`kyn-0005`), **alan değerleri** (dördü; ikisi tanımdan, ikisi sağlayıcı varsayılanından),
**bağımlılık** (iki kayıt adı) ve **sağlayıcı sürümü** (`3.4.0`). Otuz bir kalem için toplam:
31 kimlik, 80 alan değeri, 25 bağımlılık kenarı, 31 sürüm damgası.

Bu dört kalemin her biri ayrı bir işi yapar. Kimlik eşlemeyi tekilleştirir. Alan değerleri
sapmanın karşılaştırma tabanıdır — sağlayıcının koyduğu varsayılan da kaydedildiği için,
varsayılan sağlayıcı sürümüyle değişirse fark görünür olur. Bağımlılık kenarı silme sırasını
verir; kayıt dosyasındaki metinden değil, uygulanmış hâlden okunur. Sürüm damgası ise aynı
tanımın farklı sağlayıcı sürümünde farklı sonuç ürettiğini saptamayı sağlar.

#### Durumla Karşılaştırma

```js
// durum-oku.mjs — durum dosyasindaki kimlikleri saglayicidaki gercekle karsilastirir.
import { readFileSync } from "node:fs";
const d = JSON.parse(readFileSync("durum.json", "utf8"));
const gercek = new Map(JSON.parse(readFileSync("saglayici.json", "utf8")).map((k) => [k.kimlik, k]));

let okuma = 1, eslesen = 0;                      // 1: durum dosyasinin kendisi
const silinen = [], sapan = [];
for (const k of d.kalemler) {
  okuma++;                                       // kimlikle tek kalem okumasi
  const g = gercek.get(k.kimlik);
  if (!g) { silinen.push(`${k.kayit_adi} (${k.kimlik})`); continue; }
  eslesen++;
  for (const [alan, deger] of Object.entries(k.alanlar))
    if (g.alanlar[alan] !== deger) sapan.push(`${k.kayit_adi}.${alan} ${deger} -> ${g.alanlar[alan]}`);
}
const durumKimlikleri = new Set(d.kalemler.map((k) => k.kimlik));
const kayitDisi = [...gercek.values()].filter((g) => !durumKimlikleri.has(g.kimlik));

console.log(`durumla karsilastirma — okuma: 1 durum dosyasi + ${okuma - 1} kimlik = ${okuma}`);
console.log(`  eslesen: ${eslesen}/${d.kalemler.length}, hic eslenemeyen: 0`);
console.log(`  silinmis (durumda kimlik var, saglayicida yok): ${silinen.length} -> ${silinen.join("; ")}`);
console.log(`  sapan alan: ${sapan.length} -> ${sapan.join("; ")}`);
console.log(`  kaydin kapsamadigi (saglayicida var, durumda yok): ${kayitDisi.length} -> ` +
  kayitDisi.map((g) => g.etiket.ad ?? `${g.tur}/${g.kimlik}`).join(", "));
```

```sh
node durum-oku.mjs
```

```
durumla karsilastirma — okuma: 1 durum dosyasi + 31 kimlik = 32
  eslesen: 29/31, hic eslenemeyen: 0
  silinmis (durumda kimlik var, saglayicida yok): 2 -> arsiv_deneme (kyn-0004); yol_tablosu_girdisi.hazirlik[19] (kyn-0020)
  sapan alan: 1 -> toplayici_uretim_1.bellek_gb 8 -> 16
  kaydin kapsamadigi (saglayicida var, durumda yok): 4 -> gecici_toplayici, elle_arsiv, guvenlik_kurali/kyn-9003, guvenlik_kurali/kyn-9004
```

Okuma sayısı 21'den 32'ye çıktı — durum eşlemeyi **ucuzlatmıyor**, kesinleştiriyor. Kazanılan üç
sayı şudur: eşlenemeyen kalem 3'ten 0'a indi, iki silinmiş kalem adıyla ve kimliğiyle bulundu,
bir sapmış alan (`bellek_gb` 8 yerine 16) ortaya çıktı. Bu üçü durumsuz eşlemede görünmüyordu.

Dördüncü sayı kaydın **kapsamını** verir: sağlayıcıdaki 33 kalemin 4'ü hiçbir kaydın içinde
değil. İki makine ve nesne deposu adlarıyla, iki güvenlik kuralı yalnız kimlikleriyle duruyor.
Kayıt bunları ne planlar, ne siler, ne de sapma sayar; altyapının bu bölümü kaydın dışındadır ve
orada olduğu ancak kimse bakmadan silinmediği sürece bilinir.

#### Durumun Kaybı

Durum dosyası, altyapının kendisi kadar kritik bir kayıttır. Kaybının bedeli sayılabilir.

```js
// kayip.mjs — durum dosyasi kaybolursa neyin geri kazanilabildigini sayar.
import { readFileSync, rmSync, existsSync } from "node:fs";
const d = JSON.parse(readFileSync("durum.json", "utf8"));
const kayip = { kimlik: d.kalemler.length, kenar: 0, surum: 0, alan: 0 };
for (const k of d.kalemler) {
  kayip.kenar += k.bagimli.length;
  kayip.surum += k.saglayici_surumu ? 1 : 0;
  kayip.alan += Object.keys(k.alanlar).length;
}
rmSync("durum.json");
console.log(`durum.json silindi; dosya duruyor mu: ${existsSync("durum.json")}`);
console.log(`kaybolan bilgi: ${kayip.kimlik} kimlik, ${kayip.alan} alan degeri, ` +
  `${kayip.kenar} bagimlilik kenari, ${kayip.surum} saglayici surumu`);
console.log(`geriye kalan tek yol: durumsuz esleme`);
```

```sh
node uygula.mjs > /dev/null && node mudahale.mjs > /dev/null   # duzenek bastan kurulur
node kayip.mjs
```

```
durum.json silindi; dosya duruyor mu: false
kaybolan bilgi: 31 kimlik, 80 alan degeri, 25 bagimlilik kenari, 31 saglayici surumu
geriye kalan tek yol: durumsuz esleme
```

Dosya gittiğinde altyapı gitmez; **yönetilebilirliği** gider. Geriye kalan tek yol yukarıdaki
durumsuz eşlemedir ve onun tavanı bellidir: 26 kalem tek tek içe aktarılarak geri kazanılabilir,
3 kalem hiçbir biçimde eşlenemez. İki silinmiş kalemin silinmiş olduğu bilgisi de dosyayla
birlikte gitmiştir. Kayıt yeniden kurulana kadar her uygulama, var olan kaynakları yeniden
oluşturmayı planlar.

**DM5.** Durum dosyası tek bir çalışma dizininde, düz metin olarak duruyor. Bu, kaydın kendi
riskinin ikinci yüzüdür: dosya 80 alan değerini olduğu gibi tutar. Değişkenle verilen bir
bağlantı dizesi ya da erişim ölçütü tanımda gizlenmiş olsa bile uygulama sonrası hâliyle burada
yazılıdır ve dosyayı okuyabilen herkes onu okur. **DM6.** Dosyayı yazan tek şey uygulamadır;
elle düzeltilmesi durumu gerçekten uzaklaştırır, çünkü sağlayıcı tarafında karşılığı yoktur.

#### Özet

- Uygulama iki çıktı üretti: sağlayıcıdaki 31 kaynak ve bunların kaydı olan durum dosyası; tanım
  56 alan değeri yazarken durum 80 tuttu, 24 alanı sağlayıcı koydu.
- Durum olmadan eşleme 21 okuma sürdü ve 3 kalem hiç eşlenemedi; üçü de adı olmayan kaynaktı,
  imzaları birbirinin aynısıydı.
- Durum olmadan "silinmiş" ile "hiç oluşturulmamış" ayrılamıyor; durumla iki silinmiş kalem
  kimliğiyle bulundu ve bir sapmış alan ortaya çıktı.
- Durum kalem başına dört bilgi tutuyor: kimlik, alan değerleri, bağımlılık, sağlayıcı sürümü —
  31 kalem için 31 kimlik, 80 alan, 25 kenar, 31 sürüm damgası.
- Sağlayıcıdaki 33 kalemin 4'ü kaydın dışında kaldı; kayıt onları ne planlıyor ne sayıyor.
- Durumun kaybında 26 kalem içe aktarmayla geri gelir, 3 kalem gelmez; kayıp altyapıyı yok
  etmez, yönetilemez kılar.

#### Sonraki Adım

Bu dersin tamamı tek bir kişinin tek bir dizinde çalıştığını varsaydı. Durum dosyası yerel
diskte duruyordu ve ona aynı anda tek bir süreç dokunuyordu. Ekipte iki kişi aynı ortama aynı
anda uygulama başlattığında bu varsayım düşer: ikisi de aynı durumu okur, ikisi de değiştirir,
ikincisi birincinin yazdığının üstüne yazar. Sonraki ders iki eşzamanlı uygulamayı gerçekten
koşturur, kilitsiz durumda kaybolan yazmayı sayar, kilitle bekleme turlarını ölçer ve kilidi
bırakmadan düşen sürecin ardında bıraktığı artık kilidi ele alır.

### Uzak Durum ve Kilitleme

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/uzak-durum-ve-kilitleme

Aynı duruma iki uygulamanın aynı anda yazması: kilitsiz durumda kaybolan yazma ve kayıt dışı kalan kaynak, kilitle bekleme turu, kilit tutma süresinin kuyruğu, kilit bırakılmadan düşen sürecin artık kilidi ve yerel dosya ile uzak deponun kapsama farkı.

Önceki ders durum dosyasını tek bir dizinde, tek bir sürecin elinde bıraktı. O varsayım bir
kişilik bir ekipte doğrudur. İkinci kişi geldiğinde çöker: iki uygulama aynı durumu okur, ikisi
de kaynak oluşturur, ikisi de aynı dosyaya yazar. Bu ders o çakışmayı gerçekten üretir, bedelini
sayar ve kilidin ne kadarını çözdüğünü ölçer.

Eşzamanlı güncellemenin iyimser ve kötümser çözümleri Veri Erişim Katmanı kursunda ölçüldü;
oradaki mekanik burada tekrarlanmaz. Fark taneciktedir: orada kilitlenen bir satırdı ve farklı
satırlara yazan iki istemci hiç karşılaşmıyordu, burada kilitlenen **durum dosyasının tamamıdır**.
Üç ortam tek dosyada duruyorsa deneme ortamına makine ekleyen kişi, üretimin kuyruğunu büyüten
kişiyi bekler; eşzamanlılık tavanı bire iner.

#### Uzak Durum Deposu ve İki Uygulayıcı

Aşağıdaki betik tek bir uygulamayı modeller: durumu okur, uygulama süresi kadar çalışır, kendi
kalemlerini ekler ve durumu geri yazar. **DM7.** Uzak durum deposu bir dizinle modellenir; iki
süreç de aynı `depo/durum.json` dosyasına erişir. **DM8.** Uygulama süresi meşgul döngüyle
modellenir; gerçekte o süre sağlayıcıya yapılan çağrılarla geçer.

```js
// uygulayici.mjs — uzak durum deposu uzerinde tek bir uygulamayi modeller.
// modlar: kur | uygula <ad> <adet> <kilit|kilitsiz> <sure_ms> [bekleme_tavani_ms] | ozet
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, rmSync,
  openSync, closeSync, unlinkSync, existsSync } from "node:fs";

const DEPO = "depo", DURUM = "depo/durum.json", KILIT = "depo/kilit.json";
const mod = process.argv[2];
const bekle = (ms) => { const b = Date.now() + ms; while (Date.now() < b); }; // uygulama suresi modeli

if (mod === "kur") {
  rmSync(DEPO, { recursive: true, force: true });
  mkdirSync(DEPO);
  writeFileSync(DURUM, JSON.stringify({ seri: 1, kalemler: [] }));
  process.exit(0);
}

if (mod === "ozet") {
  const d = JSON.parse(readFileSync(DURUM, "utf8"));
  const sahip = {};
  for (const k of d.kalemler) sahip[k[0]] = (sahip[k[0]] ?? 0) + 1;
  const dagilim = Object.keys(sahip).sort().map((a) => `${a}=${sahip[a]}`).join(" ") || "yok";
  const g = existsSync(`${DEPO}/gunluk.txt`) ? readFileSync(`${DEPO}/gunluk.txt`, "utf8") : "";
  for (const s of g.trim().split("\n").sort()) console.log(s);
  const olusan = [...g.matchAll(/(\d+) kalem yazdi/g)].reduce((t, m) => t + Number(m[1]), 0);
  console.log(`durum: ${d.kalemler.length} kalem (${dagilim}), seri ${d.seri}, ` +
    `kilit ${existsSync(KILIT) ? "duruyor" : "yok"}`);
  console.log(`gercekte olusan ${olusan} kaynak, kayitta gorunen ${d.kalemler.length} ` +
    `-> kayit disi ${olusan - d.kalemler.length}`);
  process.exit(0);
}

if (mod !== "uygula") {
  console.log("kullanim: kur | uygula <ad> <adet> <kilit|kilitsiz> <sure_ms> | ozet");
  process.exit(0);
}

const [ad, adet, kilitModu, sureMs] =
  [process.argv[3], Number(process.argv[4]), process.argv[5], Number(process.argv[6])];
let tur = 0;
if (kilitModu === "kilit") {
  const tavan = Date.now() + Number(process.argv[7] ?? 10000);
  for (;;) {
    try {                                        // "wx": dosya varsa hata verir, atomiktir
      const f = openSync(KILIT, "wx");
      writeFileSync(f, JSON.stringify({ sahip: ad, an: "kurgu-zaman-damgasi" }));
      closeSync(f); break;
    } catch {
      tur++;
      if (Date.now() > tavan) { appendFileSync(`${DEPO}/gunluk.txt`,
        `${ad}: kilit alinamadi, ${tur} bekleme turu\n`); process.exit(1); }
      bekle(25);
    }
  }
}
const d = JSON.parse(readFileSync(DURUM, "utf8"));
writeFileSync(`${DEPO}/${ad}-okudu`, "");
if (kilitModu === "kilitsiz")                    // iki uygulama da yazmadan once okusun
  while (!existsSync(`${DEPO}/a-okudu`) || !existsSync(`${DEPO}/b-okudu`)) bekle(5);
bekle(sureMs);
for (let i = 1; i <= adet; i++) d.kalemler.push(`${ad}-kalem-${i}`);
d.seri += 1;
writeFileSync(DURUM, JSON.stringify(d));
if (kilitModu === "kilit") unlinkSync(KILIT);
appendFileSync(`${DEPO}/gunluk.txt`,
  `${ad}: ${adet} kalem yazdi, okudugu seri ${d.seri - 1}, bekleme turu ${tur}\n`);
```

Kurgu ölçüm ağının hazırlık ortamına iki kişi aynı anda kaynak ekliyor: `a` on iki kalem, `b` on
iki kalem. **DM9.** İkisinin de yazmadan önce okuduğundan emin olmak için işaret dosyalarıyla
eşitlenirler; `b`'nin uygulama süresi altmış milisaniye uzun olduğu için son yazan odur.

```sh
# kilitsiz.sh — iki uygulama ayni uzak durumu esanli degistirir, kilit yok
node uygulayici.mjs kur
node uygulayici.mjs uygula a 12 kilitsiz 0 &
node uygulayici.mjs uygula b 12 kilitsiz 60 &
wait
node uygulayici.mjs ozet
```

```
a: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
b: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
durum: 12 kalem (b=12), seri 2, kilit yok
gercekte olusan 24 kaynak, kayitta gorunen 12 -> kayit disi 12
```

Üç sayı üç ayrı zararı gösteriyor. Birincisi **kaybolan yazma**: iki uygulama on ikişer kalem
yazdı, dosyada on iki kalem kaldı; `a`'nın yazması `b` tarafından üzerine yazıldı. İkincisi
**kayıt ile gerçek arasındaki fark**: `a` kaynakları gerçekten oluşturdu, sağlayıcıda yirmi dört
kaynak var, kayıt on iki diyor — on iki kaynak kaydın dışına düştü ve bir daha ne planlanacak ne
silinecek. Üçüncüsü **tutarsız durum**: iki uygulama koştuğu hâlde seri numarası bir arttı;
ikisi de "okuduğu seri 1" diyor. Kayıt, tek bir uygulama olmuş gibi duruyor.

#### Kilit

Çözüm, durumu okumadan önce yazma hakkını almaktır. Kilit dosya sisteminin atomik "varsa hata
ver" oluşturmasıyla alınır: iki süreç aynı anda denese bile yalnız biri dosyayı yaratabilir.
Öteki yakalar, yirmi beş milisaniye bekler ve yeniden dener.

```sh
# kilitli.sh — ayni iki uygulama, bu kez durum kilidiyle
node uygulayici.mjs kur
node uygulayici.mjs uygula a 12 kilit 250 &
until [ -f depo/kilit.json ]; do :; done   # a kilidi aldi
node uygulayici.mjs uygula b 12 kilit 250 &
wait
node uygulayici.mjs ozet
```

```
a: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
b: 12 kalem yazdi, okudugu seri 2, bekleme turu 10
durum: 24 kalem (a=12 b=12), seri 3, kilit yok
gercekte olusan 24 kaynak, kayitta gorunen 24 -> kayit disi 0
```

Yirmi dört kalem, seri üç, kayıt dışı sıfır. `b` on bekleme turu harcadı — iki yüz elli
milisaniyelik tutmanın yirmi beş milisaniyelik yoklamaya bölümü — ve **güncel** durumu okudu:
"okuduğu seri 2". Kaybolan yazma ile beklemenin takası açıktır — kilidin ürettiği tek
maliyet `b`'nin iki yüz elli milisaniyesidir, kazandığı ise on iki kaynağın kayda girmesidir.

Kilit dosyası uygulamadan sonra siliniyor: kilit durumun bir parçası değil, duruma erişimin
kapısıdır.

Kilidin okuma **öncesinde** alınması da rastgele bir sıralama değildir. `b` kilidi aldığında
durumu yeniden okur ve seri iki değerini görür; önce okuyup sonra kilit almaya çalışsaydı elinde
seri bir kalırdı ve yazdığı anda `a`'nın on iki kalemini silerdi. Kilitsiz ölçümdeki sonuç, kilit
yanlış yerde alınırsa geri gelir. Sıra tektir: kilit, okuma, uygulama, yazma, bırakma.

#### Kilit Süresinin Taranması

Bekleme yalnız kilidin tutulduğu süre kadardır. Uygulama uzarsa arkadaki herkes o kadar bekler.

Ölçüm kuralı: ölçülen süre basılmaz, yalnız bir bant denetiminden geçirilir — beklenenin 0,7 ile
2,0 katı arası. Alt sınır kuyruğun gerçekten oluştuğunu doğrular; üst sınır, yirmi beş
milisaniyelik yoklama tanesi ile süreç başlatma yükü kısa tutmalarda beklenenin katına
yaklaşabildiği için geniş bırakılmıştır.

```js
// tarama.mjs — kilit tutma suresi buyudukce dort uygulamanin kuyrugu nasil uzuyor.
// Basilan her sey kosumdan bagimsizdir: beklenen degerler kuyruk aritmetiginden gelir,
// olculen sure ise yalnizca bir bant denetiminden gecirilip evet/hayir olarak basilir.
import { spawn } from "node:child_process";
import { readFileSync } from "node:fs";

const kos = (...a) => new Promise((c) =>
  spawn("node", ["uygulayici.mjs", ...a], { stdio: "ignore" }).on("exit", c));
const N = 4, YOKLAMA = 25, ALT = 0.7, UST = 2.0;
console.log(`kuyruk aritmetigi (${N} uygulama, kosumdan bagimsiz): ` +
  `toplam bekleme = ${(N * (N - 1)) / 2} x tutma, en uzun = ${N - 1} x tutma`);
console.log(`bant: olculen, beklenenin ${ALT}-${UST} kati arasinda olmali`);

const dogruluk = [];
for (const tutma of [50, 200, 800]) {
  await kos("kur");
  await Promise.all(["a", "b", "c", "d"].map(
    (ad) => kos("uygula", ad, "12", "kilit", String(tutma))));
  const turlar = readFileSync("depo/gunluk.txt", "utf8").trim().split("\n")
    .map((s) => Number(/bekleme turu (\d+)/.exec(s)[1]));
  const d = JSON.parse(readFileSync("depo/durum.json", "utf8"));
  dogruluk.push(`${d.kalemler.length} kalem, seri ${d.seri}`);

  const bekToplam = ((N * (N - 1)) / 2) * tutma, bekEnUzun = (N - 1) * tutma;
  const bantta = (olculen, beklenen) =>
    olculen >= ALT * beklenen && olculen <= UST * beklenen ? "evet" : "hayir";
  console.log(`tutma ${String(tutma).padStart(3)} ms | beklenen toplam ` +
    `${String(bekToplam).padStart(4)} ms, en uzun ${String(bekEnUzun).padStart(4)} ms | ` +
    `olculen bantta: ${bantta(turlar.reduce((x, y) => x + y, 0) * YOKLAMA, bekToplam)} / ` +
    `${bantta(Math.max(...turlar) * YOKLAMA, bekEnUzun)}`);
}
console.log(`dogruluk (kosumdan bagimsiz): ` +
  `${new Set(dogruluk).size === 1 ? `uc tutma suresinde de ${dogruluk[0]}` : dogruluk.join(" | ")}`);
```

```
kuyruk aritmetigi (4 uygulama, kosumdan bagimsiz): toplam bekleme = 6 x tutma, en uzun = 3 x tutma
bant: olculen, beklenenin 0.7-2 kati arasinda olmali
tutma  50 ms | beklenen toplam  300 ms, en uzun  150 ms | olculen bantta: evet / evet
tutma 200 ms | beklenen toplam 1200 ms, en uzun  600 ms | olculen bantta: evet / evet
tutma 800 ms | beklenen toplam 4800 ms, en uzun 2400 ms | olculen bantta: evet / evet
dogruluk (kosumdan bagimsiz): uc tutma suresinde de 48 kalem, seri 5
```

İlk satır ölçümden değil, kuyruğun kendisinden gelir: dört uygulama sıraya girdiğinde birincisi
hiç beklemez, ikincisi bir, üçüncüsü iki, dördüncüsü üç kilit tutma süresi bekler. Toplam altı,
en uzunu üç tutma süresidir. Dersin iddiası bu aritmetiğe dayanır — sıranın uzunluğu uygulama
süresiyle doğru orantılıdır — ve elli milisaniyelik tutmada üç yüz milisaniye olan toplam bekleme,
sekiz yüzde dört bin sekiz yüze çıkar: on altı kat tutma, on altı kat bekleme.

Ölçüm bunu doğrulamakla yetinir: üç tutma süresinde de **evet**. Sapmanın kaynağı sabit bir
yüktür — süreç başlatma ve yoklama tanesi — ve tutma büyüdükçe payı erir; bu koşumda sekiz yüz
milisaniyelik satırda ölçülen, beklenenin yüzde ikisi kadar üstündeydi.

Son satır da koşumdan bağımsızdır: kırk sekiz kalem, seri beş. Kilit doğruluğu bedava vermez,
**sıraya** karşılığında verir; sıranın boyu ise ölçülmeden önce hesaplanabilir.

Buradan işletme kuralı çıkar: uzun uygulamalar bir ekibin tamamını bekletir. Sırayı kısaltmanın
yolu kilidi hızlandırmak değil, kilidin kapsadığı alanı küçültmektir.

#### Artık Kilit

Kilidi alan süreç düşerse kilit dosyası ardında kalır. **DM10.** Aşağıdaki düzenek bunu doğrudan
kurar: kilit dosyası yazılır, sahibi hiç yazmadan yok olur.

```js
// artik-kilit.mjs — kilidi birakmadan dusen surecin ardinda biraktigi kilit ve elle acma.
import { writeFileSync, readFileSync, existsSync, unlinkSync, rmSync, mkdirSync } from "node:fs";
import { spawnSync } from "node:child_process";

rmSync("depo", { recursive: true, force: true });
mkdirSync("depo");
writeFileSync("depo/durum.json", JSON.stringify({ seri: 1, kalemler: [] }));
// a kilidi aldi ve yazmadan dustu; kilit dosyasi ardinda kaldi.
writeFileSync("depo/kilit.json", JSON.stringify({ sahip: "a", an: "kurgu-zaman-damgasi" }));

const sonSatir = () => readFileSync("depo/gunluk.txt", "utf8").trim().split("\n").at(-1);
const deneB = () => spawnSync("node",
  ["uygulayici.mjs", "uygula", "b", "12", "kilit", "0", "1000"]).status;

console.log(`artik kilit: ${readFileSync("depo/kilit.json", "utf8")}`);
console.log(`b denedi (cikis ${deneB()}) -> ${sonSatir()}`);
console.log(`durum: ${JSON.parse(readFileSync("depo/durum.json", "utf8")).kalemler.length} kalem`);

unlinkSync("depo/kilit.json");                   // elle acma
console.log(`kilit elle acildi; dosya duruyor mu: ${existsSync("depo/kilit.json")}`);
console.log(`b yeniden denedi (cikis ${deneB()}) -> ${sonSatir()}`);
console.log(`durum: ${JSON.parse(readFileSync("depo/durum.json", "utf8")).kalemler.length} kalem`);
console.log(`kilit kaydindaki alan: 2 (sahip, an) | sahibin hala calisip calismadigini soyleyen alan: 0`);
```

```
artik kilit: {"sahip":"a","an":"kurgu-zaman-damgasi"}
b denedi (cikis 1) -> b: kilit alinamadi, 42 bekleme turu
durum: 0 kalem
kilit elle acildi; dosya duruyor mu: false
b yeniden denedi (cikis 0) -> b: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
durum: 12 kalem
kilit kaydindaki alan: 2 (sahip, an) | sahibin hala calisip calismadigini soyleyen alan: 0
```

`b` kırk iki tur bekledi, bir saniyelik tavanı doldurdu ve sıfır kalem yazabildi. Kilit
kendiliğinden açılmaz; onu bırakacak süreç artık yok. Buradan sonrası kaydın **kendi riskidir**:
altyapıya hiç dokunulmadığı hâlde altyapı yönetilemez durumda ve tek çıkış kilidi elle açmaktır.

Elle açma iki adımda biter, ama kararı verecek kişinin elinde iki alan vardır: sahibin adı ve bir
zaman damgası. Sahibin **hâlâ çalışıp çalışmadığını** söyleyen alan sayısı sıfırdır. Karar yanlış
verilirse — sahip uzun bir uygulamanın ortasındaysa — kilit anlamını yitirir ve ilk bölümdeki
ölçüm geri gelir: on iki kalem kaybolur, on iki kaynak kayıt dışı kalır.

#### Yerel Dosya ile Uzak Deponun Farkı

Kilidin çalışabilmesi için herkesin **aynı** duruma bakması gerekir. Durum her uygulayıcının
kendi diskinde durursa kilitlenecek ortak bir şey yoktur.

```js
// yerel.mjs — durumun her uygulayicinin kendi diskinde durmasi: iki kayit, tek gercek.
import { writeFileSync, readFileSync, rmSync, mkdirSync } from "node:fs";

rmSync("yerel", { recursive: true, force: true });
mkdirSync("yerel");
const gercek = [];
for (const ad of ["a", "b"]) {                   // iki kisi kendi kopyasinda calisti
  const kendi = [];
  for (let i = 1; i <= 12; i++) { kendi.push(`${ad}-kalem-${i}`); gercek.push(`${ad}-kalem-${i}`); }
  writeFileSync(`yerel/durum-${ad}.json`, JSON.stringify({ seri: 2, kalemler: kendi }));
}
const oku = (ad) => JSON.parse(readFileSync(`yerel/durum-${ad}.json`, "utf8")).kalemler;
const a = oku("a"), b = oku("b");
const oran = (n) => `%${Math.round((100 * n) / gercek.length)}`;

console.log(`saglayicidaki gercek kaynak: ${gercek.length}`);
console.log(`yerel dosya: 2 -> durum-a ${a.length} kalem (kapsama ${oran(a.length)}), ` +
  `durum-b ${b.length} kalem (kapsama ${oran(b.length)})`);
console.log(`ortak kalem: ${a.filter((k) => b.includes(k)).length}, ` +
  `tamamini bilen dosya: 0, iki dosyanin toplami: ${a.length + b.length}`);
console.log(`sapma taramasi durum-a ile kosarsa kayit disi gorunen: ` +
  `${gercek.filter((k) => !a.includes(k)).length} kalem (hepsi yanlis)`);
console.log(`uzak depo ile: 1 dosya, ${gercek.length} kalem, kapsama %100, yanlis kayit disi 0`);
```

```
saglayicidaki gercek kaynak: 24
yerel dosya: 2 -> durum-a 12 kalem (kapsama %50), durum-b 12 kalem (kapsama %50)
ortak kalem: 0, tamamini bilen dosya: 0, iki dosyanin toplami: 24
sapma taramasi durum-a ile kosarsa kayit disi gorunen: 12 kalem (hepsi yanlis)
uzak depo ile: 1 dosya, 24 kalem, kapsama %100, yanlis kayit disi 0
```

**DM11.** İki kişi hiç haberleşmeden on ikişer kaynak açıyor. Sonuç iki ayrı kayıt: her biri
kendi içinde tutarlı, hiçbiri gerçeğin yarısından fazlasını bilmiyor. Kaybolan yazma yok — çünkü
paylaşılan bir dosya yok — ama fark daha kötüdür: iki kayıt da kendi kapsamında doğru olduğu için
kimse yanlış olduğunu fark etmez. Bir sapma taraması `durum-a` ile koşarsa on iki kalemi kayıt
dışı ilan eder; on ikisi de yanlış alarmdır, kaynakların sahibi vardır, sahibi başka bir dosyadır.

Uzak durum deposunun verdiği tek şey budur: **tek kayıt**. Kapsama yüzde yüze çıkar, kilitlenecek
bir nesne oluşur, yanlış kayıt dışı sayısı sıfırlanır. **DM12.** Depo, dosya oluşturmayı atomik
yapabilen bir hizmet varsayar; bu garanti yoksa kilit de yoktur.

Kazanç bedavaya gelmez ve bedeli kaydın kendi riskidir. İki yerel dosya iki ayrı arıza noktasıydı;
tek uzak kayıt tek arıza noktasıdır. Yarım kalan bir yazma dosyayı bozarsa önceki dersin sayısı
geri gelir: yirmi altı kalem içe aktarmayla kurtarılır, üç kalem kurtarılamaz. Depo sürüm
tutmuyorsa dönülecek bir kopya da yoktur. İkinci risk erişimdir: dosya artık bir kişinin diskinde
değil, ekibin tamamının okuyabildiği bir yerdedir ve önceki derste sayılan seksen alan değerinin
tamamı oradadır.

#### Özet

- Kilitsiz iki eşzamanlı uygulama on ikişer kalem yazdı, kayıtta on iki kalem kaldı: bir yazma
  kayboldu, on iki kaynak kayıt dışına düştü, seri iki uygulamaya rağmen bir arttı.
- Atomik dosya oluşturmayla kurulan kilit aynı ölçümde yirmi dört kalem, seri üç ve sıfır kayıt
  dışı üretti; bedeli ikinci uygulamanın on bekleme turudur.
- Dört uygulamanın kuyruğu koşumdan bağımsız olarak altı kilit tutma süresi bekler, en uzun
  bekleyen üç; ölçüm üç tutma süresinde de bandın içinde kaldı, doğruluk değişmedi.
- Kilidi bırakmadan düşen süreç bir saniyelik tavanı dolduran kırk iki bekleme turu ve sıfır
  yazılmış kalem üretti; kilit kaydında sahibin canlı olup olmadığını söyleyen alan yok.
- Durum yerel dosyada tutulunca iki kayıt oluştu, ikisinin de kapsaması yüzde elli kaldı ve bir
  sapma taraması on iki yanlış kayıt dışı üretti; uzak depo kapsamayı yüzde yüze çıkardı.

#### Sonraki Adım

Kilit doğruluğu kurtardı ama bir maliyet getirdi: kilidin kapsadığı alan tek bir dosyaysa, o
dosyaya yazan herkes sıraya girer. Ölçüm ağının tamamı tek bir durumda duruyorsa bir güvenlik
kuralını değiştirmek isteyen kişi, veri deposu ölçeklendiren kişinin uygulamasını bekler. Sonraki
ders bu tek dosyayı böler: tek durumla çalışan altyapıda plan süresini ve bir değişikliğin
kaç kalemi kilitlediğini ölçer, aynı ölçümleri bölünmüş hâlde tekrarlar ve bölmenin bedelini —
parçalar arası bağımlılıkları — sayar.

### Durum Bölme

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/durum-bolme

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ı.

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ı.

```js
// 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.

```js
// 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.

```sh
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.

```js
// 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ştirmekte**dir. 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.

### Çalışma Alanları ve Ortamlar

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/calisma-alanlari-ve-ortamlar

Üç ortamın iki ayrı kurulumu: tek tanım artı çalışma alanı ile ayrı dizin, ortama özgü fark sayısı, hangi ortamda olunduğunu söyleyen işaret sayısı ve yanlış ortama uygulama, tanım değiştiğinde birlikte değişen ortam sayısı ile ayrışma.

Önceki ders durumu parçalara böldü ve parça adlarında ortam da geçti. Ortam ayrımının nasıl
kurulduğuna ise hiç bakılmadı. Üç ortamın tanımı büyük ölçüde aynıysa iki yol vardır: tek tanım
yazıp ortamı bir ayarla seçmek ya da her ortam için ayrı dizin tutmak. Bu ders ikisini de gerçekten
kurar ve aralarındaki farkı üç sayıyla ölçer.

Ortamların birbirine ne kadar benzediği — **ortam denkliği** — daha önce ölçüldü ve burada
tekrarlanmaz. Buradaki soru benzerlik değil, benzerliğin **nerede yazıldığıdır**.

#### Aynı Tanımın Üç Örneği

**DM19.** Aşağıdaki yapılandırma dili bu ders için tanımlanan küçük bir biçimdir; `ortam(...)`
ifadesi seçili ortama göre değer seçer. Betik iki kurulumu da diske yazar: tek tanım artı çalışma
alanı, bir de üç ayrı dizin.

```js
// kurulum.mjs — uc ortam iki bicimde kurulur: tek tanim + calisma alani, bir de ayri dizin.
// Yapilandirma dili bu ders icin tanimlanan kucuk bicimdir; ortam(...) ifadesi ortama gore secer.
import { writeFileSync, mkdirSync, rmSync } from "node:fs";

const ortamlar = ["deneme", "hazirlik", "uretim"];
const TANIM = `kaynak alt_ag omurga {
  blok = ortam(deneme="10.0.0.0/16", hazirlik="10.1.0.0/16", uretim="10.2.0.0/16")
}
kaynak makine toplayici {
  sayi      = ortam(deneme=1, hazirlik=2, uretim=6)
  cekirdek  = ortam(deneme=2, hazirlik=2, uretim=8)
  bellek_gb = ortam(deneme=8, hazirlik=8, uretim=32)
  imaj      = "olcum-toplayici-4.2"
}
kaynak kuyruk okuma {
  saklama_saat = ortam(deneme=6, hazirlik=24, uretim=168)
  bolum        = ortam(deneme=1, hazirlik=4, uretim=12)
}
kaynak veri_deposu olcum {
  boyut_gb = ortam(deneme=20, hazirlik=100, uretim=2000)
  yedek    = ortam(deneme=false, hazirlik=false, uretim=true)
}`;

// ortam(...) ifadesini verilen ortam icin cozer; kosullu olmayan satir oldugu gibi kalir.
const coz = (metin, ortam) => metin.replace(/ortam\(([^)]*)\)/g, (_, ic) => {
  const esles = new RegExp(`${ortam}=([^,]+)`).exec(ic);
  return esles[1].trim();
});

rmSync("calisma-alani", { recursive: true, force: true });
rmSync("ayri-dizin", { recursive: true, force: true });
mkdirSync("calisma-alani/durum", { recursive: true });
writeFileSync("calisma-alani/tanim.txt", TANIM);
writeFileSync("calisma-alani/.secili", "deneme");
for (const o of ortamlar)
  writeFileSync(`calisma-alani/durum/${o}.json`, JSON.stringify({ ortam: o, kalemler: [] }));
for (const o of ortamlar) {
  mkdirSync(`ayri-dizin/${o}`, { recursive: true });
  writeFileSync(`ayri-dizin/${o}/tanim.txt`, coz(TANIM, o));
  writeFileSync(`ayri-dizin/${o}/durum.json`, JSON.stringify({ ortam: o, kalemler: [] }));
}

const satirlar = TANIM.split("\n");
const kosullu = satirlar.filter((s) => s.includes("ortam(")).length;
const alan = satirlar.filter((s) => s.includes("=")).length;
const ayriSatir = ortamlar.reduce((t, o) => t + coz(TANIM, o).split("\n").length, 0);
const ortakAlan = alan - kosullu;

console.log(`calisma alani: 1 tanim dosyasi, ${satirlar.length} satir, ` +
  `${alan} alan (${kosullu} ortama ozgu, ${ortakAlan} ortak), 3 durum dosyasi`);
console.log(`ayri dizin  : 3 tanim dosyasi, ${ayriSatir} satir, ` +
  `${alan * 3} alan (${kosullu * 3} ayri yazilmis, ${ortakAlan * 3} yinelenmis), 3 durum dosyasi`);
console.log(`ortama ozgu fark: ${kosullu} alan; uc ortamda ayni kalan: ${ortakAlan} alan`);
```

```
calisma alani: 1 tanim dosyasi, 17 satir, 9 alan (8 ortama ozgu, 1 ortak), 3 durum dosyasi
ayri dizin  : 3 tanim dosyasi, 51 satir, 27 alan (24 ayri yazilmis, 3 yinelenmis), 3 durum dosyasi
ortama ozgu fark: 8 alan; uc ortamda ayni kalan: 1 alan
```

İki kurulum da aynı altyapıyı anlatıyor, aynı üç durum dosyasını tutuyor ve aynı ortam ayrımını
üretiyor. Fark yazılan metnin miktarında ve **farkın nerede durduğundadır**.

Çalışma alanı biçiminde ortama özgü sekiz fark tek bir dosyada, sekiz koşullu ifadenin içinde
duruyor. Ayrı dizin biçiminde aynı sekiz fark yirmi dört ayrı satıra dağılmış — her ortam kendi
değerini kendi dosyasında taşıyor — ve üç ortamda aynı kalan tek alan üç kez yinelenmiş.

Bu ilk sayı çiftinin söylediği şudur: farkın tek yerde toplanması onu **görünür** kılar. Üç
ortamın hangi noktalarda ayrıştığını öğrenmek için çalışma alanı biçiminde bir dosyaya bakmak
yeterlidir; ayrı dizin biçiminde üç dosya karşılaştırılır. Buna karşılık ayrı dizin biçiminde
her ortamın tanımı kendi başına okunabilir: üretimin ne olduğunu anlamak için koşulları zihinde
çözmek gerekmez.

Durum tarafında ise iki kurulum arasında hiçbir fark yok: ikisi de ortam başına bir durum dosyası
tutuyor, toplam üç. Önceki iki dersin sayıları bu yüzden ikisinde de aynı biçimde geçerlidir —
kilit ortam başına alınır, üç ortam birbirini beklemeden uygulanabilir, bir ortamın durumunun
kaybı yalnız o ortamı yönetilemez kılar. Çalışma alanı **tanımı** paylaştırır; paylaştırmadığı
tek şey durumdur ve bu ayrım dersin sonunda yeniden karşımıza çıkacak.

#### Hangi Ortamdayım

İkinci ölçü işletmeye aittir. Uygulamayı başlatan kişi, düğmeye basmadan önce hangi ortamda
olduğunu kaç işaretten okuyabiliyor?

**DM20.** Çalışma alanı biçiminde seçili ortam gizli bir dosyada durur ve ayrı bir komutla
değiştirilir; komut satırında hiçbir izi yoktur. Ayrı dizin biçiminde seçim, komutun
çalıştırıldığı dizindir ve yol adında görünür. **DM21.** Seçimi atlama olasılığı işaret sayısına
bağlanır: işaret yoksa yüzde on beş, varsa yüzde üç. **DM22.** Uygulamadan önce ortam adını
ekrana basıp onay isteyen bir adım, atlanan seçimlerin onda dokuzunu yakalıyor. Üreteç ders
içinde yazılmıştır, tohum görünür.

```js
// ortam-secimi.mjs — hangi ortamda olundugunu bilme adimi ve yanlis ortama uygulama modeli.
import { readFileSync, writeFileSync } from "node:fs";

let x = 20260801 >>> 0;                          // tohum gorunur
const rast = () => { x = (Math.imul(x, 1664525) + 1013904223) >>> 0; return x / 4294967296; };
const ortamlar = ["deneme", "hazirlik", "uretim"];

// Uygulamayi baslatan kisi hangi ortamda oldugunu kac isaretten okuyabiliyor?
console.log("hangi ortamdayim: calisma alani -> ekranda 0 isaret, 1 ek komut (secili dosyasi)");
console.log("                  ayri dizin    -> yolda 1 isaret, 0 ek komut");

const kos = (etiket, unutmaOlasiligi, onayYakalama) => {
  writeFileSync("calisma-alani/.secili", "deneme");
  let yanlis = 0, uretimeDusen = 0;
  for (let i = 0; i < 200; i++) {
    const hedef = ortamlar[Math.floor(rast() * 3)];
    const gecerli = readFileSync("calisma-alani/.secili", "utf8");
    let secildi = hedef;
    if (rast() < unutmaOlasiligi) {              // secim adimi atlandi: onceki ortam gecerli
      secildi = gecerli;
      if (rast() < onayYakalama) secildi = hedef; // onay adimi yakaladi
    }
    writeFileSync("calisma-alani/.secili", secildi);
    if (secildi !== hedef) { yanlis++; if (secildi === "uretim") uretimeDusen++; }
  }
  console.log(`${etiket.padEnd(28)}: 200 uygulamada ${String(yanlis).padStart(2)} yanlis ortam, ` +
    `${uretimeDusen} tanesi uretime dustu`);
};

kos("calisma alani", 0.15, 0);
kos("ayri dizin", 0.03, 0);
kos("calisma alani + zorunlu onay", 0.15, 0.9);
```

```
hangi ortamdayim: calisma alani -> ekranda 0 isaret, 1 ek komut (secili dosyasi)
                  ayri dizin    -> yolda 1 isaret, 0 ek komut
calisma alani               : 200 uygulamada 23 yanlis ortam, 8 tanesi uretime dustu
ayri dizin                  : 200 uygulamada  4 yanlis ortam, 1 tanesi uretime dustu
calisma alani + zorunlu onay: 200 uygulamada  2 yanlis ortam, 1 tanesi uretime dustu
```

İki yüz uygulamada çalışma alanı biçimi yirmi üç kez yanlış ortama gitti, sekizi üretime düştü.
Ayrı dizin biçiminde sayı dörde, üretime düşen bire indi. Aradaki farkın kaynağı tek bir şeydir:
ekranda ortamı söyleyen bir işaretin olup olmaması.

Üçüncü satır daha önemlidir. Çalışma alanı biçimine uygulamadan önce ortam adını basan bir onay
adımı eklendiğinde yanlış uygulama yirmi üçten ikiye indi — ayrı dizin biçiminden de iyi.
Sonuç, iki kurulumdan birinin doğası gereği güvenli olmadığını söylüyor: güvenli olan, ortamın
**görünür** olmasıdır. Ayrı dizin bunu bedavaya verir çünkü yol zaten ekrandadır; çalışma alanı
biçiminde aynı görünürlük ayrıca kurulmak zorundadır.

Yanlış ortama uygulamanın bedeli bu kursun ilk dersindeki ölçüme bağlanır. Üretime düşen sekiz
uygulamanın her biri, üretim durumuna deneme değerlerini yazar: makine sayısı altıdan bire iner,
veri deposu iki bin gigabayttan yirmiye. Kayıt ile gerçek arasındaki fark burada sapmayla değil,
kaydın kendisiyle üretilmiştir — ve uygulama bittiği anda kayıt da gerçek de yanlıştır.

#### Tanım Değişince Kaç Ortam Değişir

Üçüncü ölçü zamanla ilgilidir. Tanıma bir alan eklendiğinde kaç ortam zorunlu olarak birlikte
değişir? **DM23.** On iki değişikliğin sekizi üç ortamı da ilgilendiriyor, dördü tek ortama özgü;
ayrı dizin biçiminde her elle taşıma dörtte bir olasılıkla atlanıyor. **DM24.** Uygulama yalnız
deneme ortamında koşturuldu.

```js
// degisiklik.mjs — tanim degistiginde kac ortamin zorunlu birlikte degistigi ve ayrisma.
import { readFileSync, writeFileSync, appendFileSync } from "node:fs";

let x = 991 >>> 0;                               // tohum gorunur
const rast = () => { x = (Math.imul(x, 1664525) + 1013904223) >>> 0; return x / 4294967296; };
const ortamlar = ["deneme", "hazirlik", "uretim"];

// On iki degisiklik: sekizi uc ortami da ilgilendiriyor, dordu tek ortama ozgu.
const degisiklikler = [];
for (let i = 1; i <= 8; i++) degisiklikler.push({ no: i, kapsam: "hepsi" });
for (let i = 9; i <= 12; i++) degisiklikler.push({ no: i, kapsam: ortamlar[(i - 9) % 3] });

let yeniKosul = 0, tasimaGereken = 0, tasimaAtlanan = 0;
for (const d of degisiklikler) {
  if (d.kapsam === "hepsi") {                    // tek tanim: bir satir, uc ortam birden
    appendFileSync("calisma-alani/tanim.txt", `\n  yeni_alan_${d.no} = "d${d.no}"`);
    for (const o of ortamlar) {                  // ayri dizin: her dizine elle tasinir
      tasimaGereken++;
      if (rast() < 0.25) { tasimaAtlanan++; continue; }
      appendFileSync(`ayri-dizin/${o}/tanim.txt`, `\n  yeni_alan_${d.no} = "d${d.no}"`);
    }
  } else {                                       // tek ortama ozgu: kosullu ifade gerekiyor
    yeniKosul++;
    const ic = ortamlar.map((o) => `${o}=${o === d.kapsam ? `"d${d.no}"` : '""'}`).join(", ");
    appendFileSync("calisma-alani/tanim.txt", `\n  yeni_alan_${d.no} = ortam(${ic})`);
    appendFileSync(`ayri-dizin/${d.kapsam}/tanim.txt`, `\n  yeni_alan_${d.no} = "d${d.no}"`);
  }
}

const kosullu = (s) => s.split("\n").filter((l) => l.includes("ortam(")).length;
const alan = (dosya) => new Set(readFileSync(dosya, "utf8").split("\n")
  .filter((l) => l.includes("yeni_alan_")).map((l) => /yeni_alan_\d+/.exec(l)[0]));
const beklenen = (o) => degisiklikler.filter((d) => d.kapsam === "hepsi" || d.kapsam === o).length;
const eksik = ortamlar.map((o) => beklenen(o) - alan(`ayri-dizin/${o}/tanim.txt`).size);

console.log(`calisma alani: 1 tanim dosyasi, ${degisiklikler.length} degisiklik islendi`);
console.log(`  kosullu ifade 8 -> ${kosullu(readFileSync("calisma-alani/tanim.txt", "utf8"))} ` +
  `(tek ortama ozgu her degisiklik bir kosul ekliyor)`);
console.log(`  ortak degisiklikte zorunlu birlikte degisen ortam: 3/3, ayrisan alan: 0`);
console.log(`ayri dizin: 3 tanim dosyasi, ${degisiklikler.length} degisiklik islendi`);
console.log(`  ortak degisiklik ${tasimaGereken} tasima gerektirdi, ${tasimaAtlanan} tasima atlandi`);
console.log(`  ortak degisiklikte zorunlu birlikte degisen ortam: 1/3, kalan 2 elle`);
console.log(`  ayrisan alan: ${eksik.reduce((a, b) => a + b, 0)} ` +
  `(${ortamlar.map((o, i) => `${o} ${eksik[i]}`).join(", ")})`);

// Tanim degisti, uygulama yalniz deneme ortaminda kosturuldu.
const geride = (bicim) => ortamlar.filter((o) => o !== "deneme")
  .map((o) => `${o} ${beklenen(o) - (bicim === "ayri" ? eksik[ortamlar.indexOf(o)] : 0)}`).join(", ");
console.log(`kayit ile gercek (yalniz deneme uygulandi), uygulanmayi bekleyen alan:`);
console.log(`  calisma alani -> ${geride("tek")} | ayri dizin -> ${geride("ayri")}`);
```

```
calisma alani: 1 tanim dosyasi, 12 degisiklik islendi
  kosullu ifade 8 -> 12 (tek ortama ozgu her degisiklik bir kosul ekliyor)
  ortak degisiklikte zorunlu birlikte degisen ortam: 3/3, ayrisan alan: 0
ayri dizin: 3 tanim dosyasi, 12 degisiklik islendi
  ortak degisiklik 24 tasima gerektirdi, 2 tasima atlandi
  ortak degisiklikte zorunlu birlikte degisen ortam: 1/3, kalan 2 elle
  ayrisan alan: 2 (deneme 0, hazirlik 2, uretim 0)
kayit ile gercek (yalniz deneme uygulandi), uygulanmayi bekleyen alan:
  calisma alani -> hazirlik 9, uretim 9 | ayri dizin -> hazirlik 7, uretim 9
```

Çalışma alanı biçiminde ortak bir değişiklik tek satırdır ve üç ortamın tanımını **zorunlu olarak
birlikte** değiştirir: 3/3. Ayrışan alan sıfırdır, çünkü ayrışacak ikinci bir dosya yoktur. Bedeli
öteki uçta ödenir: tek ortama özgü dört değişiklik dört yeni koşullu ifade doğurdu; koşul sayısı
sekizden on ikiye çıktı ve dosya her ortam farkında biraz daha okunmaz oldu.

Ayrı dizin biçiminde aynı sekiz ortak değişiklik yirmi dört elle taşıma gerektirdi ve ikisi
atlandı. Sonuç, hazırlık ortamında iki alanlık bir ayrışmadır. Bu ayrışmayı kimse bildirmez; iki
tanım da kendi içinde geçerlidir, iki uygulama da hatasız koşar. Fark yalnız üç dosya
karşılaştırıldığında görünür.

Atlama oranının kendisi de bir ölçüdür. Dörtte bir olasılık, on iki değişikliklik bir dönemde iki
alanlık ayrışma üretti; oran aynı kalırsa fark her dönemde biraz daha büyür ve hiçbir uygulama
bunu bildirmediği için birikir. Ayrışmanın bu koşuda hazırlık ortamında toplanması modelin
rastlantısıdır — hangi ortamın ayrışacağı önceden bilinmez, ayrışacağı bilinir. Bu da ayrı dizin
biçiminin taşımayı **denetleyen** bir adım gerektirdiği anlamına gelir: üç tanımı karşılaştıran
ve ortama özgü olmayan farkları bildiren bir denetim yoksa, ayrışma ancak üretimde bir davranış
farkı olarak ortaya çıkar.

Son iki sayı **kaydın kendi riskini** gösteriyor. Tanım her iki biçimde de değişti, uygulama
yalnız deneme ortamında koşturuldu. Çalışma alanı biçiminde hazırlık ve üretim dokuzar alan
geride; ayrı dizin biçiminde hazırlık yedi, üretim dokuz. Kayıt üç ortam için de değişmiş
durumda, gerçek yalnız biri için. Tek tanım, farkın **birikmesini** engellemez; yalnız tek yerde
yazılı olmasını sağlar. Ortamlar arası fark tanımda değil, **uygulama gecikmesinde** doğar.

#### Seçim

Üç ölçüm üç ayrı yöne işaret ediyor ve tek bir kazanan yok. Ortama özgü fark azsa çalışma alanı
biçimi kazanır: tek dosya, sıfır ayrışma, sekiz koşul. Ortama özgü fark çoğaldıkça koşullu ifade
sayısı tanımı yutar ve ayrı dizin okunabilir kalır. Yanlış ortama uygulama riski çalışma alanı
biçiminde beş kat yüksektir, ama bir onay adımıyla ikiye iner — yani bu fark kurulumun değil,
kurulumun etrafına konan korumanın işidir.

Üzerinde durulması gereken tek şey, iki biçimde de değişmeyendir: üç ortamın durumu üç ayrı
dosyadır. Çalışma alanı tek tanımı paylaştırır, durumu paylaştırmaz. Bu yüzden bir ortamda
uygulanan değişiklik ötekilerde kendiliğinden gerçekleşmez ve kaydın üç ortam için aynı olması,
gerçeğin aynı olduğu anlamına gelmez.

#### Özet

- Aynı altyapı iki biçimde kuruldu: tek tanım dosyası on yedi satır ve sekiz koşullu ifade;
  üç ayrı dizin elli bir satır, yirmi dört ayrı yazılmış ve üç yinelenmiş alan.
- Ortama özgü fark sekiz alan, üç ortamda aynı kalan bir alan; fark çalışma alanı biçiminde tek
  dosyada toplanıyor, ayrı dizin biçiminde üç dosyaya dağılıyor.
- İki yüz uygulamada yanlış ortama gitme sayısı çalışma alanı biçiminde yirmi üç, ayrı dizin
  biçiminde dört; ortam adını basan bir onay adımı sayıyı ikiye indirdi.
- Ortak bir değişiklik çalışma alanı biçiminde üç ortamı birlikte değiştiriyor ve sıfır ayrışma
  üretiyor; ayrı dizin biçiminde yirmi dört elle taşımanın ikisi atlandı ve iki alan ayrıştı.
- Tek ortama özgü dört değişiklik çalışma alanı biçiminde koşul sayısını sekizden on ikiye
  çıkardı; ayrı dizin biçiminde tek dosyada tek satır kaldı.
- Tanım üç ortam için değişti, uygulama bir ortamda koştu: çalışma alanı biçiminde iki ortam
  dokuzar, ayrı dizin biçiminde hazırlık yedi ve üretim dokuz alan geride kaldı.
  Ortamlar arası fark tanımda değil, uygulama gecikmesinde doğuyor.

#### Sonraki Adım

Bu dersin iki kurulumu da bir tekrarı çözmeye çalıştı: aynı altyapının üç kez anlatılması. Çalışma
alanı tekrarı koşullu ifadeye çevirdi, ayrı dizin ise açıkça yineledi. İkisi de tekrarı ortadan
kaldırmadı, yalnız yerini değiştirdi. Üç ortam yerine on ekip aynı kuyruğu, aynı toplayıcı
makineyi, aynı arşivi kurmak istediğinde iki biçim de yetmez; gereken şey tanımın kendisinin
paketlenmesi, girdilerinin adlandırılması ve sürümlenmesidir. Konunun bundan sonraki derslerinde
altyapı modülü ele alınır: girdi yüzeyi, sürüm sabitleme ve kırıcı değişikliğin bağlı olan her
kullanıcıya nasıl yayıldığı.

### Modül Yazımı

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/modul-yazimi

Üç 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.

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

```js
// 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

```js
// 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

```js
// 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; `bolge` bir 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-arsiv` kayı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.

### Modül Kayıtları

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/modul-kayitlari

Paylaşılan bir modül kaydından çekilen üç modül, dokuz tanım ve dokuz yayınla on haftalık bir dönem iki düzende koşturulur; sürümü sabitlenmiş ve sabitlenmemiş düzenler beklenmeyen değişiklik, kırılan tanım ve düzeltme satırı sayısıyla karşılaştırılır. Sabitlemenin kendi bedeli de sayılır.

Bir önceki ders modülü tek elin altında tuttu: yazan da kullanan da aynı taraftı; sürüm yükseltmesi
bir dosya düzenlemesiydi. **Modül kaydı** bu ilişkiyi keser. Kayıt, sürümlenmiş modül metinlerini
tanımların dışında tutan paylaşılan bir depodur; burada bir ürün ya da hizmet olarak değil, **tür
olarak** anılır. Modülü yazan taraf yayın yapar, kullanan taraf çeker ve aradaki tek bağ tanımda
yazılı bir addır.

O adın yanında bir sürüm yazıp yazmamak bu dersin ölçü ikilisidir. **Sabitlenmemiş** düzende tanım
yalnız modülün adını yazar ve uygulama anında kayıttaki en yeni sürüm çekilir. **Sabitlenmiş**
düzende tanım sürümü de yazar ve o sürüm birileri eliyle değiştirene kadar durur. Aynı yayın akışı
ve aynı uygulama takvimi iki düzende de koşturulur; ölçülen şey aradaki farktır.

Ölçüm yine **bölgesel ölçüm ağı** üzerindedir: su sayacı okumalarını toplayan **kurgu** bir yazılım.
Gerçek altyapı sağlanmaz, gerçek bir kayıt hizmetine bağlanılmaz; ölçülen şey `node` ile kurulmuş
bir **modeldir**.

**DM31.** Kayıtta üç modül ve on iki sürüm vardır; her sürüm zorunlu girdileri, varsayılanlı
girdileri ve gövdesiyle tek satırda yazılıdır.
**DM32.** Dönem on haftadır ve dokuz yayın önceden yazılıdır: dört yama, iki küçük, üç kırıcı.
Rastgelelik yoktur.
**DM33.** Dokuz tanım birer modül çağırır; uygulama haftaları önceden yazılıdır ve dönem boyunca
on sekiz uygulama koşar.
**DM34.** Sabitlenmemiş düzen uygulama anındaki en yeni sürümü, sabitlenmiş düzen tanımda yazılı
sürümü çeker.
**DM35.** Kırıcı yayın uygulamayı durdurur; eksik zorunlu girdi o anda tanıma yazılır ve satır
sayılır. Düzeltme kuralı `DUZELTME` içinde yazılıdır.
**DM36.** **Beklenmeyen değişiklik**, tanım metnine o uygulamada dokunulmadığı hâlde çıktının bir
öncekinden farklı olmasıdır. Sabitlenmiş düzende iki tanım dönem içinde elle yükseltilir.

#### Kayıt, Yayın Akışı ve Tanımlar

```js
// kayit.mjs — modul kaydi (tur olarak): uc modul, on iki surum, dokuz yayin ve dokuz tuketici tanim.
export const MODUL = {                                 // surum -> "zorunlu | varsayilanli | govde"
  "toplama-noktasi": {
    "1.0.0": "kaynak-blok | sayi=6 | kume sayi=$sayi; ag blok=$kaynak-blok",
    "1.1.0": "kaynak-blok | sayi=6,izleme=acik | kume sayi=$sayi; ag blok=$kaynak-blok; izleme durum=$izleme",
    "1.1.1": "kaynak-blok | sayi=6,izleme=acik | kume sayi=$sayi; ag blok=$kaynak-blok yon=giris; izleme durum=$izleme",
    "2.0.0": "ag-blogu | sayi=6,izleme=acik | kume sayi=$sayi; ag blok=$ag-blogu yon=giris; izleme durum=$izleme",
    "2.0.1": "ag-blogu | sayi=6,izleme=acik | kume sayi=$sayi tip=toplayici; ag blok=$ag-blogu yon=giris; izleme durum=$izleme" },
  "olcum-kuyrugu": {
    "1.0.0": "ad | bekleme=30 | kuyruk ad=$ad bekleme=$bekleme",
    "1.0.1": "ad | bekleme=30 | kuyruk ad=$ad bekleme=$bekleme; olu-mektup ad=olu-$ad",
    "1.1.0": "ad | bekleme=30,tavan=50000 | kuyruk ad=$ad bekleme=$bekleme tavan=$tavan; olu-mektup ad=olu-$ad",
    "2.0.0": "ad,bolge | bekleme=30,tavan=50000 | kuyruk ad=$ad bekleme=$bekleme tavan=$tavan bolge=$bolge; olu-mektup ad=olu-$ad" },
  "saha-agi": {
    "1.0.0": "kapi |  | ag-kurali kapi=$kapi",
    "2.0.0": "kapi,yon |  | ag-kurali kapi=$kapi yon=$yon",
    "2.0.1": "kapi,yon |  | ag-kurali kapi=$kapi yon=$yon kayit=acik" } };

export const YAYIN = ("1 olcum-kuyrugu 1.0.1 yama|2 toplama-noktasi 1.1.0 kucuk|3 saha-agi 2.0.0 kirici|" +
  "4 toplama-noktasi 1.1.1 yama|5 olcum-kuyrugu 1.1.0 kucuk|6 toplama-noktasi 2.0.0 kirici|" +
  "7 olcum-kuyrugu 2.0.0 kirici|8 toplama-noktasi 2.0.1 yama|9 saha-agi 2.0.1 yama")
  .split("|").map((s) => s.split(" ")).map(([h, m, s, t]) => [+h, m, s, t]);

export const TANIM = {                                 // "modul | gecilen | sabit surum | uygulama haftalari"
  "kuzey-nokta": "toplama-noktasi | kaynak-blok=10.20.0.0/16 sayi=4 | 1.0.0 | 2,6,9",
  "bati-nokta": "toplama-noktasi | kaynak-blok=10.30.0.0/16 | 1.0.0 | 3,7",
  "dogu-nokta": "toplama-noktasi | kaynak-blok=10.40.0.0/16 sayi=8 | 1.0.0 | 1,5,10",
  "guney-nokta": "toplama-noktasi | kaynak-blok=10.50.0.0/16 | 1.0.0 | 8",
  "kuzey-kuyruk": "olcum-kuyrugu | ad=kuzey-okuma | 1.0.0 | 2,8",
  "bati-kuyruk": "olcum-kuyrugu | ad=bati-okuma bekleme=60 | 1.0.0 | 4,9",
  "merkez-kuyruk": "olcum-kuyrugu | ad=merkez-okuma | 1.0.0 | 6,10",
  "saha-giris": "saha-agi | kapi=8080 | 1.0.0 | 4,10",
  "laboratuvar-giris": "saha-agi | kapi=9443 | 1.0.0 | 5" };
export const YUKSELT = [[7, "bati-nokta", "1.1.1"], [9, "kuzey-kuyruk", "1.1.0"]];   // sabit duzende elle
export const DUZELTME = {                              // kirici yayindan sonra tanima yazilan satir
  "ag-blogu": (a) => a["kaynak-blok"], yon: () => "giris", bolge: (a) => a.ad.split("-")[0] };
```

Üç modülün on iki sürümü yayın akışıyla birlikte okunur. `toplama-noktasi` altıncı haftada zorunlu
girdisinin adını `ag-blogu`'na çevirir, `saha-agi` üçüncü haftada `yon` diye yeni bir zorunlu girdi
ister, `olcum-kuyrugu` yedinci haftada `bolge` ister. Üçü de **kırıcı** yayındır ve üçünün de bedeli
kaydı kullanan tanımlarda çıkar.

#### İki Düzen, Aynı On Hafta

```js
// donem.mjs — ayni yayin akisi ve ayni uygulama takvimi iki duzende kosturulur; fark sayilir.
import { MODUL, YAYIN, TANIM, YUKSELT, DUZELTME } from "./kayit.mjs";

const kv = (s) => Object.fromEntries(s.trim() ? s.trim().split(/[ ,]+/).map((x) => x.split("=")) : []);
const T = Object.fromEntries(Object.entries(TANIM).map(([t, s]) => {
  const [m, g, p, h] = s.split(" | ");
  return [t, { m, g: kv(g), p, h: h.split(",").map(Number) }];
}));
const coz = (s) => { const [z, v, b] = s.split(" | ");
  return { zor: z.trim() ? z.trim().split(",") : [], vars: kv(v), govde: b.split("; ") }; };
const enYeni = (m, h) => (YAYIN.filter((y) => y[1] === m && y[0] <= h).at(-1) ?? [, , "1.0.0"])[2];
const uret = (mv, a) => mv.govde.map((l) => l.replace(/\$([\w-]+)/g, (_, x) => a[x])).join("; ");
const fark = (a, b) => {                               // iki cikti arasinda eklenen/degisen kelime
  const x = a.split(/[ ;]+/), y = b.split(/[ ;]+/);
  return y.filter((w) => !x.includes(w)).join(" ") || "(cikarildi)";
};

const kos = (sabit) => {
  const arg = {}, pin = {}, once = {}, kosan = {}, son = {}, oncekiS = {}, bek = [];
  let beklenmeyen = 0, kirikTanim = new Set(), kirikSatir = 0, degisen = 0;
  for (const t in T) { arg[t] = { ...T[t].g }; pin[t] = T[t].p; kosan[t] = new Set(); }
  for (let h = 1; h <= 10; h++) {
    if (sabit) for (const [hy, t, s] of YUKSELT) if (hy === h) { pin[t] = s; arg[t].$duzeltildi = 1; }
    for (const t in T) {
      if (!T[t].h.includes(h)) continue;
      const s = sabit ? pin[t] : enYeni(T[t].m, h);
      const mv = coz(MODUL[T[t].m][s]), tam = { ...mv.vars, ...arg[t] };
      const eksik = mv.zor.filter((a) => !(a in tam));
      let duzeltildi = arg[t].$duzeltildi;
      if (eksik.length) {                              // kirici yayin: tanim o anda duzeltilir
        for (const e of eksik) { arg[t][e] = DUZELTME[e](tam); tam[e] = arg[t][e]; }
        kirikTanim.add(t); kirikSatir += eksik.length; duzeltildi = 1;
      }
      const c = uret(mv, tam);
      kosan[t].add(s);
      if (once[t] !== undefined && once[t] !== c) {
        degisen++;
        if (!duzeltildi) { beklenmeyen++; bek.push([h, t, oncekiS[t], s, fark(once[t], c)]); }
      }
      once[t] = c; son[t] = s; oncekiS[t] = s; delete arg[t].$duzeltildi;
    }
  }
  return { beklenmeyen, kirikTanim, kirikSatir, degisen, pin, kosan, son, bek };
};

const A = kos(false), S = kos(true), TS = Object.keys(T).length;
const surumler = (m) => YAYIN.filter((y) => y[1] === m);
console.log(`donem 10 hafta | yayin ${YAYIN.length}: ` + ["yama", "kucuk", "kirici"]
  .map((k) => `${k} ${YAYIN.filter((y) => y[3] === k).length}`).join(", ") +
  ` | tanim ${TS} | uygulama ${Object.values(T).reduce((a, b) => a + b.h.length, 0)}`);
const yanlis = Object.keys(T).filter((t) => S.pin[t] !== S.son[t]).length;   // yazilan surum kosmuyor
for (const [ad, r, y] of [["sabitlenmemis", A, TS], ["sabitlenmis  ", S, yanlis]])
  console.log(`${ad} | cikti degisimi ${r.degisen} | beklenmeyen degisiklik ${r.beklenmeyen}` +
    ` | kirilan tanim ${r.kirikTanim.size} | duzeltme satiri ${r.kirikSatir} | kosan farkli surum ` +
    `${Object.values(r.kosan).reduce((a, b) => a + b.size, 0)} | kaydin kosani soylemedigi tanim ${y}/${TS}`);

for (const [h, t, e, s, d] of A.bek)                   // tanima dokunulmadan degisen ciktilar
  console.log(`  beklenmeyen  h${String(h).padEnd(2)} ${t.padEnd(12)} ${e} -> ${s}  eklenen: ${d}`);
for (const [h, m, s, tur] of YAYIN) {                  // bir yayin kac kullaniciya ulasti
  const u = Object.keys(T).filter((t) => T[t].m === m && A.kosan[t].has(s));
  const b = Object.keys(T).filter((t) => T[t].m === m && S.kosan[t].has(s));
  console.log(`  h${String(h).padEnd(2)} ${m.padEnd(16)} ${s} ${tur.padEnd(7)} kullanan tanim ${Object.keys(T)
    .filter((t) => T[t].m === m).length} | ulastigi: sabitlenmemis ${u.length}, sabitlenmis ${b.length}`);
}

let geri = 0, kacan = 0, eski = 0;                     // sabitlemenin bedeli: donem sonunda
for (const t in T) {
  const l = surumler(T[t].m), sonrasi = l.slice(l.findIndex((y) => y[2] === S.pin[t]) + 1);
  geri += sonrasi.length; kacan += sonrasi.filter((y) => y[3] === "yama").length;
  if (sonrasi.length) eski++;
  console.log(`  ${t.padEnd(18)} kayitli ${S.pin[t]}  kosan ${S.son[t]}  en yeni ${l.at(-1)[2]}` +
    `  geride ${sonrasi.length}  alinmayan yama ${sonrasi.filter((y) => y[3] === "yama").length}`);
}
const kullanilan = new Set(Object.keys(T).map((t) => `${T[t].m} ${S.pin[t]}`));
const disSatir = [...kullanilan].reduce((a, k) => {
  const [m, s] = k.split(" "), v = coz(MODUL[m][s]);
  return a + v.zor.length + Object.keys(v.vars).length + v.govde.length;
}, 0);
console.log(`eski surumde kalan tanim ${eski}/${TS} | toplam geride ${geri} surum | alinmayan yama ${kacan}` +
  ` | depodaki tanim ${Object.values(T).reduce((a, d) => a + 3 + Object.keys(d.g).length, 0)} satir` +
  ` | bagli olunan ${kullanilan.size} modul surumu, ${disSatir} satir depo disinda`);
```

```
donem 10 hafta | yayin 9: yama 4, kucuk 2, kirici 3 | tanim 9 | uygulama 18
sabitlenmemis | cikti degisimi 9 | beklenmeyen degisiklik 3 | kirilan tanim 9 | duzeltme satiri 9 | kosan farkli surum 18 | kaydin kosani soylemedigi tanim 9/9
sabitlenmis   | cikti degisimi 1 | beklenmeyen degisiklik 0 | kirilan tanim 0 | duzeltme satiri 0 | kosan farkli surum 10 | kaydin kosani soylemedigi tanim 1/9
  beklenmeyen  h5  dogu-nokta   1.0.0 -> 1.1.1  eklenen: yon=giris izleme durum=acik
  beklenmeyen  h9  kuzey-nokta  2.0.0 -> 2.0.1  eklenen: tip=toplayici
  beklenmeyen  h10 saha-giris   2.0.0 -> 2.0.1  eklenen: kayit=acik
  h1  olcum-kuyrugu    1.0.1 yama    kullanan tanim 3 | ulastigi: sabitlenmemis 2, sabitlenmis 0
  h2  toplama-noktasi  1.1.0 kucuk   kullanan tanim 4 | ulastigi: sabitlenmemis 2, sabitlenmis 0
  h3  saha-agi         2.0.0 kirici  kullanan tanim 2 | ulastigi: sabitlenmemis 2, sabitlenmis 0
  h4  toplama-noktasi  1.1.1 yama    kullanan tanim 4 | ulastigi: sabitlenmemis 1, sabitlenmis 1
  h5  olcum-kuyrugu    1.1.0 kucuk   kullanan tanim 3 | ulastigi: sabitlenmemis 1, sabitlenmis 0
  h6  toplama-noktasi  2.0.0 kirici  kullanan tanim 4 | ulastigi: sabitlenmemis 2, sabitlenmis 0
  h7  olcum-kuyrugu    2.0.0 kirici  kullanan tanim 3 | ulastigi: sabitlenmemis 3, sabitlenmis 0
  h8  toplama-noktasi  2.0.1 yama    kullanan tanim 4 | ulastigi: sabitlenmemis 3, sabitlenmis 0
  h9  saha-agi         2.0.1 yama    kullanan tanim 2 | ulastigi: sabitlenmemis 1, sabitlenmis 0
  kuzey-nokta        kayitli 1.0.0  kosan 1.0.0  en yeni 2.0.1  geride 4  alinmayan yama 2
  bati-nokta         kayitli 1.1.1  kosan 1.1.1  en yeni 2.0.1  geride 2  alinmayan yama 1
  dogu-nokta         kayitli 1.0.0  kosan 1.0.0  en yeni 2.0.1  geride 4  alinmayan yama 2
  guney-nokta        kayitli 1.0.0  kosan 1.0.0  en yeni 2.0.1  geride 4  alinmayan yama 2
  kuzey-kuyruk       kayitli 1.1.0  kosan 1.0.0  en yeni 2.0.0  geride 1  alinmayan yama 0
  bati-kuyruk        kayitli 1.0.0  kosan 1.0.0  en yeni 2.0.0  geride 3  alinmayan yama 1
  merkez-kuyruk      kayitli 1.0.0  kosan 1.0.0  en yeni 2.0.0  geride 3  alinmayan yama 1
  saha-giris         kayitli 1.0.0  kosan 1.0.0  en yeni 2.0.1  geride 2  alinmayan yama 1
  laboratuvar-giris  kayitli 1.0.0  kosan 1.0.0  en yeni 2.0.1  geride 2  alinmayan yama 1
eski surumde kalan tanim 9/9 | toplam geride 25 surum | alinmayan yama 11 | depodaki tanim 39 satir | bagli olunan 5 modul surumu, 20 satir depo disinda
```

On sekiz uygulamanın on sekizi iki düzende de koştu; fark neyin koştuğundadır. Sabitlenmemiş düzende
çıktı dokuz kez değişti, sabitlenmiş düzende bir kez. O bir kez de elle yapılmış bir yükseltmedir;
kimse istemeden hiçbir çıktı değişmedi.

Dokuz değişimin **üçü beklenmeyen değişikliktir**: tanım metnine o uygulamada hiç dokunulmadı, gene
de üretilen kaynak bir öncekinden farklı çıktı. Kalan altısı kırıcı yayınların hemen ardından geldi
ve tanım o sırada zaten düzeltilmişti. Beklenmeyen üçün dökümü ortak yanlarını gösterir:
`dogu-nokta` beşinci haftada iki sürüm birden atlayıp bir ağ alanı ile bir izleme satırı kazandı,
`kuzey-nokta` dokuzuncu haftada makine kümesine `tip=toplayici` aldı, `saha-giris` onuncu haftada
ağ kuralına `kayit=acik` aldı. Üçü de kayıtta görünmeyen bir yayının sonucudur, üçü de sessizce
uygulandı ve **hiçbiri hata vermedi**.

Kırıcı yayınların yayılması ikinci sayıdır. Üç kırıcı yayın sabitlenmemiş düzende **dokuz tanımın
dokuzunu** kırdı ve dokuz satır düzeltme istedi. Yayın sayısı üç, düzeltme sayısı dokuz: bir kırıcı
yayının maliyeti, onu yayınlayan tarafta bir kez, kullanan tarafta kullanıcı sayısı kadar ödenir.
Sabitlenmiş düzende aynı üç yayın hiçbir tanımı kırmadı ve sıfır satır düzeltme istedi — ama bu
maliyetin silindiği anlamına gelmez, ertelendiği anlamına gelir.

Yayın başına döküm bir yayının kaç kullanıcıya ulaştığını verir. Dokuz yayının hiçbiri o modülü
kullanan tanımların tamamına ulaşmadı: en geniş yayılım üçte üç, en darı dörtte bir. Bir yayının
kime ulaştığına yayını yapan taraf değil, **kullanan tarafın uygulama takvimi** karar veriyor.
Sabitlenmiş düzende aynı dokuz yayının sekizi hiçbir kullanıcıya ulaşmadı; ulaşan tek yayın, elle
yükseltilen tanımın çektiği yamadır. Sabitlenmemiş düzende dokuz tanım dönem boyunca on sekiz farklı
sürüm koşturdu, sabitlenmişte on: tanım başına ikiye karşı 1,1.

#### Sabitlemenin Bedeli

Dönem sonunda tablo tersine döner. Sabitlenmiş düzende **dokuz tanımın dokuzu eski sürümdedir**;
toplamda yirmi beş sürüm geride kalmışlardır ve **on bir yama** onlara hiç ulaşmamıştır. Yama bir
hata düzeltmesidir; sabitleme yalnız istenmeyen değişikliği durdurmaz, istenen düzeltmeyi de durdurur.

Dağılım ertelemenin nasıl biriktiğini gösterir. Hiç yükseltilmeyen yedi tanımın beşi üçle dört
sürüm arası, ikisi iki sürüm geride; dönem içinde bir kez yükseltilen `bati-nokta` iki sürüm
geride ve bir yama eksik. Yükseltme bir tanımı öne çeker ama yayın akışı durmadığı için mesafe
hemen yeniden açılır. Sabitlemenin bedeli tek seferlik bir gecikme değil, **sürekli büyüyen bir
açıklıktır**.

Ertelemenin faizi de sayılıdır: bugün ödenmeyen dokuz satır düzeltme silinmedi, o dokuz tanım bir
gün yükseltilecekse aynı üç kırıcı yayınla aynı anda karşılaşacaklar.

#### Kaydın Söylemediği

Üçüncü sayı kaydın kendisiyle ilgilidir. Sabitlenmemiş düzende tanım hiçbir sürüm yazmaz;
**dokuz tanımın dokuzunda kayıt, koşan şeyi tarif etmiyor.** Tanım dosyası okunarak üretilecek
kaynağın ne olduğu bilinemez, çünkü metnin bir kısmı başka bir yerde ve o yer değişebilir.

Sabitlemek bunu bire indirir ama sıfıra indirmez. `kuzey-kuyruk` satırı farkı gösterir: kayıtlı
sürüm 1.1.0, koşan sürüm 1.0.0. Dokuzuncu haftada sürüm yükseltildi, ama o tanımın dönem içinde
başka uygulaması olmadı. **Kayıt yeni sürümü söylüyor, sahada eski sürüm duruyor.** Sabitleme,
kayıt ile gerçek arasındaki farkı ortadan kaldırmaz; farkı yayın anından **uygulama anına** taşır.

Son satır bağımlılığın büyüklüğünü verir. Depodaki dokuz tanım otuz dokuz satırdır ve beş ayrı modül
sürümüne, toplam **yirmi satır depo dışı metne** bağlıdır. Sabitlenmiş düzende o yirmi satırın hangi
hâli olduğu tanımda yazılıdır; sabitlenmemişte yazılı değildir ve depoda tek bir kayıt bile
değişmeden başkalaşabilir.

#### Özet

- Aynı 18 uygulama iki düzende koştu: sabitlenmemiş düzende çıktı 9 kez, sabitlenmiş düzende 1 kez
  değişti; o 1 değişim de elle yapılan bir yükseltmedir.
- 9 değişimin 3'ü **beklenmeyen değişikliktir** — tanım metnine dokunulmadı, çıktı değişti ve
  hiçbiri hata vermedi.
- 3 kırıcı yayın sabitlenmemiş düzende 9 tanımı kırdı ve 9 satır düzeltme istedi; sabitlenmiş
  düzende 0 tanım kırıldı, ama maliyet silinmedi, ertelendi.
- Sabitlemenin bedeli dönem sonunda görülür: 9 tanımın 9'u eski sürümde, toplam 25 sürüm geride ve
  11 yama hiç ulaşmadı.
- Sabitlenmemiş düzende 9 tanımın 9'unda kayıt koşanı söylemiyor; sabitlenmişte 1'inde söylemiyor —
  yazılan sürüm uygulanmadığı için kayıt ile gerçek yine ayrıştı.

#### Sonraki Adım

Modül kaydından gelen metin artık tanımın bir parçası gibi davranıyor: girdileri alıyor, kaynak
üretiyor, çıktı veriyor. Bu derste o girdilerin hepsi zararsızdı — bir ağ bloğu, bir kapı numarası,
bir kuyruk adı. Aralarında bir veri deposu parolası olsaydı ne olurdu? Değer tanımdan modüle,
modülden kaynağa, kaynaktan durum dosyasına geçer; yol boyunca plan çıktısına, günlüğe ve hat
kaydına da uğrar. Bir sonraki ders kurgu bir hassas değeri bu yola sokar ve `node` ile yazılmış bir
iz tarayıcısıyla kaç yerde göründüğünü sayar; sonra o değere "hassas" damgası vurulur ve tarama
yeniden koşturulur — kaç yerde gizlendiği, kaç yerde hâlâ durduğu sayılır.

### Hassas Değerler

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/hassas-degerler

Kurgu bir parola tanımdan geçirilir ve yedi iz noktasında kaç kopya bıraktığı bir iz tarayıcısıyla sayılır; sonra alanlara hassas işareti konur ve tarama yeniden koşturulur. İşaretlemenin kapattığı ve kapatamadığı yerler, döndürme sonrası kalıntı ve maruziyet ayrı ayrı ölçülür.

Bir önceki ders modül kaydından çekilen metnin girdilerini saydı ve hepsi zararsızdı: bir ağ bloğu,
bir kapı numarası, bir kuyruk adı. Bu derste girdilerden biri bir veri deposu parolasıdır. Değer
değişmedi, yol değişmedi, ölçülen şey değişti — artık sorulan soru "kaç satır" değil, **"kaç yerde
görünüyor"**.

**Sır**, saklanması gereken değerin kendisidir. **Hassas değer** ise o değerin kayıt boyunca taşıdığı
işarettir; bir alanın "bunu gösterme" diye damgalanmasıdır. Aradaki fark bu dersin ölçtüğü şeydir:
işaret değere değil, **alana** konur ve değer bir alandan başka bir alana kopyalandığı anda işaret
onunla birlikte gitmez.

Sır sızıntısı bu modülün önceki kurslarında iki kez ölçüldü: biri depo genelinde bir sızıntı taraması
koşturdu ve şifreli sır kaydını ele aldı, öteki hat kayıtlarında görünen değerleri saydı. Burada o
taramalar tekrarlanmaz. Ölçülen şey daha dar: **tek bir değerin, tek bir uygulamada bıraktığı iz.**

Ölçüm yine **bölgesel ölçüm ağı** üzerindedir ve ağ **kurgudur**. Değerin kendisi de kurgudur; gerçek
bir sır, anahtar ya da kimlik bilgisi yazılmaz. Gerçek bir sağlayıcıya bağlanılmaz; iz noktaları
`node` ile üretilmiş bir **modeldir**.

**DM37.** Hassas değer kurgu bir dizedir: `KURGU-parola-7f31`.
**DM38.** Değer depodaki bir değişken dosyasından girer, modüle girdi olur ve iki kaynak alanına
yazılır: doğrudan `parola` alanına, bir de değerin içine gömüldüğü bir bağlantı dizesine.
**DM39.** Yedi iz noktası vardır: değişken dosyası, plan çıktısı, uygulama günlüğü, durum dosyası,
modül çıktısı, sağlayıcı hata mesajı ve hat kaydı. Hat kaydı ötekilerin kopyasını taşır.
**DM40.** İşaretleme alana yapılır; işaretli alan plan, günlük ve modül çıktısında maskelenir.
**DM41.** Durum dosyası şifresizdir ve işaretlemeden etkilenmez; sağlayıcı hata mesajı maskeleme
kuralını bilmez.
**DM42.** Üç tur koşulur: işaretsiz, yalnız `parola` işaretli, `parola` ve `baglanti` işaretli.
Sonra değer döndürülür ve eski değerin kaç yerde kaldığı sayılır.
**DM43.** Maruziyet **model bir sayıdır**: kopya × okuyabilen kişi × saklama günü. Yer, gün ve kişi
sayıları ders içinde yazılıdır.

#### Değerin Yolu ve İz Tarayıcısı

```js
// iz.mjs — kurgu bir hassas deger tanimdan gecirilir, iz noktalari uretilir, bir iz tarayicisi kosar.
const DEGER = "KURGU-parola-7f31";                     // kurgu dize; gercek bir sir degildir
const YENI = "KURGU-parola-b204";                      // dondurmeden sonraki kurgu deger

const uret = (d, im) => {                              // im: isaretli alanlar; gosterimde maskelenir
  const alan = { parola: d, baglanti: `olcum:${d}@depo.olcum-agi:5432` };
  const g = (a) => (im.has(a) ? "(hassas deger)" : alan[a]);
  const plan = `+ veri-deposu olcum-deposu\n    parola = ${g("parola")}\n` +
    `+ baglanti olcum-baglanti\n    dize = ${g("baglanti")}`;
  const gunluk = `uygulandi veri-deposu olcum-deposu parola=${g("parola")}\n` +
    `uygulandi baglanti olcum-baglanti dize=${g("baglanti")}`;
  const durum = JSON.stringify({ "veri-deposu": { parola: alan.parola },   // durum sifresizdir
    baglanti: { dize: alan.baglanti } });                                  // isaretleme onu etkilemez
  const cikti = `cikti baglanti-dizesi = ${g("baglanti")}`;
  const hata = `saglayici hatasi 400: gecersiz istek, govde {"parola":"${alan.parola}"}`;
  return { degerler: `parola = ${alan.parola}`, plan, gunluk, durum, cikti, hata,
    hat: [plan, gunluk, cikti].join("\n") };           // hat kaydi otekilerin kopyasini tasir
};
const tara = (iz, d) => Object.entries(iz).map(([a, m]) => [a, m.split(d).length - 1]);

const AD = ["degerler", "plan", "gunluk", "durum", "cikti", "hata", "hat"];
const TUR = [["isaretsiz", new Set()], ["parola isaretli", new Set(["parola"])],
  ["parola+baglanti", new Set(["parola", "baglanti"])]];
console.log("tur                " + AD.map((a) => a.padStart(9)).join("") + "   nokta  kopya");
const sonuc = {};
for (const [ad, im] of TUR) {
  const s = tara(uret(DEGER, im), DEGER);
  sonuc[ad] = s;
  console.log(ad.padEnd(19) + s.map(([, n]) => String(n).padStart(9)).join("") +
    `   ${s.filter(([, n]) => n).length}/${AD.length}` +
    `${String(s.reduce((a, [, n]) => a + n, 0)).padStart(7)}`);
}
const ilk = sonuc["isaretsiz"], son = sonuc["parola+baglanti"];
const kapanan = ilk.filter(([a, n], i) => n && !son[i][1]).map(([a]) => a);
const acik = son.filter(([, n]) => n).map(([a]) => a);
console.log(`iki isaretlemeden sonra kapanan ${kapanan.length}/${AD.length} (${kapanan})` +
  ` | acik kalan ${acik.length}/${AD.length} (${acik})`);

const KALINTI = { "durum surumu -1": uret(DEGER, new Set(["parola", "baglanti"])).durum,
  "arsiv hat kaydi": uret(DEGER, new Set()).hat, "depo gecmisi": uret(DEGER, new Set()).degerler };
const yeniKosum = tara(uret(YENI, new Set(["parola", "baglanti"])), DEGER);
const k = tara(KALINTI, DEGER);
console.log(`deger dondurulduktan sonra yeni kosumda eski deger ${yeniKosum.reduce((a, [, n]) => a + n, 0)} kopya` +
  ` | kalintida ${k.filter(([, n]) => n).length} yer, ${k.reduce((a, [, n]) => a + n, 0)} kopya: ` +
  k.map(([a, n]) => `${a} ${n}`).join(", "));
console.log(`durum dosyasi her turda ${ilk[3][1]} kopya tasidi; isaretleme onu hicbir turda azaltmadi`);

const YER = {                                          // iz noktasi: [nerede durur, saklama gunu, okuyabilen]
  degerler: ["depo", 3650, 24], plan: ["hat kaydi", 90, 31], gunluk: ["hat kaydi", 90, 31],
  durum: ["uzak durum deposu", 3650, 6], cikti: ["baska tanimlar", 3650, 12],
  hata: ["destek kaydi", 365, 40], hat: ["hat arsivi", 90, 31] };
const mar = (s) => s.reduce((a, [ad, n]) => a + n * YER[ad][1] * YER[ad][2], 0);
console.log("nokta     nerede              gun  okuyan  kopya(ilk/son)  maruziyet(ilk/son)");
for (const [i, [ad, n]] of ilk.entries()) {
  const [y, g, o] = YER[ad];
  console.log(`${ad.padEnd(10)}${y.padEnd(20)}${String(g).padStart(4)}${String(o).padStart(8)}` +
    `${`${n}/${son[i][1]}`.padStart(16)}${`${n * g * o}/${son[i][1] * g * o}`.padStart(20)}`);
}
const kp = (s) => s.reduce((a, [, n]) => a + n, 0);
console.log(`kopya ${kp(ilk)} -> ${kp(son)} (%${Math.round((1 - kp(son) / kp(ilk)) * 100)} dustu)` +
  ` | maruziyet ${mar(ilk)} -> ${mar(son)} (%${Math.round((1 - mar(son) / mar(ilk)) * 100)} dustu)`);

const TUKETEN = ["saha-hizmeti", "raporlama", "dogrulayici"];    // modul ciktisini giren tanimlar
const asagi = Object.fromEntries(TUKETEN.map((t) =>              // her biri kendi durumuna yazar
  [`durum:${t}`, JSON.stringify({ [t]: { baglanti: `olcum:${DEGER}@depo.olcum-agi:5432` } })]));
const a = tara(asagi, DEGER), aMar = kp(a) * YER.durum[1] * YER.durum[2];
console.log(`ciktiyi tuketen tanim ${TUKETEN.length}, her biri ayri durum dosyasi | ` +
  a.map(([ad, n]) => `${ad} ${n}`).join(" ") + ` | bolunmus durumla acik nokta ` +
  `${acik.length}+${a.filter(([, n]) => n).length}=${acik.length + a.length}` +
  ` | kopya ${kp(son)}+${kp(a)}=${kp(son) + kp(a)}` +
  ` | maruziyet ${mar(son)}+${aMar}=${mar(son) + aMar} (isaretsiz tura gore %` +
  `${Math.round((1 - (mar(son) + aMar) / mar(ilk)) * 100)} dustu)`);
```

```
tur                 degerler     plan   gunluk    durum    cikti     hata      hat   nokta  kopya
isaretsiz                  1        2        2        2        1        1        5   7/7     14
parola isaretli            1        1        1        2        1        1        3   7/7     10
parola+baglanti            1        0        0        2        0        1        0   3/7      4
iki isaretlemeden sonra kapanan 4/7 (plan,gunluk,cikti,hat) | acik kalan 3/7 (degerler,durum,hata)
deger dondurulduktan sonra yeni kosumda eski deger 0 kopya | kalintida 3 yer, 8 kopya: durum surumu -1 2, arsiv hat kaydi 5, depo gecmisi 1
durum dosyasi her turda 2 kopya tasidi; isaretleme onu hicbir turda azaltmadi
nokta     nerede              gun  okuyan  kopya(ilk/son)  maruziyet(ilk/son)
degerler  depo                3650      24             1/1         87600/87600
plan      hat kaydi             90      31             2/0              5580/0
gunluk    hat kaydi             90      31             2/0              5580/0
durum     uzak durum deposu   3650       6             2/2         43800/43800
cikti     baska tanimlar      3650      12             1/0             43800/0
hata      destek kaydi         365      40             1/1         14600/14600
hat       hat arsivi            90      31             5/0             13950/0
kopya 14 -> 4 (%71 dustu) | maruziyet 214910 -> 146000 (%32 dustu)
ciktiyi tuketen tanim 3, her biri ayri durum dosyasi | durum:saha-hizmeti 1 durum:raporlama 1 durum:dogrulayici 1 | bolunmus durumla acik nokta 3+3=6 | kopya 4+3=7 | maruziyet 146000+65700=211700 (isaretsiz tura gore %1 dustu)
```

#### Üç Tur

İşaretsiz turda değer **yedi iz noktasının yedisinde** görünüyor ve toplam on dört kopya
bırakıyor. Tek bir uygulamanın tek bir girdisi için on dört kopya. Sayının büyük kısmı çoğaltmadan
geliyor: hat kaydı planı, günlüğü ve çıktıyı olduğu gibi topladığı için tek başına beş kopya
taşıyor. Bu beş kopyanın hiçbiri yeni bir karardan doğmadı; kimse "parolayı hat kaydına yaz"
demedi. Kayıt okundu; çıktısı bir yere yazıldı; o yer de saklandı. **Kopyaların çoğu bir karar
değil, bir yan üründür** ve bu yüzden hiçbiri bir gözden geçirmede görünmez.

İkinci tur işaretlemenin ne kadar dar bir şey olduğunu gösterir. `parola` alanı işaretlendi; plan ve
günlük o alanı maskeledi, kopya sayısı on dörtten ona indi — ama **görünen nokta sayısı yine yedi**.
Nedeni ilk bölümde yazılıydı: değer bir alandan başka bir alana kopyalanmıştı. `baglanti` dizesi
parolayı içine gömdüğü için ayrı bir alandır ve işaretlenmemiştir. Maskeleme alanı tanır, değeri
tanımaz. İşaretlemenin gözden kaçırdığı şey bir kural değil, bir **kopya**dır.

Üçüncü tur ikinci alanı da işaretler ve o zaman gerçek düşüş gelir: kopya dörde, görünen nokta üçe
iner. Kapanan dört nokta plan, günlük, modül çıktısı ve hat kaydıdır. Açık kalan üç nokta ise
değişken dosyası, durum dosyası ve sağlayıcı hata mesajıdır — ve üçünün açık kalması üç ayrı
nedenledir.

Değişken dosyası açıktır çünkü işaretleme bir **gösterim** kuralıdır; değerin nerede yazılı olduğuna
karışmaz. Damga, kaydın okunduğu yerde ne yazacağını söyler, kaydın kendisinde ne yazdığını değil.
Sağlayıcı hata mesajı açıktır çünkü maskeleme kuralı kaydı yazan tarafın kuralıdır ve hatayı üreten
taraf onu bilmez; hata, gönderilen isteğin gövdesini olduğu gibi geri yazar. Bu ikisinin ortak yanı,
ikisinin de **kaydın denetimi dışında** olmasıdır: biri kayıttan önce gelir, öteki kayıttan sonra.
Durum dosyası ise ikisine de benzemez; kaydın tam ortasındadır ve bu yüzden en önemlisidir.

#### Durum, Kalıntı ve Maruziyet

Durum dosyası üç turun üçünde de iki kopya taşıdı ve işaretleme onu hiçbir turda azaltmadı. Bunun
nedeni durumun ne olduğudur: durum, uygulanmış kaynakların gerçek değerlerinin kaydıdır. Bir alanı
"gösterme" diye damgalamak o alanın **değerini** ortadan kaldırmaz; sonraki planın kayıt ile gerçeği
karşılaştırabilmesi için değerin bir yerde durması gerekir. Bu kursun kuralı burada da geçerlidir:
altyapı ancak kaydı kadar yönetilebilir ve kayıt değeri tutmak zorundadır.

Döndürme bunu bir kez daha gösterir. Değer yenisiyle değiştirildikten sonra yeni koşumda eski değer
**sıfır kopya** bırakıyor — yani ileriye doğru temiz. Geriye doğru değil: kalıntıda üç yer ve sekiz
kopya duruyor. Eski durum sürümü iki, arşivlenmiş hat kaydı beş, deponun geçmişi bir. Sekiz kopyanın
beşi, işaretleme hiç yokken alınmış tek bir arşivden geliyor; bugün konan damga dünkü kaydı
düzeltmez. Döndürme değeri geçersiz kılar ama **kayıttan silmez**; silinmesi gereken şey artık
değer değil, kaydın kendisidir.

Son tablo işaretlemenin değerini bir de ağırlıklı olarak ölçer. Maruziyet kopya sayısını bulunduğu
yerin saklama süresi ve o yeri okuyabilen kişi sayısıyla çarpar; **model bir sayıdır**, gerçek bir
ölçüm değil. Sonuç şudur: iki işaretleme **kopyaların yüzde yetmiş birini** sildi ama **maruziyetin
yalnız yüzde otuz ikisini**. Aradaki fark, kapanan noktaların hepsinin kısa ömürlü olmasından gelir
— plan, günlük ve hat kaydı doksan gün saklanır, açık kalan üç noktanın ikisi kalıcıdır.

Kalan maruziyetin dağılımı da beklenenden farklıdır. En büyük pay durum dosyasında değil, depodaki
değişken dosyasındadır: yüz kırk altı binin seksen yedi bin altı yüzü, yani yüzde altmışı. Değer
depoda yazılı olduğu sürece onu okuyabilen yirmi dört kişi, uzak durum deposunu okuyabilen altı
kişiden fazladır. İşaretleme bu payı hiç azaltmaz; azaltacak olan şey başka bir karardır — değeri
kayıttan çıkarıp uygulama anında dışarıdan almak.

#### Bölünmüş Durumda Çoğalma

Buraya kadarki sayım tek bir tanımın tek bir durumunu ölçtü. Bu konuda durum daha önce bölünmüştü:
her ekip kendi tanımını, kendi durumunu ve kendi uygulama takvimini aldı. Bölünmüş durum hassas
değerde başka türlü davranır.

Modül `baglanti-dizesi` diye bir çıktı veriyor ve bu çıktıyı üç aşağı tanım girdi olarak alıyor.
Çıktı gösterimde maskelendi — üçüncü turda `cikti` noktası sıfır kopya gösteriyordu. Ama maskeleme
gösterimdir; tüketen tarafa giden şey değerin kendisidir. Üç tanımın üçü değeri kendi kaynağına
yazar, kaynak da kendi durum dosyasına düşer. Son satır bunu sayar: **üç yeni durum dosyası, üç yeni
kopya.**

Toplam böylece tersine döner. İşaretlemeden sonra üç noktada dört kopya kalmıştı; bölünmüş durum
sayılınca altı noktada yedi kopya oluyor. Maruziyet daha da serttir: yüz kırk altı bin, altmış beş
bin yedi yüz artarak iki yüz on bir bin yedi yüze çıkıyor. İşaretsiz turdaki iki yüz on dört bin
dokuz yüz onla karşılaştırıldığında düşüş **yüzde bir**. İki alan işaretlendi, dört iz noktası
kapandı ve toplam maruziyet neredeyse hiç değişmedi.

Bunun nedeni durum bölmenin kendisi değil, işaretlemenin sınırıdır. İşaret bir tanımın gösterimine
konur; tanım sınırını geçen değer işareti geride bırakır. Modül çıktısı bir sınırdır, bir tanımın
başka bir tanıma verdiği değer başka bir sınırdır ve her sınırda aynı şey olur — değer geçer,
damga geçmez. Durumu bölmek kayıt yönetimini kolaylaştırdı, ama hassas değer bakımından yaptığı şey
**kopya sayısını bölünme sayısı kadar artırmak** oldu.

#### Özet

- Kurgu bir parola tek bir uygulamada 7 iz noktasının 7'sinde görünüyor ve 14 kopya bırakıyor; hat
  kaydı tek başına 5 kopya taşıyor çünkü plan, günlük ve çıktıyı kopyalıyor.
- Yalnız `parola` işaretlendiğinde kopya 14'ten 10'a indi ama görünen nokta 7'de kaldı — değer bir
  bağlantı dizesine kopyalanmıştı; iki alan da işaretlenince kopya 4'e, nokta 3'e indi ve plan,
  günlük, modül çıktısı ile hat kaydı kapandı.
- Açık kalan 3 nokta değişken dosyası, durum dosyası ve sağlayıcı hata mesajıdır; durum dosyası üç
  turda da 2 kopya taşıdı, çünkü kayıt karşılaştırma yapabilmek için değeri tutmak zorundadır.
- Döndürme sonrası yeni koşumda eski değer 0 kopya bırakıyor, kalıntıda 3 yerde 8 kopya duruyor:
  eski durum sürümü, arşiv hat kaydı ve depo geçmişi.
- İşaretleme kopyaların yüzde 71'ini sildi ama maruziyetin yalnız yüzde 32'sini; kalan maruziyetin
  yüzde 60'ı depodaki değişken dosyasındadır.
- Modül çıktısını tüketen 3 tanım değeri kendi durum dosyalarına yazınca açık nokta 3'ten 6'ya,
  kopya 4'ten 7'ye çıktı ve maruziyetin düşüşü yüzde 32'den yüzde 1'e indi: değer tanım sınırını
  geçerken damgası geçmiyor.

#### Sonraki Adım

Bu konu boyunca kayıt dört kez ele alındı. Önce durum dosyası ve kilit kuruldu, sonra durum
bölündü, sonra tekrar eden tanım bir modüle çıkarıldı ve paylaşılan bir kayıttan sürümlenerek
çekildi, en sonunda da içinden geçen hassas değerler işaretlendi. Her adımda kaydın kapsamı,
bedeli ve kendi riski sayıldı.

Bir şey hiç yapılmadı: kayıt **uygulanmadan önce doğru olup olmadığı hiç sınanmadı**. Bu konuda
yazılan her kayıt, sınanma yerine uygulanarak denendi — plan bakıldı, uygulandı, sonuç görüldü.
Yanlış yazılmış bir alan, bir politikayı çiğneyen bir kaynak ya da beklenenden pahalı bir değişiklik
ancak uygulama sırasında ya da sonrasında ortaya çıktı. Bir sonraki konu bu boşluğu ele alır: altyapı
tanımı üzerinde uygulamadan önce koşan denetimler yazılır ve şu sayılır — bir tanımın kaç kusuru
metne bakarak yakalanabilir, kaçı ancak uygulanınca görünür ve yakalayan denetimin kaç yanlış alarm
ürettiği.

## Doğrulama ve Yapılandırma Yönetimi

Altyapı değişikliğinin uygulanmadan önce sınanması: biçim ve doğrulama denetimleri, kural ihlallerini erken yakalayan politika taraması, birim, sözleşme ve uçtan uca altyapı testi, değişikliğin bütçeye etkisinin tahmini, ajanlı ve ajansız yapılandırma yönetimi modelleri, önceden pişmiş sunucu imajı üretimi ve onaylı otomatik uygulama akışının hatta tümleştirilmesi.

### Biçim ve Doğrulama Denetimleri

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/bicim-ve-dogrulama-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ı.

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.

```js
// 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.

```js
// 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.

```js
// 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.

### Politika ve Uyumluluk Taraması

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/politika-ve-uyumluluk-taramasi

Dışarıdan gelen kuralların altyapı kaydı üzerinde koşturulması: aynı politika kümesinin bir kez tanım metni üzerinde, bir kez çözülmüş plan üzerinde koşturulması, iki taramanın yakalanan, kaçan ve yanlış alarm sayılarının karşılaştırılması, hesaplanan değerlerin tanım taramasından nasıl kaçtığı, erkenlik ile doğruluk arasındaki değiş tokuş ve muafiyet kaydının rapordan düşürdüğü ihlallerin sayılması.

Önceki ders kaydın **doğruluğunu** sınadı: biçimi tutuyor mu, söz dizimi geçerli mi, başvurular
çözülüyor mu. Bunların hepsi kaydın kendi kurallarıydı. Ama kusursuz bir kayıt yine de kabul
edilemez olabilir — şifrelemesi kapalı bir arşiv, açık ağa bakan bir makine ya da yedeği üç gün olan
bir veri deposu söz dizimi bakımından kusursuzdur.

**Politika taraması** dışarıdan gelen kuralları kayıt üzerinde koşturur. Yakalanan, kaçan ve yanlış
alarm üçlüsü daha önce **uygunluk işlevi** ölçülürken mimari kurallar üzerinde kurulmuştu; mekaniği
orada ölçüldüğü için burada tekrarlanmaz, değişen ölçülen nesnedir. Bu dersin sorusu şudur:
politika **tanım** üzerinde mi, yoksa **plan** üzerinde mi koşmalı? Tanım erkendir ve sağlayıcıya
hiç bağlanmaz ama yalnız yazılanı görür; plan geçtir ve sağlayıcı çağrısı ister ama çözülmüş değeri
görür. İki tarama aynı politika kümesiyle aynı kayıt üzerinde koşturulur ve dört şey sayılır:
yakalanan, kaçan, yanlış alarm ve muafiyetin açtığı yol. Through-line **bölgesel ölçüm ağıdır** ve
kurgudur.

#### Yazılan Değer ile Çözülen Değer

Bir kayıtta yazılan her değer okunduğu gibi değildir. Dört yerde metin ile gerçek ayrılır:
**değişken** (değer başka satırda tanımlıdır), **sağlayıcı varsayılanı** (alan bırakılır, değeri
sağlayıcı koyar), **küme temelli yineleme** (tek blok birden çok örneğe açılır) ve **koşul** (blok
belirli bir ortamda hiç yaratılmaz). Politikanın hangi katmanda koşacağı sorusu tam da bu dördü
yüzünden vardır.

**AD7 — ölçüm ağının üretim ortamı tek bir kayıt dosyasında tutulur ve dördünü de içerir (kurgu).**
**AD8 — sağlayıcı varsayılanları kurgudur**: makinenin genel ağ alanı boş bırakılırsa `evet`,
nesne deposunun şifreleme alanı boş bırakılırsa `hayir` gelir. **AD9 — uygulama anında atanan
değerler kayıtta `@atanacak` olarak durur ve planda da çözülmez.**

```js
// kayit.mjs — olcum aginin uretim ortami kaydini yazar; degisken, saglayici varsayilani,
// kume temelli yineleme ve kosul icerir (kurgu)
import { writeFileSync } from "node:fs";

const KAYIT = `degisken ortam = uretim
degisken sahip = olcum-ekibi
degisken yedek = 3

kaynak ag/omurga-bati:
  bolge = bati
  blok  = 10.20.0.0/16
  sahip = $sahip

kaynak ag/omurga-dis:
  bolge = bati
  blok  = 198.51.100.0/24
  sahip = $sahip

kaynak makine/toplayici:
  yinele      = bati,dogu,kuzey
  bolge       = $yinele
  genel_ip    = evet
  genel_adres = @atanacak
  sahip       = $sahip

kaynak makine/gecit:
  bolge       = bati
  genel_ip    = evet
  genel_adres = @atanacak
  sahip       = $sahip
  muafiyet    = P2:2026-03-31

kaynak makine/arsivci:
  bolge       = bati
  genel_ip    = @varsayilan
  genel_adres = @atanacak
  sahip       = $sahip

kaynak makine/deneme:
  kosul       = ortam=hazirlik
  bolge       = bati
  genel_ip    = evet
  genel_adres = @atanacak
  sahip       = $sahip

kaynak veri_deposu/olcum-deposu:
  bolge    = bati
  yedek    = $yedek
  sahip    = $sahip
  muafiyet = *:2026-06-30

kaynak kuyruk/olcum-kuyrugu:
  bolge   = bati
  saklama = 720

kaynak kuyruk/deneme-kuyrugu:
  kosul   = ortam=hazirlik
  bolge   = bati
  saklama = 720
  sahip   = $sahip

kaynak nesne_deposu/olcum-arsiv:
  bolge    = bati
  sifreli  = @varsayilan
  sahip    = $sahip
  muafiyet = P1:2025-11-30
`;

writeFileSync("kayit.tanim", KAYIT);
const say = (k) => (KAYIT.match(k) ?? []).length;
console.log(`kayit.tanim: ${KAYIT.split("\n").length - 1} satir, ${say(/^degisken /gm)} degisken, ` +
  `${say(/^kaynak /gm)} blok\n`);
console.log(`cozulmesi gereken deger: ${say(/= \$/gm)} degisken basvurusu, ` +
  `${say(/@varsayilan/g)} saglayici varsayilani, ${say(/@atanacak/g)} uygulamada atanan; ` +
  `${say(/^  yinele /gm)} yinelemeli blok, ${say(/^  kosul /gm)} kosullu blok, ` +
  `${say(/^  muafiyet /gm)} muafiyet kaydi`);
```

```
kayit.tanim: 62 satir, 3 degisken, 10 blok

cozulmesi gereken deger: 11 degisken basvurusu, 2 saglayici varsayilani, 4 uygulamada atanan; 1 yinelemeli blok, 2 kosullu blok, 3 muafiyet kaydi
```

On blok yazılıdır ama plana on blok girmez: biri yinelemeyle üç örneğe açılır, ikisi koşulu
tutmadığı için hiç girmez. Tanım katmanında on yedi değer henüz çözülmemiş durumdadır.

#### İki Tarama

**AD10 — politika kümesi yedi kuraldır ve ekipçe kabul edilmiştir (kurgu).** P1 nesne deposu
şifreli olmalı, P2 makine açık ağa bakmamalı, P3 veri deposu yedeği en az yedi gün olmalı, P4 kuyruk
saklaması en çok 168 saat olmalı, P5 her kaynağın sahibi yazılmalı, P6 ağ bloğu özel aralıkta
olmalı, P7 makinenin genel adresi kayıtlı aralıkta olmalı. **AD11 — muafiyet kaydı bir politika
koduna (ya da `*` jokerine) ve bir bitiş tarihine bağlıdır; bugün 2026-02-15 kabul edilir (kurgu).**
**AD12 — plan üretimi sağlayıcıya tür başına bir çağrı yapar; çağrı dört, örnek başına plan hesabı
bir saniyedir (kurgu).**

Her politika okuduğu alanı bildirir. Bir tarama o alanın değerini çözemiyorsa — değer `$` ya da `@`
ile başlıyorsa — kural **kararsız** kalır ve ne ihlal ne uygun sayılır. Plan taraması gerçeği
görür; tanım taraması onunla karşılaştırılır.

```js
// tara.mjs — ayni politika kumesi once tanim uzerinde, sonra plan uzerinde kosturulur (model)
import { readFileSync } from "node:fs";

const BUGUN = "2026-02-15";
const VARSAYILAN = { "makine.genel_ip": "evet", "nesne_deposu.sifreli": "hayir" }; // kurgu
// [kod, kural, kuralin okudugu alan (null: degere bakmaz)]
const POLITIKA = [
  ["P1", (t, a) => t !== "nesne_deposu" || a.sifreli === "evet", "sifreli"],
  ["P2", (t, a) => t !== "makine" || a.genel_ip === "hayir", "genel_ip"],
  ["P3", (t, a) => t !== "veri_deposu" || Number(a.yedek) >= 7, "yedek"],
  ["P4", (t, a) => t !== "kuyruk" || Number(a.saklama) <= 168, "saklama"],
  ["P5", (t, a) => a.sahip !== undefined, null],
  ["P6", (t, a) => t !== "ag" || a.blok.startsWith("10."), "blok"],
  ["P7", (t, a) => t !== "makine" || a.genel_adres.startsWith("10."), "genel_adres"],
];

const DEGISKEN = {}, BLOK = [];
let c = null;
for (const s of readFileSync("kayit.tanim", "utf8").split("\n")) {
  const d = /^degisken (\w+) = (.+)$/.exec(s), h = /^kaynak ([a-z_]+)\/([a-z0-9-]+):$/.exec(s);
  const a = /^\s+(\w+)\s*=\s*(.+)$/.exec(s);
  if (d) DEGISKEN[d[1]] = d[2];
  else if (h) BLOK.push((c = { tur: h[1], ad: h[2], alan: {} }));
  else if (a && c) c.alan[a[1]] = a[2].trim();
}

// plan: kosul degerlendirilir, yineleme acilir, degisken ve saglayici varsayilani cozulur;
// "@atanacak" cozulmez, cunku degeri uygulama aninda atanir
const PLAN = [];
for (const b of BLOK) {
  const [k, v] = (b.alan.kosul ?? "=").split("=");
  if (b.alan.kosul && DEGISKEN[k] !== v) continue;
  for (const oge of b.alan.yinele ? b.alan.yinele.split(",") : [null]) {
    const alan = {};
    for (const [ad, deger] of Object.entries(b.alan)) {
      if (["yinele", "kosul", "muafiyet"].includes(ad)) continue;
      alan[ad] = deger === "@varsayilan" ? VARSAYILAN[`${b.tur}.${ad}`]
        : deger === "$yinele" ? oge
          : deger.startsWith("$") ? DEGISKEN[deger.slice(1)] : deger;
    }
    PLAN.push({ tur: b.tur, blok: b.ad, ad: oge ? `${b.ad}-${oge}` : b.ad, alan,
      muafiyet: b.alan.muafiyet });
  }
}

const cozulmemis = (v) => v !== undefined && /^[$@]/.test(v);
const muafMi = (n, kod) => {
  const [p, tarih] = (n.muafiyet ?? ":").split(":");
  return (p === "*" || p === kod) && tarih >= BUGUN;
};
function tara(nesneler) {
  const bulgu = [];
  let kararsiz = 0;
  for (const n of nesneler) for (const [kod, kural, alan] of POLITIKA) {
    if (alan && cozulmemis(n.alan[alan])) { kararsiz += 1; continue; }
    if (!kural(n.tur, n.alan)) bulgu.push({ ad: n.ad, blok: n.blok, kod, muaf: muafMi(n, kod) });
  }
  return { bulgu, kararsiz };
}

const tanimNesnesi = BLOK.map((b) => ({ tur: b.tur, ad: b.ad, blok: b.ad, alan: b.alan,
  muafiyet: b.alan.muafiyet }));
const T = tara(tanimNesnesi), P = tara(PLAN);
const esit = (x, y) => x.blok === y.blok && x.kod === y.kod;
const yakalanan = P.bulgu.filter((p) => T.bulgu.some((t) => esit(t, p)));
const yanlis = T.bulgu.filter((t) => !P.bulgu.some((p) => esit(t, p)));
const SURE = new Set(BLOK.map((b) => b.tur)).size * 4 + PLAN.length; // AD12: kurgu saniye

console.log(`${"tarama".padEnd(9)}${"gordugu deger".padEnd(15)}${"bulgu".padStart(6)}` +
  `${"yakalanan".padStart(11)}${"kacan".padStart(7)}${"yanlis alarm".padStart(14)}` +
  `${"muaf".padStart(6)}${"raporlanan".padStart(12)}${"kararsiz".padStart(10)}${"hazir".padStart(8)}`);
for (const [ad, deger, S, yak, yan, sure] of [
  ["tanim", "yazili", T, yakalanan.length, yanlis.length, 0],
  ["plan", "cozulmus", P, P.bulgu.length, 0, SURE]]) {
  const muaf = S.bulgu.filter((b) => b.muaf).length;
  console.log(`${ad.padEnd(9)}${deger.padEnd(15)}${String(S.bulgu.length).padStart(6)}` +
    `${`${yak}/${P.bulgu.length}`.padStart(11)}${String(P.bulgu.length - yak).padStart(7)}` +
    `${String(yan).padStart(14)}${String(muaf).padStart(6)}` +
    `${String(S.bulgu.length - muaf).padStart(12)}${String(S.kararsiz).padStart(10)}` +
    `${`${sure} sn`.padStart(8)}`);
}

console.log(`\nyalniz planin gordugu ihlaller:`);
for (const p of P.bulgu.filter((p) => !T.bulgu.some((t) => esit(t, p)))) {
  const alan = POLITIKA.find(([k]) => k === p.kod)[2];
  console.log(`  ${p.kod}  ${p.ad.padEnd(16)}${alan.padEnd(12)}tanimda ` +
    `"${BLOK.find((b) => b.ad === p.blok).alan[alan]}", planda "${PLAN.find((n) => n.ad === p.ad).alan[alan]}"`);
}

const M = BLOK.filter((b) => b.alan.muafiyet).map((b) => ({ ad: `${b.tur}/${b.ad}`,
  kayit: b.alan.muafiyet, yururlukte: b.alan.muafiyet.split(":")[1] >= BUGUN }));
console.log(`\n${"muafiyet".padEnd(28)}${"kayit".padEnd(16)}${"durum".padEnd(14)}` +
  `${"kapattigi politika".padStart(19)}${"dusurdugu ihlal".padStart(17)}`);
for (const m of M) {
  const d = P.bulgu.filter((b) => b.muaf && `${PLAN.find((n) => n.ad === b.ad).tur}/${b.blok}` === m.ad);
  const kapsam = m.yururlukte ? (m.kayit.startsWith("*") ? POLITIKA.length : 1) : 0;
  console.log(`${m.ad.padEnd(28)}${m.kayit.padEnd(16)}` +
    `${(m.yururlukte ? "yururlukte" : "suresi dolmus").padEnd(14)}` +
    `${String(kapsam).padStart(19)}${String(d.length).padStart(17)}`);
}
console.log(`\ntanim taramasinin yanlis alarmi: ` +
  yanlis.map((y) => `${y.kod} ${y.ad}`).join(", ") + ` (kosulu yanlis, plana hic girmiyor); ` +
  `her iki taramanin da karar veremedigi politika P7 — genel_adres uygulama aninda atanir`);
```

```
tarama   gordugu deger   bulgu  yakalanan  kacan  yanlis alarm  muaf  raporlanan  kararsiz   hazir
tanim    yazili              7       7/10      3             2     1           6         7    0 sn
plan     cozulmus           10      10/10      0             0     2           8         5   30 sn

yalniz planin gordugu ihlaller:
  P2  arsivci         genel_ip    tanimda "@varsayilan", planda "evet"
  P3  olcum-deposu    yedek       tanimda "$yedek", planda "3"
  P1  olcum-arsiv     sifreli     tanimda "@varsayilan", planda "hayir"

muafiyet                    kayit           durum          kapattigi politika  dusurdugu ihlal
makine/gecit                P2:2026-03-31   yururlukte                      1                1
veri_deposu/olcum-deposu    *:2026-06-30    yururlukte                      7                1
nesne_deposu/olcum-arsiv    P1:2025-11-30   suresi dolmus                   0                0

tanim taramasinin yanlis alarmi: P2 deneme, P4 deneme-kuyrugu (kosulu yanlis, plana hic girmiyor); her iki taramanin da karar veremedigi politika P7 — genel_adres uygulama aninda atanir
```

#### Kimin Ne Gördüğü

Plan taraması on ihlal bulur; tanım taraması bunların yedisini yakalar, üçünü kaçırır ve üstüne iki
yanlış alarm verir. Kaçan üçünün ortak yanı çıktının ikinci bölümünde tek tek yazılıdır: hepsi
**hesaplanan değerdir**. Arşivci makinesinin genel ağ alanı tanımda `@varsayilan` yazar, planda
`evet` olur; veri deposunun yedeği tanımda `$yedek` yazar, planda `3` olur; arşivin şifreleme alanı
tanımda `@varsayilan` yazar, planda `hayir` olur. Üçünde de kayıt metni ihlali **saklamaz**, yalnız
göstermez — değer kaydın başka bir yerinde ya da sağlayıcıdadır.

İki yanlış alarmın nedeni de aynı asimetrinin öteki ucudur: koşulu tutmayan iki blok plana hiç
girmez, ama tanım metninde ihlalli değerleriyle dururlar. Tanım taraması bunları raporlar ve
raporlanan altı bulgunun ikisi, yani üçte biri, hiç yaratılmayacak kaynaklara aittir.

İki taramayı birlikte koşturmak da bedelsiz değildir. Tanım raporunun altı satırından dördü ile
plan raporunun sekiz satırından altısı aynı dört kusuru anlatır; ekip aynı ihlali iki ayrı raporda
görür ve hangisinin kapatıldığını izlemek için üçüncü bir kayıt tutar. Kapıya yalnız birini koymak
zorunda olan bir hat içinse soru "hangisi daha doğru" değil, **hangi hata biçiminin daha pahalı
olduğudur**: yanlış alarm insan zamanı harcar ve sonunda kuralın kapatılmasıyla biter, kaçan ihlal
ise üretime iner.

Bir üçüncü fark sayıda saklıdır. Tanım taraması yedi bulgu üretir ama bunların beşi gerçek
ihlaldir; o beş bulgu planın yedi ihlalini kapsar, çünkü yinelemeli tek blok üç örneğe açılır. Aynı
kusur tanımda bir kez, planda üç kez raporlanır. Düzeltme her iki durumda da tek satırdadır; fark
raporun uzunluğunda ve sayının nasıl okunacağındadır.

#### Erkenlik ile Doğruluk

Tabloda son iki sütun bu dersin değiş tokuşudur. Tanım taraması sıfır saniyede hazırdır: tek
dosyayı okur, sağlayıcıya bağlanmaz, kayıt her değiştiğinde koşabilir. Plan taraması otuz saniye
bekler — beş kaynak türü için birer sağlayıcı çağrısı, sonra on plan örneği için hesap — ve ancak
plan üretilebilen bir ortamda koşar; yetkisi olmayan bir katkı hattında hiç koşamaz.

Pratikte ikisinin yeri de farklıdır. Tanım taraması kaydın yazıldığı yerde, katkı daha
birleşmeden koşabilir; yanlış alarmı orada en ucuzdur, çünkü bloğun koşullu olduğunu bilen kişi
raporu okuyan kişidir. Plan taraması ise planın üretildiği kapıda durur ve orada verdiği karar
bağlayıcıdır.

Karşılığında aldığı şey doğruluktur: kaçan sıfır, yanlış alarm sıfır. Buradan çıkan sonuç
"hangisi daha iyi" değildir, ikisinin **farklı hata biçimleri** olduğudur. Tanım taraması yanlış
alarm üretir ve hesaplanan değeri kaçırır; plan taraması geç gelir ve bir sağlayıcı bağlantısına
bağımlıdır. Kararsız sütunu ikisinin ortak sınırını gösterir: tanım yedi, plan beş kuralda karar
veremez ve ikisinin de karar veremediği P7'nin nedeni aynıdır — genel adres uygulama anında
atanır, iki katmanda da yazılı değildir.

#### Muafiyetin Açtığı Yol

Üç muafiyet kaydı var ve üçü üç ayrı davranış gösteriyor. Geçidin muafiyeti bir politikaya bağlıdır
ve yürürlüktedir: tek bir ihlali rapordan düşürür, geçidin öteki altı politikası açık kalır. Veri
deposunun muafiyeti jokerdir: bugün yine tek bir ihlali düşürür, ama kapattığı politika sayısı
yedidir — o kaynağa sonradan eklenecek her ihlal de sessizce geçer, muafiyet süresi dolana dek.
Arşivin muafiyetinin süresi dolmuştur ve düşürdüğü ihlal sıfırdır; şifrelemenin kapalı olması
rapora geri dönmüştür.

Muafiyetin kendisi de bir kayıttır ve bakımı kimsede değildir. Üç kayıttan biri süresi dolmuş
hâlde durmaktadır; kimse silmemiştir. Süresi dolmuş muafiyet görünürdür — ihlali rapora geri getirir
ve kendini duyurur. Tehlikeli olan öteki uçtur: süresi dolmamış ve unutulmuş bir muafiyet sessizdir,
raporu kısaltır ve ihlali yerinde bırakır. Muafiyet kaydını denetleyen tek şey tarihtir; joker
kaydın kapsamını denetleyen bir kural yoktur.

Sayı olarak: plan taraması on ihlal buldu, ikisi muafiyetle düştü, sekizi raporlandı. Muafiyetin
açtığı yol **iki ihlal** genişliğindedir ve bunun biri joker kaydın açtığı yoldur. Muafiyetin
bedeli düşürdüğü ihlal sayısıyla değil, **kapattığı politika sayısıyla** ölçülür: bir politikaya
bağlı muafiyet bir kapı açar, joker muafiyet yedi kapı birden.

#### Özet

- Aynı yedi kurallık politika kümesi kurgu ölçüm ağı kaydının iki katmanında koşturuldu: yazılı
  tanım metni ve çözülmüş plan.
- Plan taraması 10 ihlal buldu; tanım taraması 7'sini yakaladı, 3'ünü kaçırdı ve 2 yanlış alarm
  verdi — raporladığı 6 bulgunun 2'si hiç yaratılmayacak kaynaklara aitti.
- Kaçan üç ihlalin üçü de **hesaplanan değerdir**: ikisi sağlayıcı varsayılanı (`@varsayilan` →
  `evet` ve `hayir`), biri değişken (`$yedek` → `3`); tanım metni bunları göstermez.
- Erkenlik ile doğruluk takas edilir: tanım taraması 0 saniyede ve sağlayıcıya bağlanmadan hazırdır,
  plan taraması 30 saniye ve beş sağlayıcı çağrısı ister; ikisinin de karar veremediği P7'nin değeri
  uygulama anında atanır (tanımda 7, planda 5 kural kararsız kalır).
- Üç muafiyet kaydının ikisi yürürlüktedir ve 10 ihlalin 2'sini rapordan düşürür; joker muafiyet tek
  başına 7 politikayı birden kapatırken politikaya bağlı muafiyet 1 politikayı kapatır, süresi
  dolmuş muafiyet ise ihlali rapora geri getirir.

#### Sonraki Adım

İki tarama da kaydı okudu — biri yazıldığı gibi, öteki çözüldüğü gibi. İkisinin de göremediği şey
kaynağın **koştuğu hâlidir**: P7 kararsız kaldı çünkü genel adres uygulama anında atanır ve
politika kümesinin hiçbir kuralı "bu makine gerçekten açılıyor mu, kuyruğa yazılan ileti veri
deposuna ulaşıyor mu" diye sormaz. Sonraki ders bu soruyu üç seviyede sorar: altyapı tanımına
bakan birim doğrulaması, iki kaynağın arasındaki anlaşmayı sınayan sözleşme doğrulaması ve gerçek
kaynak yaratan uçtan uca doğrulama. Aynı kusur kümesinde üçünün tuttuğu, süresi ve maliyeti sayılır
— ve üçünün birden kaçırdığı ortak boşluk adıyla yazılır.

### Altyapı Testi

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/altyapi-testi

Altyapı değişikliğinin üç seviyede doğrulanması: birim, sözleşme ve uçtan uca doğrulamanın aynı on beş kusurluk kümede koşturulması, her seviyenin tuttuğu, yalnız kendisinin tuttuğu, süresi, yarattığı kaynak ve kurgu birim cinsinden maliyeti, üç seviyenin de kaçırdığı ortak boşluğun adlandırılması ve temizlenmeyen test kaynaklarının bıraktığı artık ile sızan maliyetin sayılması.

Politika taraması kaydın **kabul edilebilirliğini** sınadı ve iki katmanda da kararsız kalan bir
kural bıraktı: değeri uygulama anında atanan bir alan. O kararsızlık tesadüf değil, sınırdır —
tarama kaydı okur, koşan altyapıyı görmez. Kaynak gerçekten açılıyor mu, diski bağlanıyor mu,
kuyruğa yazılan ileti veri deposuna ulaşıyor mu: bunların hiçbiri metinde yazmaz.

**Altyapı testi** bu soruları sorar ve yazılım tarafındaki test seviyeleriyle aynı adları taşır —
birim, sözleşme, uçtan uca — ama hizalama adlarda değil, **neyin yaratıldığındadır**: burada en üst
seviye gerçek kaynak açar, gerçek ücret işletir ve arkasında iz bırakır. Bu ders üç seviyeyi aynı
kusur kümesinde koşturur ve her biri için üç şey sayar: tuttuğu kusur, süresi ve maliyeti. Sonra
iki sayı daha gelir: üç seviyenin birden kaçırdığı ortak boşluk ve testin arkasında bıraktığı
temizlenmemiş kaynak. Through-line **bölgesel ölçüm ağıdır** ve kurgudur.

#### Kusur Kümesi ve Üç Seviye

**AD13 — bilinen kusur kümesi on beş kusurdur, elle işaretlenmiştir ve her kusur bir katmana
bağlıdır**: `tanim` (tek kaynağın tanımında görünür), `iliski` (ancak iki kaynak karşılaştırılınca
görünür), `kosum` (ancak kaynak koşarken görünür), `degisim` (ancak var olan altyapı üzerine
uygulanınca görünür). **AD14 — birim doğrulaması tek kaynağın ya da modülün tanımını kaynak
yaratmadan sınar; sözleşme doğrulaması plan üzerinde koşar ve yalnız arayüz alanlarına bakar.**
**AD15 — uçtan uca doğrulama gerçek kaynak yaratır ama yalnız ana akışı koşturur; yan akışta kalan
kusur ona görünmez.**

```js
// seviye.mjs — ayni kusur kumesi uc dogrulama seviyesinde kosturulur (model)

// [kod, kusur, katman (nerede gorunur), alan, yol (uctan uca akisinda ana mi yan mi)]
const KUSUR = [
  ["D01", "makine diski 10 GB, toplayici yaziyor", "tanim", "disk_gb", "ana"],
  ["D02", "kuyruk saklamasi 0 saat", "tanim", "saklama", "yan"],
  ["D03", "arsiv sinifi sicak birakilmis", "tanim", "sinif", "yan"],
  ["D04", "toplayici boyutu kucuk", "tanim", "boyut", "ana"],
  ["D05", "sahip etiketi yazilmamis", "tanim", "sahip", "yan"],
  ["D06", "makine bolgesi ag bolgesiyle uyusmuyor", "iliski", "bolge", "ana"],
  ["D07", "kuyruk ile veri deposu ayri bolgede", "iliski", "bolge", "yan"],
  ["D08", "modul cikti adi tuketicinin bekledigi degil", "iliski", "cikti", "ana"],
  ["D09", "yedek penceresi bakim penceresiyle cakisiyor", "iliski", "pencere", "yan"],
  ["D10", "guvenlik kurali toplayici trafigini kesiyor", "kosum", "kural", "ana"],
  ["D11", "disk baglanmiyor, baglama noktasi yok", "kosum", "baglama", "ana"],
  ["D12", "kimlik yetkisi eksik, depoya yazilamiyor", "kosum", "yetki", "ana"],
  ["D13", "alan degisikligi kaynagi yeniden olusturuyor, veri gidiyor", "degisim", "disk_gb", "ana"],
  ["D14", "yerinde guncelleme sirasinda kesinti oluyor", "degisim", "boyut", "ana"],
  ["D15", "kayit disi elle degisikligin uzerine yaziliyor", "degisim", "kural", "ana"],
].map(([kod, ne, katman, alan, yol]) => ({ kod, ne, katman, alan, yol }));

// AD16: uctan uca dogrulama gercek kaynak yaratir; adet, yaratma suresi ve saatlik ucret kurgudur
const KAYNAK = [["ag", 2, 20, 0], ["makine", 3, 45, 4], ["kuyruk", 1, 30, 1],
  ["veri_deposu", 1, 180, 12], ["nesne_deposu", 1, 15, 1]];
const ADET = KAYNAK.reduce((s, [, n]) => s + n, 0);
const YARATMA = KAYNAK.reduce((s, [, n, y]) => s + n * y, 0);
const SAATLIK = KAYNAK.reduce((s, [, n, , u]) => s + n * u, 0);
const UCTAN_UCA_SURE = YARATMA + 60 + YARATMA / 2; // yaratma + akis dogrulamasi + silme

// AD17: sozlesme dogrulamasi yalniz arayuz alanlarina bakar
const SOZLESME_ALANI = ["bolge", "cikti", "disk_gb"];
const SEVIYE = [
  ["birim", (k) => k.katman === "tanim", 12, 0, 0],
  ["sozlesme", (k) => k.katman === "iliski" ||
    (k.katman === "tanim" && SOZLESME_ALANI.includes(k.alan)), 45, 0, 0],
  ["uctan uca", (k) => k.katman !== "degisim" && k.yol === "ana",
    UCTAN_UCA_SURE, ADET, (SAATLIK * UCTAN_UCA_SURE) / 3600],
];

const tuttugu = SEVIYE.map(([, f]) => KUSUR.filter(f).map((k) => k.kod));
console.log(`${"seviye".padEnd(11)}${"tuttugu".padStart(9)}${"yalniz kendisi".padStart(16)}` +
  `${"kacirdigi".padStart(11)}${"yaratilan kaynak".padStart(18)}${"sure".padStart(9)}` +
  `${"maliyet (kurgu birim)".padStart(23)}`);
SEVIYE.forEach(([ad, , sure, kaynak, ucret], j) => {
  const yalniz = tuttugu[j].filter((k) => tuttugu.every((t, o) => o === j || !t.includes(k)));
  console.log(`${ad.padEnd(11)}${`${tuttugu[j].length}/${KUSUR.length}`.padStart(9)}` +
    `${String(yalniz.length).padStart(16)}${String(KUSUR.length - tuttugu[j].length).padStart(11)}` +
    `${String(kaynak).padStart(18)}${`${sure} sn`.padStart(9)}${ucret.toFixed(2).padStart(23)}`);
});

const hepsi = new Set(tuttugu.flat());
const kacan = KUSUR.filter((k) => !hepsi.has(k.kod));
console.log(`\nuc seviye birlikte ${KUSUR.length} kusurun ${hepsi.size} tanesini tutar; ` +
  `toplam sure ${SEVIYE.reduce((s, [, , t]) => s + t, 0)} sn, ` +
  `maliyet ${SEVIYE.reduce((s, [, , , , u]) => s + u, 0).toFixed(2)} kurgu birim`);
console.log(`\nuc seviyenin de kacirdigi ${kacan.length} kusur (katman: ${kacan[0].katman}):`);
for (const k of kacan) console.log(`  ${k.kod}  ${k.ne}`);
console.log(`\nseviye basina kusur maliyeti: ` + SEVIYE.map(([ad, , sure, , ucret], j) =>
  `${ad} ${(sure / tuttugu[j].length).toFixed(1)} sn/kusur` +
  (ucret ? ` ve ${(ucret / tuttugu[j].length).toFixed(2)} birim/kusur` : "")).join("; "));
```

```
seviye       tuttugu  yalniz kendisi  kacirdigi  yaratilan kaynak     sure  maliyet (kurgu birim)
birim           5/15               3         10                 0    12 sn                   0.00
sozlesme        5/15               2         10                 0    45 sn                   0.00
uctan uca       7/15               3          8                 8   660 sn                   4.77

uc seviye birlikte 15 kusurun 12 tanesini tutar; toplam sure 717 sn, maliyet 4.77 kurgu birim

uc seviyenin de kacirdigi 3 kusur (katman: degisim):
  D13  alan degisikligi kaynagi yeniden olusturuyor, veri gidiyor
  D14  yerinde guncelleme sirasinda kesinti oluyor
  D15  kayit disi elle degisikligin uzerine yaziliyor

seviye basina kusur maliyeti: birim 2.4 sn/kusur; sozlesme 9.0 sn/kusur; uctan uca 94.3 sn/kusur ve 0.68 birim/kusur
```

#### Kimin Neyi Tuttuğu

Üç seviye birlikte on beş kusurun on ikisini tutar. Tek başına bakıldığında hiçbiri yarıyı geçmez:
birim beş, sözleşme beş, uçtan uca yedi. Asıl bilgi "yalnız kendisi" sütunundadır, çünkü bir
seviyeyi hattan çıkarmanın bedeli orada yazar. Birim doğrulaması çıkarılırsa üç kusur kimsesiz
kalır — kuyruğun sıfır saklaması, arşivin yanlış sınıfı, yazılmamış sahip etiketi. Üçü de tek bir
kaynağın tanımında, göz önünde durur ve üçü de yan akıştadır; uçtan uca doğrulama gerçek kaynağı
yaratır ama o yollardan hiç geçmez.

Sözleşme doğrulamasının yalnız kendisine ait iki kusuru da aynı nedenle oradadır: kuyruk ile veri
deposunun ayrı bölgede olması ve yedek penceresinin bakım penceresiyle çakışması ancak iki kaynak
yan yana konunca görünür ve ikisi de ana akışta değildir. Sözleşme doğrulamasının değeri
**karşılaştırmadadır**, koşumda değil; bu yüzden kaynak yaratmadan bir uçtan uca testin
göremeyeceğini görür.

Örtüşme de sayılabilir. Yakalanan on iki kusurdan biri — makinenin on gigabaytlık diski — üç
seviyenin üçüne birden takılır: tanımda yazılıdır, sözleşme alanlarından biridir ve ana akışta
koşarken de kendini gösterir. Üçü de aynı kusuru raporlar, ekip aynı şeyi üç kez okur. Üç kusur iki
seviyeye takılır, geri kalan sekizi tek bir seviyenin işidir. Bir hattın kapı sırasını belirleyen
şey tam da bu dağılımdır.

Uçtan uca doğrulamanın üç kusuru ise ancak koşum katmanında vardır: kesen bir güvenlik kuralı,
bağlanmayan bir disk, eksik bir yetki. Bunların hiçbiri kayıtta yanlış yazılmamıştır — kayıt
doğrudur, gerçek farklıdır. Bu, kursun ölçü ekseninin en açık hâlidir: metin ile gerçek arasındaki
fark ancak gerçek çalıştırılınca ölçülür.

#### Süre ve Maliyet

Son üç sütun aynı kapsamın fiyatıdır. Birim doğrulaması on iki saniyede biter, hiç kaynak
yaratmaz, ücreti sıfırdır. Sözleşme doğrulaması kırk beş saniye sürer — plan üretimini bekler — ama
yine hiçbir şey yaratmaz. Uçtan uca doğrulama sekiz gerçek kaynak açar, altı yüz altmış saniye
sürer ve 4,77 kurgu birim tutar; süresinin onda dokuzu yaratma ve silmedir, doğrulamanın kendisi
altmış saniyedir.

Kusur başına bakıldığında fark daha keskindir: birim 2,4 saniye/kusur, sözleşme 9,0 saniye/kusur,
uçtan uca 94,3 saniye ve 0,68 birim/kusur. Uçtan uca doğrulama bir kusuru birim doğrulamasının
kırk katı sürede tutar. Bu, "uçtan uca kötüdür" demek değildir — tuttuğu üç kusuru başka hiçbir
seviye tutamaz. Söylediği şey, **her seviyenin kendi katmanında en ucuz olduğu**: aynı kusuru iki
seviyede sınamak, kusuru pahalı olanda ikinci kez ödemektir.

Sıra da bu tablodan okunur. Birim doğrulaması on iki saniyede beş kusuru eler; ondan sonra koşan
uçtan uca doğrulama o beşini artık aramaz. Ters sıranın bedeli de hesaplanabilir: uçtan uca önce
koşarsa, birim doğrulamasının on iki saniyede bulacağı bir kusur için sekiz gerçek kaynak
yaratılmış, altı yüz altmış saniye beklenmiş ve 4,77 birim ödenmiş olur. Aynı kusurun bedeli, hangi
kapıda yakalandığına göre kırk kata kadar değişir.

#### Üç Seviyenin Ortak Boşluğu

Kaçan üç kusurun ortak adı çıktıda yazılıdır: **değişim katmanı**. Üç seviye de doğrulamayı
sıfırdan kurulan temiz bir ortamda yapar. Gerçek değişiklik ise sıfırdan kurulmuş bir ortama değil,
**var olan altyapının üzerine** uygulanır — ve kusurlar tam orada doğar: bir alanın değişmesi
kaynağı yeniden oluşturur ve içindeki veri gider; yerinde bir güncelleme kesinti üretir; kayıt dışı
yapılmış elle bir değişikliğin üzerine yazılır.

Üçünü de sınamak boş ortamda kurulan bir testle olanaksızdır, çünkü sınanacak şey **geçmişin
kendisidir**: kaynağın hâlihazırda taşıdığı veri, hâlihazırda aldığı trafik, hâlihazırda üzerinde
yapılmış kayıt dışı değişiklik. Bu boşluğu kapatan şey bir test seviyesi değil, uygulamadan önce
okunan plandır — hangi kalemin yeniden oluşturulacağını orada yazar. Kaydın kapsamadığı elle
değişiklik ise ne testte ne planda görünür; onun tek karşılığı sapmanın ayrıca ölçülmesidir.

#### Temizlenmeyen Kaynak

Uçtan uca doğrulamanın tablodaki 4,77 birimlik maliyeti, temizleme adımının her koşumda başarılı
olduğunu varsayar. Silme de yaratma gibi bir uygulama adımıdır ve o da başarısız olabilir: hat
zaman aşımına uğrar, bir kaynak başka bir kaynağa bağlı olduğu için silinemez, koşum yarıda kesilir.

```js
// artik.mjs — uctan uca dogrulamanin biraktigi artik kaynak ve sizan maliyet (model)

// AD18: otuz gun boyunca gunde bir uctan uca kosum; temizleme adimi bes kosumdan birinde
// basarisiz olur ve basarisiz kosumda her kaynak yarim olasilikla ayakta kalir (kurgu)
const TOHUM = 20260215;
let durum = TOHUM;
const rast = () => (durum = (durum * 48271) % 2147483647) / 2147483647; // carpim 2^53 altinda kalir

const KAYNAK = [["ag", 2, 0], ["makine", 3, 4], ["kuyruk", 1, 1], ["veri_deposu", 1, 12],
  ["nesne_deposu", 1, 1]]; // tur, adet, saatlik kurgu birim
const ORNEK = KAYNAK.flatMap(([t, n, u]) => Array.from({ length: n }, (_, i) => [`${t}-${i + 1}`, u]));
const GUN = 30, HATA = 0.2, KOSUM_UCRETI = 4.77; // 4.77: onceki blokta hesaplanan kosum maliyeti

const kalan = [];
let hatali = 0, sizan = 0;
for (let g = 1; g <= GUN; g += 1) {
  if (rast() < HATA) {
    hatali += 1;
    for (const [ad, u] of ORNEK) if (rast() < 0.5) kalan.push({ ad: `${ad}#k${g}`, u, gun: g });
  }
  sizan += kalan.reduce((s, k) => s + k.u, 0) * 24; // birikmis artik gun boyu ucret isletir
}

const amaclanan = GUN * KOSUM_UCRETI;
console.log(`${GUN} gun, ${GUN} kosum, tohum ${TOHUM}: temizleme ${hatali} kosumda basarisiz, ` +
  `${kalan.length} artik kaynak birikti`);
console.log(`\n${"artik kaynak".padEnd(20)}${"birakildigi gun".padStart(16)}` +
  `${"saatlik ucret".padStart(14)}${"ay sonuna dek ucret".padStart(21)}`);
for (const k of kalan) {
  console.log(`${k.ad.padEnd(20)}${String(k.gun).padStart(16)}${String(k.u).padStart(14)}` +
    `${String(k.u * 24 * (GUN - k.gun + 1)).padStart(21)}`);
}
console.log(`\namaclanan maliyet (${GUN} kosum x ${KOSUM_UCRETI}) ${amaclanan.toFixed(2)} birim; ` +
  `sizan maliyet ${sizan.toFixed(2)} birim; oran ${(sizan / amaclanan).toFixed(1)}x`);
const pahali = [...kalan].sort((a, b) => b.u - a.u)[0];
console.log(`ay sonundaki artik saatlik ucret ${kalan.reduce((s, k) => s + k.u, 0)} birim; ` +
  `en pahali artik ${pahali.ad} (${pahali.u} birim/saat); ` +
  `kayitta gorunen artik kaynak sayisi 0`);
```

```
30 gun, 30 kosum, tohum 20260215: temizleme 4 kosumda basarisiz, 11 artik kaynak birikti

artik kaynak         birakildigi gun saatlik ucret  ay sonuna dek ucret
ag-1#k3                            3             0                    0
kuyruk-1#k3                        3             1                  672
veri_deposu-1#k3                   3            12                 8064
nesne_deposu-1#k3                  3             1                  672
ag-2#k12                          12             0                    0
makine-3#k12                      12             4                 1824
veri_deposu-1#k12                 12            12                 5472
makine-1#k24                      24             4                  672
ag-1#k29                          29             0                    0
kuyruk-1#k29                      29             1                   48
veri_deposu-1#k29                 29            12                  576

amaclanan maliyet (30 kosum x 4.77) 143.10 birim; sizan maliyet 18000.00 birim; oran 125.8x
ay sonundaki artik saatlik ucret 47 birim; en pahali artik veri_deposu-1#k3 (12 birim/saat); kayitta gorunen artik kaynak sayisi 0
```

Otuz koşumun dördünde temizleme başarısız oldu ve on bir kaynak ayakta kaldı. Otuz koşumun
amaçlanan maliyeti 143 kurgu birimdi; sızan maliyet 18.000 birim, yani **yüz yirmi altı katı**.
Oranı bu kadar büyüten şey başarısızlık sayısı değil süredir: koşum on bir dakika sürer, artık
kaynak ise fark edilene kadar günlerce durur. Üçüncü günün veri deposu tek başına 8.064 birim
işletmiştir.

Tabloda okunacak ikinci bir şey daha var. On bir artığın yedisi üçüncü ve on ikinci günde
bırakılmıştır ve sızan maliyetin neredeyse tamamını onlar üretir: üçüncü günün dört artığı 9.408
birim, on ikinci günün üç artığı 7.296 birim işletirken yirmi dokuzuncu günün üç artığı yalnız 624
birim işletir. Temizlik başarısızlığının bedeli sabit değildir, **fark edilme süresiyle** çarpılır;
bu yüzden artık kaynağı azaltmanın yolu temizliği yüzde yüz başarılı kılmak değil, artığı erken
bulmaktır.

Son satır bu kursun ölçü ekseniyle doğrudan bağlıdır: **kayıtta görünen artık kaynak sayısı
sıfırdır.** Bu kaynakları kayıt yaratmadı, test yarattı; kaydın durumu onları içermez, kayda bakan
hiç kimse onları göremez. Testin bıraktığı artık, kaydın kapsamadığı altyapının en sessiz
biçimidir — ve onu bulmanın tek yolu sağlayıcıyı kayıtla karşılaştırmaktır.

#### Özet

- On beş kusurluk küme üç doğrulama seviyesinde koşturuldu: birim 5, sözleşme 5, uçtan uca 7 kusur
  tuttu; üçü birlikte 12'sini tuttu, hiçbiri tek başına yarıyı geçmedi.
- Yalnız kendisinin tuttuğu kusur sayısı 3, 2 ve 3'tür; sözleşme doğrulaması kaynak yaratmadan iki
  ilişki kusurunu tutar, uçtan uca doğrulama ise üç koşum kusurunu başka hiçbir seviyenin
  göremeyeceği biçimde tutar.
- Maliyet katmanla birlikte büyür: birim 12 sn ve 0 kaynak, sözleşme 45 sn ve 0 kaynak, uçtan uca
  660 sn, 8 gerçek kaynak ve 4,77 kurgu birim; kusur başına 2,4 / 9,0 / 94,3 saniye.
- Üç seviyenin ortak boşluğu **değişim katmanıdır**: üçü de sıfırdan kurulan temiz ortamda koşar,
  oysa gerçek değişiklik var olan altyapının üzerine uygulanır (yeniden oluşturma ve veri kaybı,
  yerinde güncelleme kesintisi, kayıt dışı elle değişikliğin üzerine yazılması).
- Otuz koşumda temizleme 4 kez başarısız oldu, 11 artık kaynak kaldı; amaçlanan 143 birime karşı
  18.000 birim sızdı (126 kat) ve bu kaynakların kayıtta görünen sayısı sıfırdır.

#### Sonraki Adım

Bu dersin son iki sayısı aynı yere bakıyor: uçtan uca doğrulamanın koşum maliyeti ve artık
kaynağın sızan maliyeti. İkisi de değişikliğin **parasal** yüzüdür ve ikisi de ancak olan bitince
sayıldı. Oysa aynı soru uygulamadan önce sorulabilir: bu değişiklik uygulanırsa aylık gider ne
kadar artar? Sonraki ders plan üzerinden değişiklik başına tahmin üretir ve gerçekleşenle
karşılaştırır — hangi kalem sabit birim fiyatlı olduğu için tahmin edilebiliyor, hangi kalem
kullanıma bağlı olduğu için edilemiyor, tahmin hatası nasıl dağılıyor ve "tahmin şu kadarı aşarsa
dur" biçiminde bir eşik kuralı bilinen değişiklik kümesinde kaç değişikliği gereksiz yere
durduruyor.

### Maliyet Tahmini

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/maliyet-tahmini

Altyapı değişikliğinin bütçeye etkisinin uygulanmadan önce sayılması: plan farkından değişiklik başına aylık tahmin üretilmesi, sabit birim fiyatlı kalemler ile kullanıma bağlı kalemlerin ayrılması, gerçekleşenle karşılaştırıldığında hatanın nereden geldiği ve nasıl dağıldığı, toplam doğruluğun tek tek değişikliklerdeki sapmayı nasıl gizlediği ve bir eşik kuralının kaç değişikliği gereksiz durdurup kaç pahalı değişikliği geçirdiği.

Önceki ders bir doğrulama koşumunun kendi maliyetini ve arkasında bıraktığı artığın sızan
maliyetini saydı. İkisi de olan bitince sayıldı — para harcandıktan sonra. Aynı soru uygulamadan
önce de sorulabilir, çünkü plan zaten elde: hangi kaynağın ekleneceği, hangisinin büyütüleceği,
hangisinin silineceği orada yazılıdır. Bir plan farkı aynı zamanda bir **fatura farkıdır**.

Sistem ölçeğinde kapasite ve maliyet farkındalığı daha önce ölçüldü; buradaki birim ise
**değişikliğin kendisidir**: bu katkı birleşirse aylık gider ne kadar artar? Bu ders plandan
değişiklik başına tahmin üretir, gerçekleşenle karşılaştırır ve üç şeyi sayar: hangi kalem
tahmin edilebiliyor, hangi kalem edilemiyor, hata nasıl dağılıyor. Sonra bir eşik kuralı — "tahmin
şu kadarı aşarsa dur" — bilinen değişiklik kümesinde koşturulur ve kuralın iki hatası birden
sayılır. Through-line **bölgesel ölçüm ağıdır**; fiyat listesi de tutarlar da kurgudur.

#### Tahmin Edilebilen ve Edilemeyen Kalem

Bir maliyet kalemi iki türden biridir. **Sabit birim fiyatlı** kalemin miktarı plandan doğrudan
okunur: üç makine, 120 gigabayt disk, 500 gigabayt depo. Bu kalemler için tahmin bir çarpma
işlemidir ve ay boyunca değişmez. **Kullanıma bağlı** kalemin miktarı ise planda yoktur; plan
kaynağın var olacağını söyler ama kaç istek alacağını, kaç gigabayt veri çıkaracağını, kaç ileti
taşıyacağını söylemez. Bu kalemler için tahmin, kayıtta yazılı **beklenen kullanıma** dayanır — yani
bir tahminin üstüne kurulmuş bir tahmindir.

```js
// tahmin.mjs — plan uzerinden degisiklik basina aylik maliyet tahmini uretir (model)
import { writeFileSync } from "node:fs";

// AD19: fiyat listesi kurgudur, hicbir saglayicinin fiyatiyla ilgisi yoktur; birim "kb" (kurgu birim)
const SABIT = { "makine-orta": 30, "makine-buyuk": 60, "disk-gb": 0.5, "depo-gb": 0.8,
  "arsiv-gb": 0.1 };                       // kalem basina aylik sabit birim fiyat
// kullanima bagli kalem: [aylik birim fiyat, belirsizlik carpani r] — gercek miktar 1/r ile r arasi
const KULLANIM = { "istek-milyon": [4, 4], "cikan-veri-gb": [0.9, 4], "ileti-milyon": [2, 2],
  "okuma-milyon": [3, 2] };

// AD20: on iki degisiklik plandan okunmustur; her kalemin miktari plandaki kaynak farkidir,
// kullanima bagli kalemlerde ise kayitta yazili beklenen kullanimdir (kurgu)
const DEGISIKLIK = [
  ["G01", "uc toplayici makine eklenir", [["makine-orta", 3], ["disk-gb", 120],
    ["cikan-veri-gb", 60]]],
  ["G02", "veri deposu 500 GB buyutulur", [["depo-gb", 500], ["okuma-milyon", 20]]],
  ["G03", "arsiv akisi eklenir", [["arsiv-gb", 2000], ["okuma-milyon", 30]]],
  ["G04", "olcum kuyrugu eklenir", [["ileti-milyon", 120]]],
  ["G05", "toplayici boyutu buyuk yapilir", [["makine-buyuk", 3], ["makine-orta", -3]]],
  ["G06", "yeni bolge: ag ve iki makine", [["makine-orta", 2], ["disk-gb", 80], ["cikan-veri-gb", 300]]],
  ["G07", "toplayici diski 40'tan 200 GB'a", [["disk-gb", 480], ["okuma-milyon", 10]]],
  ["G08", "yedek penceresi 7 gunden 30 gune", [["depo-gb", 1400]]],
  ["G09", "dis uc nokta acilir", [["istek-milyon", 20], ["cikan-veri-gb", 800]]],
  ["G10", "arsiv sinifi soguga alinir", [["arsiv-gb", -1500]]],
  ["G11", "toplayici sayisi 3'ten 9'a", [["makine-orta", 6], ["disk-gb", 240],
    ["cikan-veri-gb", 120]]],
  ["G12", "toplama sikligi iki katina", [["ileti-milyon", 40], ["okuma-milyon", 60]]],
];

const T = DEGISIKLIK.map(([kod, ne, kalemler]) => {
  let sabit = 0;
  const kullanim = [];
  for (const [kalem, miktar] of kalemler) {
    if (SABIT[kalem] !== undefined) sabit += SABIT[kalem] * miktar;
    else kullanim.push([kalem, KULLANIM[kalem][0] * miktar, KULLANIM[kalem][1]]);
  }
  const k = kullanim.reduce((s, [, t]) => s + t, 0);
  return { kod, ne, sabit, kullanim, tahmin: sabit + k, sabitKalem: kalemler.length - kullanim.length };
});
writeFileSync("tahmin.json", JSON.stringify(T));

console.log(`${"kod".padEnd(5)}${"degisiklik".padEnd(34)}${"sabit kalem".padStart(12)}` +
  `${"kullanim kalemi".padStart(16)}${"sabit kb".padStart(10)}${"kullanim kb".padStart(13)}` +
  `${"tahmin kb".padStart(11)}`);
for (const d of T) {
  const k = d.tahmin - d.sabit;
  console.log(`${d.kod.padEnd(5)}${d.ne.padEnd(34)}${String(d.sabitKalem).padStart(12)}` +
    `${String(d.kullanim.length).padStart(16)}${d.sabit.toFixed(1).padStart(10)}` +
    `${k.toFixed(1).padStart(13)}${d.tahmin.toFixed(1).padStart(11)}`);
}
const sabitToplam = T.reduce((s, d) => s + d.sabit, 0);
const kullanimToplam = T.reduce((s, d) => s + (d.tahmin - d.sabit), 0);
const kalem = T.reduce((s, d) => s + d.sabitKalem + d.kullanim.length, 0);
console.log(`\n${T.length} degisiklik, ${kalem} kalem: ` +
  `${T.reduce((s, d) => s + d.sabitKalem, 0)} sabit birim fiyatli (tahmin edilebilir), ` +
  `${T.reduce((s, d) => s + d.kullanim.length, 0)} kullanima bagli (tahmin edilemez)`);
console.log(`toplam tahmin ${(sabitToplam + kullanimToplam).toFixed(1)} kb; ` +
  `sabit bolum ${sabitToplam.toFixed(1)} kb (%${(100 * sabitToplam / (sabitToplam + kullanimToplam)).toFixed(0)}), ` +
  `kullanim bolumu ${kullanimToplam.toFixed(1)} kb (%${(100 * kullanimToplam / (sabitToplam + kullanimToplam)).toFixed(0)})`);
console.log(`kullanima bagli kalem tasiyan degisiklik: ${T.filter((d) => d.kullanim.length).length}/${T.length}`);
```

```
kod  degisiklik                         sabit kalem kullanim kalemi  sabit kb  kullanim kb  tahmin kb
G01  uc toplayici makine eklenir                  2               1     150.0         54.0      204.0
G02  veri deposu 500 GB buyutulur                 1               1     400.0         60.0      460.0
G03  arsiv akisi eklenir                          1               1     200.0         90.0      290.0
G04  olcum kuyrugu eklenir                        0               1       0.0        240.0      240.0
G05  toplayici boyutu buyuk yapilir               2               0      90.0          0.0       90.0
G06  yeni bolge: ag ve iki makine                 2               1     100.0        270.0      370.0
G07  toplayici diski 40'tan 200 GB'a              1               1     240.0         30.0      270.0
G08  yedek penceresi 7 gunden 30 gune             1               0    1120.0          0.0     1120.0
G09  dis uc nokta acilir                          0               2       0.0        800.0      800.0
G10  arsiv sinifi soguga alinir                   1               0    -150.0          0.0     -150.0
G11  toplayici sayisi 3'ten 9'a                   2               1     300.0        108.0      408.0
G12  toplama sikligi iki katina                   0               2       0.0        260.0      260.0

12 degisiklik, 24 kalem: 13 sabit birim fiyatli (tahmin edilebilir), 11 kullanima bagli (tahmin edilemez)
toplam tahmin 4362.0 kb; sabit bolum 2450.0 kb (%56), kullanim bolumu 1912.0 kb (%44)
kullanima bagli kalem tasiyan degisiklik: 9/12
```

Yirmi dört kalemin on üçü sabit birim fiyatlıdır, on biri kullanıma bağlı. Kalem sayısı bakımından
çoğunluk tahmin edilebilir görünür, ama tutar bakımından bölünme farklıdır: toplamın yüzde
elli altısı sabit, yüzde kırk dördü kullanıma bağlıdır. Daha önemlisi dağılımdır — on iki
değişikliğin dokuzu en az bir kullanıma bağlı kalem taşır. Üç değişiklik (G05, G08, G10) yalnız
sabit kalemden oluşur ve onların tahmini bir hesaplama değil, bir **kesinliktir**.

#### Gerçekleşenle Karşılaştırma

**AD21 — sabit birim fiyatlı kalem ay boyunca değişmez, hatası tam olarak sıfırdır; kullanıma
bağlı kalemin gerçek miktarı beklenenin `1/r` ile `r` katı arasındadır.** **AD22 — on iki
değişiklik aynı ay içinde uygulanır ve gerçekleşen tutar ay sonunda okunur (kurgu).** Belirsizlik
çarpanı kaleme göre değişir: dış istek ve çıkan veri en oynak kalemlerdir (`r = 4`), kuyruk iletisi
ve depo okuması daha durgundur (`r = 2`).

```js
// gercek.mjs — gerceklesen maliyet, tahmin hatasinin dagilimi ve esik kuralinin sonucu (model)
import { readFileSync } from "node:fs";

// AD21: sabit birim fiyatli kalem ay boyunca degismez, hatasi sifirdir; kullanima bagli kalemin
// gercek miktari beklenenin 1/r ile r kati arasindadir (r kalemin belirsizlik carpani)
const TOHUM = 20260401;
let durum = TOHUM;
const rast = () => (durum = (durum * 48271) % 2147483647) / 2147483647;

const G = JSON.parse(readFileSync("tahmin.json", "utf8")).map((d) => {
  const gercek = d.sabit + d.kullanim.reduce((s, [, tutar, r]) =>
    s + tutar * Math.pow(r, 2 * rast() - 1), 0);
  return { ...d, gercek, hata: gercek - d.tahmin };
});

console.log(`${"kod".padEnd(5)}${"tahmin kb".padStart(11)}${"gerceklesen kb".padStart(16)}` +
  `${"hata kb".padStart(10)}${"hata %".padStart(9)}${"kaynak".padStart(20)}`);
for (const d of G) {
  const yuzde = d.tahmin === 0 ? 0 : (100 * d.hata) / Math.abs(d.tahmin);
  console.log(`${d.kod.padEnd(5)}${d.tahmin.toFixed(1).padStart(11)}${d.gercek.toFixed(1).padStart(16)}` +
    `${d.hata.toFixed(1).padStart(10)}${yuzde.toFixed(0).padStart(9)}` +
    `${(d.kullanim.length ? `${d.kullanim.length} kullanim kalemi` : "yalniz sabit").padStart(20)}`);
}

const hatasiz = G.filter((d) => d.hata === 0);
const sapan = G.filter((d) => d.hata !== 0);
const oran = (d) => d.gercek / d.tahmin;
console.log(`\n${hatasiz.length} degisiklikte hata tam olarak sifir (yalniz sabit birim fiyatli ` +
  `kalem tasiyorlar); ${sapan.length} degisiklikte sapma var`);
console.log(`sapma araligi ${Math.min(...sapan.map(oran)).toFixed(2)}x ile ` +
  `${Math.max(...sapan.map(oran)).toFixed(2)}x arasinda; ` +
  `toplam tahmin ${G.reduce((s, d) => s + d.tahmin, 0).toFixed(1)} kb, ` +
  `gerceklesen ${G.reduce((s, d) => s + d.gercek, 0).toFixed(1)} kb ` +
  `(toplamda %${(100 * (G.reduce((s, d) => s + d.hata, 0) / G.reduce((s, d) => s + d.tahmin, 0))).toFixed(0)})`);

// AD23: esik kurali plandaki tahmine bakar: tahmin esigi asarsa degisiklik durur ve elle onaya gider
console.log(`\n${"esik kb".padStart(8)}${"duran".padStart(7)}${"gereksiz duran".padStart(16)}` +
  `${"gecen pahali".padStart(14)}${"dogru duran".padStart(13)}${"onaya giden butce".padStart(19)}`);
for (const E of [200, 300, 500, 800]) {
  const duran = G.filter((d) => d.tahmin > E);
  const gereksiz = duran.filter((d) => d.gercek <= E);
  const gecen = G.filter((d) => d.tahmin <= E && d.gercek > E);
  console.log(`${String(E).padStart(8)}${String(duran.length).padStart(7)}` +
    `${String(gereksiz.length).padStart(16)}${String(gecen.length).padStart(14)}` +
    `${String(duran.length - gereksiz.length).padStart(13)}` +
    `${duran.reduce((s, d) => s + d.tahmin, 0).toFixed(0).padStart(19)}`);
}
for (const E of [200, 300]) {
  const ad = (f) => G.filter(f).map((d) => d.kod).join(", ") || "yok";
  console.log(`\nesik ${E} kb: duran ${ad((d) => d.tahmin > E)}; ` +
    `gereksiz duran ${ad((d) => d.tahmin > E && d.gercek <= E)}; ` +
    `gecen pahali ${ad((d) => d.tahmin <= E && d.gercek > E)}`);
}
```

```
kod    tahmin kb  gerceklesen kb   hata kb   hata %              kaynak
G01        204.0           192.3     -11.7       -6   1 kullanim kalemi
G02        460.0           461.5       1.5        0   1 kullanim kalemi
G03        290.0           362.5      72.5       25   1 kullanim kalemi
G04        240.0           121.6    -118.4      -49   1 kullanim kalemi
G05         90.0            90.0       0.0        0        yalniz sabit
G06        370.0           535.2     165.2       45   1 kullanim kalemi
G07        270.0           288.3      18.3        7   1 kullanim kalemi
G08       1120.0          1120.0       0.0        0        yalniz sabit
G09        800.0           958.4     158.4       20   2 kullanim kalemi
G10       -150.0          -150.0       0.0        0        yalniz sabit
G11        408.0           368.8     -39.2      -10   1 kullanim kalemi
G12        260.0           316.2      56.2       22   2 kullanim kalemi

3 degisiklikte hata tam olarak sifir (yalniz sabit birim fiyatli kalem tasiyorlar); 9 degisiklikte sapma var
sapma araligi 0.51x ile 1.45x arasinda; toplam tahmin 4362.0 kb, gerceklesen 4664.7 kb (toplamda %7)

 esik kb  duran  gereksiz duran  gecen pahali  dogru duran  onaya giden butce
     200     10               2             0            8               4422
     300      5               0             2            5               3158
     500      2               0             1            2               1920
     800      1               0             1            1               1120

esik 200 kb: duran G01, G02, G03, G04, G06, G07, G08, G09, G11, G12; gereksiz duran G01, G04; gecen pahali yok

esik 300 kb: duran G02, G06, G08, G09, G11; gereksiz duran yok; gecen pahali G03, G12
```

Hata tablosunun ilk okunuşu yanıltıcıdır. Toplamda tahmin 4.362, gerçekleşen 4.664,7 kurgu birim —
yüzde yedi sapma, kabul edilebilir görünür. Ama bu yüzde yedi, tek tek değişikliklerdeki sapmaların
birbirini götürmesiyle oluşur: G04 yarı yarıya düşük gerçekleşir (0,51x), G06 yüzde kırk beş yüksek
(1,45x), G03 yüzde yirmi beş yüksek. **Toplam doğruluk, karar veren kişiye hiçbir şey vermez**,
çünkü kararlar tek tek değişikliklerde verilir. Bir bütçe raporu için yüzde yedi iyi bir sayıdır;
bir kapı için sapmanın 0,51x–1,45x aralığı asıl sayıdır.

İkinci okuma hatanın kaynağını gösterir. Üç değişiklikte hata **tam olarak sıfırdır** ve bu bir
şans değil, yapısal bir sonuçtur: o üçü yalnız sabit birim fiyatlı kalem taşır. Kalan dokuzun
hepsinde sapma vardır ve sapmanın büyüklüğü kalemin belirsizlik çarpanıyla birlikte artar. Tahmin
hatasını düşürmenin yolu daha iyi bir tahmin aracı değildir; **kullanıma bağlı kalemin payını
bilmektir**. Bir değişikliğin tahmini yanında "bu tutarın yüzde kaçı kullanıma bağlı" sayısı
yazılmıyorsa, tahmin tek başına ne kadar güvenilir olduğunu söylemez.

Tabloda tek negatif kalem de aynı kurala uyar: arşiv sınıfının soğuğa alınması aylık 150 birim
tasarruf olarak tahmin edilir ve tam olarak 150 birim tasarruf eder. Sabit birim fiyatlı bir kalemde
tasarruf da gider kadar kesindir; kullanıma bağlı bir kalemde vaat edilen düşüş ise aynı belirsizliği
taşır ve gerçekleşmeyebilir.

#### Eşik Kuralı

Tahmin bir kapıya bağlandığı anda karar üretmeye başlar: tahmin eşiği aşarsa değişiklik durur ve
elle onaya gider. Kuralın iki hatası vardır ve ikisi de tabloda sayılıdır. **Gereksiz duran**,
tahmini eşiği aşan ama gerçekte aşmayan değişikliktir; bedeli bekleyen bir katkı ve harcanan onay
zamanıdır. **Geçen pahalı**, tahmini eşiğin altında kalan ama gerçekte üstüne çıkan değişikliktir;
bedeli bütçedeki sürprizdir.

İki yüz birimlik eşik on iki değişikliğin onunu durdurur. Bu eşikte hiçbir pahalı değişiklik
kaçmaz — ama duranların ikisi (G01 ve G04) gerçekte eşiğin altında kalır ve onaya giden bütçe
4.422 birime, yani neredeyse toplamın tamamına çıkar. Kapı bu noktada bir kapı olmaktan çıkar:
her şey durur; onay bir tören hâline gelir. Üç yüz birimlik eşikte duran sayısı beşe iner ve o
beşin beşi de gerçekte eşiği aşar — ama bu kez iki değişiklik (G03 ve G12) tahmini altında kaldığı
için geçer ve gerçekte üstüne çıkar.

Sekiz yüz birimlik eşikte yalnız bir değişiklik durur ve bir tanesi kaçar. Hiçbir eşik ikisini
birden sıfırlamaz, çünkü eşik bir sayıya bakar ve o sayının kendisi belirsizdir. Kuralı
iyileştirmenin yolu eşiği daha iyi seçmek değil, **eşiğe bakılan sayıyı değiştirmektir**: tahminin
kendisi yerine tahminin kullanıma bağlı payı da eşiğe girerse, yalnız sabit kalem taşıyan G08 gibi
bir değişiklik yüksek tutarına rağmen otomatik geçebilir; çünkü onun tahmini kesindir.

Bir sınır daha var. Bu tahminlerin girdisi plandır ve plan da uygulamayı tam öngörmez: planda hiç
görünmeyen bir kalem tahmine de girmez, planda görünüp gerçekleşmeyen bir kalem ise tahmini şişirir.
Buradaki bütün sayılar planın doğru olduğunu varsayar; kayıt ile gerçek arasındaki fark, tahmin
hatasının üstüne binen ikinci bir hatadır.

#### Özet

- On iki değişikliğin tahmini plan farkından üretildi: 24 kalemin 13'ü sabit birim fiyatlı
  (tahmin edilebilir), 11'i kullanıma bağlıdır (tahmin edilemez); tutarın %56'sı sabit, %44'ü
  kullanıma bağlıdır ve 12 değişikliğin 9'u en az bir kullanıma bağlı kalem taşır.
- Yalnız sabit kalem taşıyan 3 değişiklikte hata tam olarak sıfırdır; kalan 9'unda sapma
  0,51x ile 1,45x arasındadır.
- Toplam sapma %7'dir ve bu sayı aldatıcıdır: tek tek sapmalar birbirini götürür, oysa kararlar
  değişiklik başına verilir.
- Eşik kuralının iki hatası ters yönde çalışır: 200 birimlik eşik 12 değişikliğin 10'unu durdurur
  (2'si gereksiz, onaya giden bütçe 4.422 birim) ve hiçbir pahalı değişikliği kaçırmaz; 300 birimde
  5 durur ve 2 pahalı değişiklik geçer; 800 birimde 1 durur ve 1 pahalı değişiklik geçer.
- Tahmin hatasını düşürmenin yolu daha iyi bir araç değil, tahminin **kullanıma bağlı payını**
  raporlamaktır; eşik o paya da bakarsa kesin tahminli yüksek tutarlı değişiklik gereksiz yere
  durmaz.

#### Sonraki Adım

Bu konunun buraya kadarki dersleri kaydı denetledi, politikadan geçirdi, sınadı ve fiyatladı.
Dördü de aynı şeye baktı: **kaynağın kendisine** — makine, disk, ağ, kuyruk, depo. Ama bir makine
yaratıldığında içi boştur; üzerinde hangi paketin kurulu olacağı, hangi dosyanın hangi izinle
duracağı, hangi servisin açık kalacağı sağlama katmanının kaydında yazmaz. Sonraki ders bu
sınırın öte yanını ele alır: makine yaratıldıktan sonra devreye giren **yapılandırma yönetimi**
katmanı, ajanlı ve ajansız yaklaşımların farkı ve bu katmanın kendi kaydının sağlama kaydıyla
nerede örtüşüp nerede ayrıldığı.

### Yapılandırma Yönetimi Araçları

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/yapilandirma-yonetimi-araclari

Ajanlı ve ajansız yapılandırma yönetimi tür olarak ölçülür: aynı otuz dört kalemlik ayar kümesi elli makineye iki modelde uygulanır; yayılma turu ve süresi, kısmi başarısızlıkta kayıttan ayrışan makine sayısı, sapmanın kaç dakika yaşadığı ve modelin kendi bedeli — ajan süreci, bağlantı yüzeyi, yetkili kimlik — sayılır.

Önceki ders bir değişikliği uygulanmadan önce fiyatladı: plan kalemlerine birim maliyet bağlandı,
tahminin ne kadar saptığı sayıldı, tahmin edilemeyen kalem adıyla yazıldı. Ama fiyatlanan şey
kaynağın kendisiydi — kaç makine, hangi ölçüde, kaç disk. Kaynak oluşturulduktan sonra **içine
giren ayar** o kaydın dışındadır: hangi paket sürümü kurulu, hangi dosya hangi izinle duruyor,
hangi servis açılışta başlıyor. Bu ayarlar hâlâ elle giriliyor.

Sağlama kaynağı yaratır; yapılandırma yönetimi kaynağın içini kayda bağlar. İki model vardır ve
ikisi de ürün olarak değil tür olarak anılır: **ajanlı** modelde makinede sürekli koşan bir süreç
kaydı kendi çeker ve uygular; **ajansız** modelde merkezdeki bir koşucu makinelere bağlanıp ayarı
iter. M22/K01 elle, betikle ve bildirimsel sağlamayı karşılaştırmıştı; o karşılaştırma burada
tekrarlanmaz. Buradaki soru **kaydı makineye taşıyan yolun** kendisidir.

**AD25.** Filo, bölgesel ölçüm ağının — su sayaçlarından okuma toplayan kurgu yazılımın — okuma
toplayıcılarını koşturan 50 makinedir. **AD26.** Kayıt 34 yapılandırma kalemi taşır; makinede
değiştirilebilir ayar sayısı 61'dir. **AD27.** Bütün sayılar kendi üretecimizden gelir; tohumlar
`20260731`, `20260802` ve `20260803` görünürdür ve sayılar bir ölçüm değil, büyüklük mertebesidir.
**AD28.** Ajansız koşucu aynı anda 12 makineye bağlanır, bağlantı 1 dakika, uygulama 4 dakika
sürer; ajan 30 dakikada bir, kendi ofsetinde yoklar. **AD29.** Koşum anında makinelerin yüzde 8'i
ağa kapalıdır, yüzde 4'ünde ajan süreci durmuştur; ajan kendi sürümünü günde bir kez dener, deneme
yüzde 94 başarılıdır ve başarısızlığın yüzde 35'i ajanı öldürür. **AD30.** 30 günde makine başına
0,6 elle değişiklik düşer, ajansız model haftalık çizelgeye üç kayıt değişikliği koşumu ekler,
ajan 46 MB bellek tutar ve bir kalem kayıtta 790 bayttır.

#### Aynı Ayar Kümesi, İki Yol

Aynı 34 kalem 50 makineye iki modelde uygulanır. Ajansız modelde eş zamanlı bağlantı sayısı tavan
olduğu için filo turlara bölünür; ajanlı modelde tur yoktur, her ajan kendi ofsetinde bağımsız
çeker.

```js
// olcum-agi/model.mjs — ayni ayar kumesinin iki modelde yayilmasi (model; urun degil tur)
export const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;

export const KALEM = 34;    // kayittaki yapilandirma kalemi
export const PENCERE = 30;  // ajanli model: yoklama araligi (dk)
const KOL = 12, UYGULAMA = 4, BAGLANTI = 1;  // ajansiz: es zamanli kol, uygulama, baglanma (dk)

// Filo: yoklama ofseti, o anki ulasilabilirlik ve ajan durumu ureteçten gelir.
export const filo = (n, tohum = 20260731) =>
  Array.from({ length: n }, ((r) => () => ({
    ofset: Math.floor(r() * PENCERE), ulasilabilir: r() > 0.08, ajanCalisir: r() > 0.04,
  }))(uretec(tohum)));

const yuzde = (d, p) => d.slice().sort((a, b) => a - b)[Math.floor((p / 100) * d.length)];

// Ajansiz: merkez turlar halinde baglanir, ulasilamayan makine bu kosumda atlanir.
// Ajanli: her ajan kendi ofsetinde ceker; merkez sirasi yok, duran ajan disarida kalir.
export const kosum = (m, model) => {
  const ajansiz = model === "ajansiz";
  const hedef = m.filter((x) => (ajansiz ? x.ulasilabilir : x.ajanCalisir));
  const sure = ajansiz
    ? hedef.map((_, i) => (Math.floor(i / KOL) + 1) * (BAGLANTI + UYGULAMA))
    : hedef.map((x) => x.ofset + UYGULAMA);
  return {
    model, tur: ajansiz ? Math.ceil(hedef.length / KOL) : 1, ulasan: hedef.length,
    ayrisan: m.length - hedef.length, p50: yuzde(sure, 50), p95: yuzde(sure, 95),
    max: Math.max(...sure),
  };
};

if (import.meta.url === `file://${process.argv[1]}`) {
  const m = filo(50);
  console.log(`50 makine, kayitta ${KALEM} kalem, kayittaki toplam: ${50 * KALEM} satir\n`);
  console.log("model".padEnd(10) + "tur".padEnd(6) + "p50".padEnd(8) + "p95".padEnd(8) +
    "max".padEnd(8) + "ulasan".padEnd(9) + "ayrisan".padEnd(10) + "tutan satir");
  for (const s of ["ajansiz", "ajanli"].map((x) => kosum(m, x)))
    console.log(s.model.padEnd(10) + String(s.tur).padEnd(6) + `${s.p50} dk`.padEnd(8) +
      `${s.p95} dk`.padEnd(8) + `${s.max} dk`.padEnd(8) + `${s.ulasan}/50`.padEnd(9) +
      `${s.ayrisan}/50`.padEnd(10) + `${s.ulasan * KALEM}/${m.length * KALEM}`);
  console.log("\nfilo buyudugunde tam yayilma:");
  console.log("makine".padEnd(9) + "ajansiz tur".padEnd(13) + "ajansiz max".padEnd(13) +
    "ajanli tur".padEnd(12) + "ajanli max");
  for (const n of [50, 200, 800]) {
    const f = filo(n), a = kosum(f, "ajansiz"), b = kosum(f, "ajanli");
    console.log(String(n).padEnd(9) + String(a.tur).padEnd(13) + `${a.max} dk`.padEnd(13) +
      String(b.tur).padEnd(12) + `${b.max} dk`);
  }
}
```

```
50 makine, kayitta 34 kalem, kayittaki toplam: 1700 satir

model     tur   p50     p95     max     ulasan   ayrisan   tutan satir
ajansiz   4     10 dk   20 dk   20 dk   47/50    3/50      1598/1700
ajanli    1     21 dk   32 dk   33 dk   45/50    5/50      1530/1700

filo buyudugunde tam yayilma:
makine   ajansiz tur  ajansiz max  ajanli tur  ajanli max
50       4            20 dk        1           33 dk
200      16           80 dk        1           33 dk
800      63           315 dk       1           33 dk
```

Elli makinede ajansız model daha hızlıdır: dört turda, 20 dakikada biter; ajanlı model 33 dakika
alır, çünkü en geç yoklayan ajan aralığın sonundadır. İkinci tablo bu sıralamayı bozar. Ajansız
modelde tur sayısı filoyla doğru orantılı büyür — 800 makinede 63 tur, 315 dakika. Ajanlı modelde
**33 dakika sabittir**: paralellik merkezde değil makinelerdedir.

Son sütun bu kursun ölçüsüdür. Kayıt 1700 satır söyler; tek bir başarılı koşumdan sonra ajansız
modelde 1598, ajanlı modelde 1530 satır gerçekten tutar. Fark **kayıttan ayrışan makinelerden**
gelir: ağa kapalı 3 makine o koşumda atlanır, süreci durmuş 5 makine hiç bakmaz. Başarısızlık
biçimi de aynı değildir. Ajansız modelde başarısızlık **merkezden görülür** — koşucu
bağlanamadığını raporlar. Ajanlı modelde **sessizdir**: bakmayan ajan hata da üretmez; merkez,
ajanın son ne zaman haber verdiğine bakmadıkça ayrışmayı fark etmez.

#### Sapma Ne Kadar Yaşıyor

Yayılma tek seferlik bir olaydır. Asıl fark, uygulamadan sonra makinede elle bir değişiklik
yapıldığında ortaya çıkar. Ajanlı model sürekli bakar; ajansız model tetiklenir ve aradaki sürede
sapma birikir. Bu kursun `iac-temelleri` konusu sürekli uzlaşma ile tetiklenen uygulama arasındaki
farkı kurmuştu; burada o fark **dakikayla** sayılır.

```js
// olcum-agi/sapma.mjs — iki modelde sapmanin kac dakika yasadigi (model)
import { uretec, filo, KALEM, PENCERE } from "./model.mjs";

const GUN = 1440, SON = 30 * GUN;
const AYAR = 61;    // makinede degistirilebilir ayar; bunun 34'u kayitta
const ORAN = 0.6;   // makine basina 30 gunde elle degisiklik (kurgu)
// Ajansiz model tetiklenir: haftalik cizelge + uc kayit degisikligi kosumu (kurgu gunler).
const TETIK = [0, 3.2, 7, 11.6, 14, 19.4, 21, 28].map((g) => Math.round(g * GUN));

const olaylar = (m, tohum = 20260802) => {
  const r = uretec(tohum);
  return m.flatMap((x) => (r() < ORAN
    ? [{ m: x, an: Math.floor(r() * SON), kayitta: r() * AYAR < KALEM }] : []))
    .sort((a, b) => a.an - b.an);
};

// Ajanli: her ajan kendi ofsetinde, PENCERE dakikada bir kayitla karsilastirir.
const ajanli = (o) => o.m.ajanCalisir
  ? ((((o.m.ofset - o.an) % PENCERE) + PENCERE) % PENCERE) || PENCERE : null;

// Ajansiz: bir sonraki tetige kadar yasar; makine o an kapaliysa bir tetik daha.
const ajansiz = (o) => {
  const k = TETIK.filter((t) => t > o.an);
  const t = o.m.ulasilabilir ? k[0] : k[1];
  return t === undefined ? null : t - o.an;
};

const say = (liste, omur) => {
  const g = liste.filter((o) => o.kayitta).map((o) => [o, omur(o)]);
  const kapali = g.filter(([, v]) => v !== null).map(([, v]) => v);
  // Acik kalan sapma pencerenin sonuna kadar yasar; toplama oyle girer.
  const acik = g.filter(([, v]) => v === null);
  const acikSure = acik.reduce((t, [o]) => t + (SON - o.an), 0);
  const kapaliSure = kapali.reduce((t, v) => t + v, 0);
  return [g.length, kapali.length, acik.length, Math.round(kapaliSure / kapali.length),
    Math.max(...kapali), kapali.filter((v) => v <= 60).length,
    kapaliSure + acikSure, acikSure / (kapaliSure + acikSure)];
};

const liste = olaylar(filo(50));
console.log(`30 gun, 50 makine, ${liste.length} elle degisiklik; ` +
  `kayit ${KALEM}/${AYAR} ayari kapsiyor\n`);
console.log("model".padEnd(10) + "tetik".padEnd(11) + "gorulen".padEnd(10) +
  "kapanan".padEnd(10) + "acik".padEnd(7) + "ort. omur".padEnd(12) + "en uzun".padEnd(11) +
  "1 saatte".padEnd(11) + "sapma-saati");
for (const [ad, tetik, fn] of [["ajanli", `${GUN / PENCERE}/gun`, ajanli],
  ["ajansiz", `${TETIK.length}/30 gun`, ajansiz]]) {
  const [olay, kap, ac, ort, uzun, saatte, top, pay] = say(liste, fn);
  console.log(ad.padEnd(10) + tetik.padEnd(11) + String(olay).padEnd(10) +
    String(kap).padEnd(10) + String(ac).padEnd(7) + `${ort} dk`.padEnd(12) +
    `${(uzun / 60).toFixed(1)} sa`.padEnd(11) + `${saatte}/${kap}`.padEnd(11) +
    (top / 60).toFixed(0) + ` (%${(100 * pay).toFixed(1)} acik kalandan)`);
}
console.log(`\nkayit disi ayarda olusan sapma: ${liste.filter((o) => !o.kayitta).length}` +
  ` olay - iki modelde de gorunmez`);
console.log(`kayit disi kalan ayar: ${AYAR - KALEM}/${AYAR} (makine basina)`);
```

```
30 gun, 50 makine, 27 elle degisiklik; kayit 34/61 ayari kapsiyor

model     tetik      gorulen   kapanan   acik   ort. omur   en uzun    1 saatte   sapma-saati
ajanli    48/gun     18        15        3      20 dk       0.5 sa     15/15      1251 (%99.6 acik kalandan)
ajansiz   8/30 gun   18        16        2      3873 dk     159.5 sa   0/16       1320 (%21.7 acik kalandan)

kayit disi ayarda olusan sapma: 9 olay - iki modelde de gorunmez
kayit disi kalan ayar: 27/61 (makine basina)
```

Olay başına iki mertebe fark vardır. Ajanlı modelde kapanan bir sapma ortalama **20 dakika** yaşar
ve on beşi de bir saat içinde kapanır. Ajansız modelde ortalama ömür **3873 dakikadır** — iki
buçuk günden fazla — ve on altısının hiçbiri bir saat içinde kapanmaz; en uzun sapma 159,5 saat,
6,6 gün ayakta kalır. Fark, yoklama aralığı ile tetik aralığı arasındaki orandır.

Son sütun bu üstünlüğü geri alır. Otuz günlük toplam sapma süresi ajanlı modelde 1251, ajansız
modelde 1320 saattir; fark yüzde altıdır. Nedeni tabloda yazılıdır: ajanlı modeldeki toplamın
**yüzde 99,6'sı** hiç kapanmayan üç sapmadan gelir. Ajanı durmuş makinede sapma ölçüsüz yaşar,
çünkü bakan yoktur; modelin hızı kendi başarısızlık biçimiyle yenilir. Ajansız modelde açık
kalanın payı yüzde 21,7'dir — orada başarısızlık merkeze görünür ve sonraki tetikte düzelir.

Son iki satır iki modelin de kapsamadığı yeri yazar. Makinede 61 ayar değiştirilebilir, kayıt
bunun 34'ünü tutar; otuz günde oluşan 27 değişikliğin **9'u kayıt dışı ayarlardadır** ve iki
modelde de görünmez. Sapmanın ne kadar hızlı kapandığı araç sorusudur; hangi sapmanın görüldüğü
**kaydın kapsamı** sorusudur ve araç seçimi onu değiştirmez.

#### Modelin Kendi Bedeli

Her iki model de makineye bir şey ekler: ajanlı model bir süreç, ajansız model bir bağlantı
noktası. Bedel ayrıca sayılır.

```js
// olcum-agi/bedel.mjs — modelin kendi bedeli: surec, baglanti yuzeyi, kimlik (model)
import { uretec, filo, KALEM, PENCERE } from "./model.mjs";

const BELLEK = 46e6;   // ajan surecinin makinede tuttugu bellek (kurgu)
const BAYT = 790;      // bir yapilandirma kaleminin kayittaki bayti (kurgu)
const N = filo(50).length;
const cekme = (1440 / PENCERE) * N;        // ajanli: gunluk kayit cekme
const kosum = Math.round((8 / 30) * N);    // ajansiz: gunluk baglanti (30 gunde 8 kosum)
const mb = (b) => (b / 1e6).toFixed(1) + " MB";

const SATIR = [
  ["makinede kosan surec", `${N}`, "0"],
  ["makinede tutulan bellek", `${(N * BELLEK / 1e9).toFixed(2)} GB`, "0.00 GB"],
  ["makinede dinleyen uc", "0", `${N}`],
  ["merkezde dinleyen uc", "1", "0"],
  ["yetkili kimlik", `${N} (tek makineye acik)`, `1 (${N} makineye acik)`],
  ["bir kimlik dusunce", "1 makine", `${N} makine`],
  ["merkeze gunluk baglanti", `${cekme}`, `${kosum}`],
  ["merkezden gunluk trafik", mb(cekme * KALEM * BAYT), mb(kosum * KALEM * BAYT)],
];

// Ajanin kendi surumu de yayilmak zorundadir: gunde bir deneme, basarisizlik ajani oldurebilir.
const yayilim = (tohum = 20260803) => {
  const r = uretec(tohum), d = Array(N).fill("eski"), iz = [];
  for (let g = 1; g <= 14; g++) {
    for (let i = 0; i < N; i++)
      if (d[i] === "eski") d[i] = r() < 0.94 ? "yeni" : r() < 0.35 ? "olu" : "eski";
    if ([1, 3, 7, 14].includes(g))
      iz.push([g, ...["yeni", "eski", "olu"].map((v) => d.filter((x) => x === v).length)]);
  }
  return iz;
};

console.log("kalem".padEnd(28) + "ajanli".padEnd(24) + "ajansiz");
for (const [a, b, c] of SATIR) console.log(a.padEnd(28) + b.padEnd(24) + c);
console.log(`\najanin kendi surumu ${N} makineye yayilirken:`);
console.log("gun".padEnd(7) + "yeni surum".padEnd(13) + "eski surum".padEnd(13) + "olu ajan");
const iz = yayilim();
for (const [g, yeni, eski, olu] of iz)
  console.log(`${g}.`.padEnd(7) + `${yeni}/${N}`.padEnd(13) +
    `${eski}/${N}`.padEnd(13) + `${olu}/${N}`);
console.log(`\nolu ajanli ${iz.at(-1)[3]} makine kendini toparlayamaz: o makineye ancak ` +
  `ajansiz yoldan girilir; yilda 12 surumde ${iz.at(-1)[3] * 12} makineye el deger`);
```

```
kalem                       ajanli                  ajansiz
makinede kosan surec        50                      0
makinede tutulan bellek     2.30 GB                 0.00 GB
makinede dinleyen uc        0                       50
merkezde dinleyen uc        1                       0
yetkili kimlik              50 (tek makineye acik)  1 (50 makineye acik)
bir kimlik dusunce          1 makine                50 makine
merkeze gunluk baglanti     2400                    13
merkezden gunluk trafik     64.5 MB                 0.3 MB

ajanin kendi surumu 50 makineye yayilirken:
gun    yeni surum   eski surum   olu ajan
1.     46/50        3/50         1/50
3.     49/50        0/50         1/50
7.     49/50        0/50         1/50
14.    49/50        0/50         1/50

olu ajanli 1 makine kendini toparlayamaz: o makineye ancak ajansiz yoldan girilir; yilda 12 surumde 12 makineye el deger
```

İlk iki satır doğrudan bedeldir: ajanlı modelde filo, hiçbir ölçüm okuması yapmadan **2,30 GB
bellek** tutar. Üçüncü ve dördüncü satır yönü yazar: ajanlı modelde makinelerde dinleyen uç yoktur,
ajan dışarı bağlanır ve merkezde tek bir uç dinler; ajansız modelde **50 makine** uzaktan erişim
için dinler, merkez hiç dinlemez.

Beşinci ve altıncı satır asıl ayrımdır. Ajanlı modelde her makinenin kendi kimliği vardır ve o
kimlik yalnız kendi makinesini yazabilir; biri düşerse bir makine düşer. Ajansız modelde tek bir
kimlik elli makinenin hepsinde yetkilidir; düşerse **elli makine** birden düşer. Kimlik sayısı
ajanlı modelde elli kat fazladır, ama tek bir kimliğin bedeli elli kat azdır.

Son iki satır merkezin yükünü sayar: ajanlı modelde günde 2400 çekme ve 64,5 MB, ajansız modelde
13 bağlantı ve 0,3 MB. Aradaki 200 kat, sapmanın 20 dakikada kapanmasının fiyatıdır — sürekli
bakmak sürekli sormak demektir.

Alt tablo ajanlı modelin kapanmayan halkasıdır. Ajanın kendisi de yazılımdır, yeni sürümü de
yayılmak zorundadır. On dört günde 49 makine geçer, **1 makinede ajan ölür** ve o makine kendini
toparlayamaz: ajan çalışmadığı için yeni sürümü çekemez. Yılda on iki sürümde 12 makineye el değer
ve ajanlı model, bir kaçış yolu olarak ajansız yolu da tutmayı gerektirir.

#### Özet

- Elli makinede ajansız model 4 turda 20 dakikada, ajanlı model tek turda 33 dakikada yayılır;
  800 makinede sıralama tersine döner — ajansız 63 tur ve 315 dakika, ajanlı hâlâ 33 dakika.
- Tek koşumdan sonra kayıttaki 1700 satırın ajansız modelde 1598'i, ajanlı modelde 1530'u tutar;
  fark ağa kapalı 3 ile ajanı durmuş 5 makineden gelir ve ikincisi **sessizdir**.
- Kapanan bir sapma ajanlı modelde ortalama 20 dakika, ajansız modelde 3873 dakika yaşar; en uzun
  ajansız sapma 6,6 gün ayakta kalır.
- Toplam sapma süresi buna rağmen 1251 saate karşı 1320 saattir: ajanlı modeldeki toplamın yüzde
  99,6'sı hiç kapanmayan üç sapmadan gelir.
- Kaydın kapsamadığı 27/61 ayarda oluşan 9 sapma iki modelde de görünmez; kapanma hızı araç
  sorusu, hangi sapmanın görüldüğü kayıt sorusudur.
- Modelin bedeli: ajanlı modelde 50 süreç, 2,30 GB bellek, günde 2400 çekme ve ölebilen bir ajan;
  ajansız modelde 50 dinleyen uç ve elli makinede birden yetkili tek kimlik.

#### Sonraki Adım

İki model de aynı varsayımı paylaşır: makine önce boş açılır, sonra yapılandırılır. Yayılma
süresinin tamamı bu sıralamadan gelir — 20 dakika da 33 dakika da açılmış bir makineyi kayda
getirmek için geçer. Kısmi başarısızlık da oradan gelir: makine kayda uymadan iş almaya
başlamıştır.

Sıralama tersine çevrilebilir. Ayar açılıştan sonra taşınmak yerine makinenin açıldığı imajın
içine önceden konabilir; makine kayda uygun **doğarsa** yayılma süresi sıfırdır. M22/K02 temel
imajın boyutunu ve güncellenme yükünü ölçmüştü; o ölçüm tekrarlanmaz. Sonraki ders bir katman
üstünü sayar: açılışta yapılandırma ile önceden pişmiş imaj yan yana konduğunda açılış kaç saniye
sürüyor, dışarıya kaç çağrı gidiyor ve bir güvenlik düzeltmesi geldiğinde kaç imaj yeniden
pişiyor?

### Sunucu İmajı Üretimi

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/sunucu-imaji-uretimi

Her açılışta yapılandırma ile önceden pişmiş imaj yan yana ölçülür: açılış süresi, açılışta dışarıya yapılan çağrı sayısı ve bir bağımlılık düştüğünde kaç makinenin açıldığı sayılır; bir güvenlik düzeltmesi geldiğinde kaç imajın yeniden piştiği, yayılma penceresinin kaç gün olduğu ve imajın içerik kaydının kaç bileşeni kaçırdığı ölçülür.

Önceki ders kaydı çalışan bir makineye taşımanın iki yolunu ölçtü. İkisi de aynı sıralamayı
varsaydı: makine önce boş açılır, sonra yapılandırılır. Yayılma süresi de kısmi başarısızlık da bu
sıralamadan geldi — makine iş almaya başlamıştı ve kayda henüz uymuyordu.

Sıralama tersine çevrilebilir: ayarlar makineye açılıştan sonra taşınmak yerine, makinenin açıldığı
imajın içine **önceden konabilir**. Bu ders üç yolu tür olarak yan yana koyar — **her açılışta
yapılandır**, **önceden pişmiş imaj** ve **karma** (pişmiş taban artı açılışta uygulanan küçük
ayar). M22/K02 temel imajın baytını ve güncelleme yükünü ölçmüştü; o ölçüm tekrarlanmaz. Burada
ölçülen imajın boyutu değil, **imaj üretim akışıdır**.

**AD31.** Filo, bölgesel ölçüm ağının — su sayaçlarından okuma toplayan kurgu yazılımın — 6 rolde
ve 3 ortamda koşan 50 makinesidir. **AD32.** Üç yolun açılış adımları ve saniyeleri kurgudur; her
adımın hangi bağımlılığa kaç çağrı yaptığı yazılıdır. **AD33.** Bağımlılıkların açılış anında
ayakta olma olasılıkları 0,995 ile 0,999 arasındadır ve ani talep artışında aynı anda 40 makine
açılır. **AD34.** Sayılar kendi üretecimizden gelir; tohumlar `20260811`–`20260814` görünürdür ve
büyüklük mertebesidir. **AD35.** Bir imaj 7–19 dakikada pişer, üstüne 6 dakika sınama biner; imaj
üretici aynı anda 3 imaj pişirir, filo günde 12 makine değiştirir. **AD36.** İmajın içerik kaydı
yalnız doğrudan bileşenleri yazar, dolaylı gelenleri yazmaz; tam yeniden pişirme döngüsü 30 gündür.

#### Açılışta Ne Oluyor

Üç yol aynı servisi açar. Her açılışta yapılandıran yol paketleri açılış anında indirir ve kurar; pişmiş imaj yolu bunları imajın içinde hazır bulur; karma yol imajı hazır bulur
ama ortama göre değişen ayarı açılışta çeker.

```js
// olcum-agi/acilis.mjs — ayni servisin uc acilis yolu (model; urun degil tur)
export const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;

// Dis bagimlilik turleri ve her birinin acilis aninda ayakta olma olasiligi (kurgu).
export const AYAKTA = {
  "ad cozumleyici": 0.999, "paket deposu": 0.995, "yapilandirma sunucusu": 0.995,
  "sir deposu": 0.998, "imaj deposu": 0.997,
};

// Adim: [ad, saniye, bagimlilik ya da null, o adimda yapilan cagri sayisi].
export const YOL = {
  "her acilista": [
    ["cekirdek acilisi", 12, null, 0], ["ad cozumleme", 2, "ad cozumleyici", 1],
    ["paket listesi", 9, "paket deposu", 1], ["14 paket kurulumu", 128, "paket deposu", 14],
    ["yapilandirma cekme", 11, "yapilandirma sunucusu", 1], ["sir cekme", 6, "sir deposu", 1],
    ["servis baslatma", 8, null, 0],
  ],
  "pismis imaj": [
    ["imaj cekme", 19, "imaj deposu", 1], ["cekirdek acilisi", 12, null, 0],
    ["sir cekme", 6, "sir deposu", 1], ["servis baslatma", 8, null, 0],
  ],
  "karma": [
    ["imaj cekme", 19, "imaj deposu", 1], ["cekirdek acilisi", 12, null, 0],
    ["ortam ayari cekme", 7, "yapilandirma sunucusu", 1], ["sir cekme", 6, "sir deposu", 1],
    ["servis baslatma", 8, null, 0],
  ],
};

export const olc = (ad) => {
  const adim = YOL[ad];
  const cagri = adim.reduce((t, [, , , c]) => t + c, 0);
  const basari = adim.reduce((t, [, , b, c]) => t * (b ? AYAKTA[b] ** c : 1), 1);
  const bagli = [...new Set(adim.filter(([, , b]) => b).map(([, , b]) => b))];
  return { ad, sure: adim.reduce((t, [, s]) => t + s, 0), cagri, basari, bagli };
};

if (import.meta.url === `file://${process.argv[1]}`) {
  const YENI = 40;   // ani talep artisinda ayni anda acilan makine (kurgu)
  console.log("yol".padEnd(14) + "acilis".padEnd(9) + "dis cagri".padEnd(11) +
    "bagimlilik".padEnd(12) + "acilis basarisi".padEnd(17) + `${YENI} makinede acilmayan`);
  for (const ad of Object.keys(YOL)) {
    const o = olc(ad);
    console.log(o.ad.padEnd(14) + `${o.sure} sn`.padEnd(9) + String(o.cagri).padEnd(11) +
      `${o.bagli.length} tur`.padEnd(12) + `%${(100 * o.basari).toFixed(1)}`.padEnd(17) +
      (YENI * (1 - o.basari)).toFixed(1));
  }
  console.log("\nbir bagimlilik dustugunde acilan makine (40 makinede):");
  const sut = Object.keys(YOL);
  console.log("dusen".padEnd(24) + sut.map((a) => a.padEnd(14)).join("").trimEnd());
  for (const b of Object.keys(AYAKTA))
    console.log((b.padEnd(24) + sut.map((ad) =>
      (olc(ad).bagli.includes(b) ? "0/40" : "40/40").padEnd(14)).join("")).trimEnd());
}
```

```
yol           acilis   dis cagri  bagimlilik  acilis basarisi  40 makinede acilmayan
her acilista  176 sn   18         4 tur       %92.0            3.2
pismis imaj   45 sn    2          2 tur       %99.5            0.2
karma         52 sn    3          3 tur       %99.0            0.4

bir bagimlilik dustugunde acilan makine (40 makinede):
dusen                   her acilista  pismis imaj   karma
ad cozumleyici          0/40          40/40         40/40
paket deposu            0/40          40/40         40/40
yapilandirma sunucusu   0/40          40/40         0/40
sir deposu              0/40          0/40          0/40
imaj deposu             40/40         0/40          0/40
```

Açılış süresi 176 saniyeden 45 saniyeye iner — yaklaşık dört kat. Ama asıl fark ikinci sütundadır:
**18 dış çağrı 2'ye** düşer. Bu, süreden bağımsız bir niceliktir; her çağrı ayrı bir başarısızlık
noktasıdır ve talep artışında hepsi aynı anda yapılır. Kırk makinelik bir açılışta beklenen
açılmayan makine sayısı 3,2'den 0,2'ye iner; pişmiş imaj yolu açılışı hızlandırmaktan çok
**açılışı bağımsızlaştırır**.

Alt tablo bunun sınırını yazar. Sır deposu satırında üç yol da sıfırdır: hassas değer imaja
pişirilemez — pişirilirse imajı okuyabilen herkes sırrı da okur. Pişmiş yol on sekiz çağrının on
yedisini kaldırır, on sekizincisini kaldıramaz. İmaj deposu satırı takasın öteki yüzüdür: pişmiş
yol dört bağımlılığı ikiye indirirken **yeni bir bağımlılık** edinir. Her açılışta yapılandıran
yolun tek üstünlüğü budur; imaj deposu düşse de açılmaya devam eder.

Kayıt ile gerçek arasındaki fark burada da vardır. Her açılışta yapılandıran yolda kayıt "şu
paketler kurulacak" der; gerçekte kurulan, paket deposunun **o gün** sunduğudur ve iki farklı gün
açılan iki makine aynı içeriği almaz. Pişmiş yolda kayıt bir kez gerçeğe çevrilir ve imaja donar.

#### Düzeltme Geldiğinde Kaç İmaj Pişiyor

Donmuş içeriğin bedeli, içeriği değiştirmenin artık bir üretim adımı gerektirmesidir. Güvenlik
düzeltmesi geldiğinde her açılışta yapılandıran yol hiçbir şey pişirmez; pişmiş yol etkilenen bütün
imajları yeniden pişirir. Kaç imajın piştiği, ortamın nereye yazıldığına bağlıdır.

```js
// olcum-agi/pisirme.mjs — bir guvenlik duzeltmesi geldiginde kac imaj yeniden pisiyor (model)
import { uretec } from "./acilis.mjs";

export const ROL = 6, ORTAM = 3;    // olcum aginin alti rolu, uc ortami (kurgu)
const KOL = 3, SINAMA = 6;          // es zamanli pisirme kolu; imaj basina sinama (dk)
const MAKINE = 50, DEGISIM = 12;    // filo ve gunluk makine degistirme kapasitesi

// Saf pismis yolda her rol her ortam icin ayri pisirilir; karma yolda ortam ayari
// acilista uygulandigi icin rol basina tek imaj yeter.
export const IMAJ = { "her acilista": 0, "pismis imaj": ROL * ORTAM, "karma": ROL };

// Duzeltme, bileseni tasiyan rollere dokunur; taban bileseni butun rollerdedir.
const duzeltmeler = (tohum = 20260811) => {
  const r = uretec(tohum);
  return ["cekirdek kutuphane", "sifreleme kutuphanesi", "ad cozumleyici eklentisi",
    "kabuk", "sikistirma kutuphanesi"].map((ad) =>
      ({ ad, rol: r() < 0.4 ? ROL : 1 + Math.floor(r() * (ROL - 1)) }));
};

// Pencere = pisirme duvar saati + filonun yenilenmesi (gunde DEGISIM makine).
export const pencere = (n, tohum = 20260812) => {
  const r = uretec(tohum);
  const tek = Array.from({ length: n }, () => 7 + Math.round(r() * 12) + SINAMA);
  let duvar = 0;
  for (let i = 0; i < n; i += KOL) duvar += Math.max(...tek.slice(i, i + KOL));
  return { duvar, toplam: tek.reduce((t, v) => t + v, 0), tur: Math.ceil(n / KOL),
    gun: duvar / 1440 + MAKINE / DEGISIM };
};

if (import.meta.url === `file://${process.argv[1]}`) {
  console.log(`${ROL} rol, ${ORTAM} ortam, ${MAKINE} makine; ` +
    `imaj sayisi: saf ${IMAJ["pismis imaj"]}, karma ${IMAJ["karma"]}\n`);
  console.log("duzeltme".padEnd(26) + "dokundugu rol".padEnd(15) +
    "saf pismis".padEnd(12) + "karma".padEnd(8) + "her acilista");
  let saf = 0, kar = 0;
  for (const f of duzeltmeler()) {
    saf += f.rol * ORTAM; kar += f.rol;
    console.log(f.ad.padEnd(26) + `${f.rol}/${ROL}`.padEnd(15) +
      `${f.rol * ORTAM} imaj`.padEnd(12) + `${f.rol} imaj`.padEnd(8) + "0 imaj");
  }
  console.log("toplam".padEnd(41) + `${saf} imaj`.padEnd(12) + `${kar} imaj`.padEnd(8) + "0 imaj");

  console.log("\nen genis duzeltme (butun roller) icin yayilma penceresi:");
  console.log("yol".padEnd(14) + "pisen imaj".padEnd(12) + "tur".padEnd(6) +
    "duvar saati".padEnd(13) + "pisirici dakika".padEnd(17) + "pencere");
  for (const [ad, n] of Object.entries(IMAJ)) {
    const p = pencere(n);
    console.log(ad.padEnd(14) + String(n).padEnd(12) + String(p.tur).padEnd(6) +
      `${(p.duvar / 60).toFixed(1)} sa`.padEnd(13) + String(p.toplam).padEnd(17) +
      `${p.gun.toFixed(2)} gun`);
  }
}
```

```
6 rol, 3 ortam, 50 makine; imaj sayisi: saf 18, karma 6

duzeltme                  dokundugu rol  saf pismis  karma   her acilista
cekirdek kutuphane        6/6            18 imaj     6 imaj  0 imaj
sifreleme kutuphanesi     3/6            9 imaj      3 imaj  0 imaj
ad cozumleyici eklentisi  4/6            12 imaj     4 imaj  0 imaj
kabuk                     6/6            18 imaj     6 imaj  0 imaj
sikistirma kutuphanesi    6/6            18 imaj     6 imaj  0 imaj
toplam                                   75 imaj     25 imaj 0 imaj

en genis duzeltme (butun roller) icin yayilma penceresi:
yol           pisen imaj  tur   duvar saati  pisirici dakika  pencere
her acilista  0           0     0.0 sa       0                4.17 gun
pismis imaj   18          6     2.1 sa       334              4.26 gun
karma         6           2     0.7 sa       117              4.20 gun
```

İlk tablo ortamın nereye yazıldığının bedelini gösterir. Ortam imaja pişirilirse her rol üç kez
pişer: beş düzeltme **75 imaj pişirmesi** üretir. Ortam açılışta uygulanırsa aynı beş düzeltme
**25 pişirme** ister. Aradaki üç kat, karma yolun açılışta ödediği tek ek çağrının karşılığıdır.

İkinci tablo yaygın bir beklentiyi düzeltir. Önceden pişmiş imajın tazeliği geciktirdiği
düşünülür; sayılınca gecikme **0,09 gündür** — 4,26 güne karşı 4,17 gün. Pişirme duvar saati 2,1
saattir ve pencerenin yaklaşık yüzde ikisini tutar. Pencereyi belirleyen pişirme değil, **filonun
yenilenmesi**: 50 makine günde 12 değiştirilebildiği için düzeltme dört günden önce her makineye
ulaşamaz ve bu, üç yolun ortak bedelidir. Değişmez altyapı disiplini makineyi yerinde yamamak
yerine değiştirmeyi gerektirir; pencere o kuralın fiyatıdır.

Pişirici dakika sütunu yinelenen bedeldir: aynı düzeltme saf pişmiş yolda 334, karma yolda 117
pişirici dakikası harcar. İmaj üretici de bakılması gereken bir altyapı parçasıdır; kapasitesi
düşerse pencere doğrudan uzar.

#### İmajın İçinde Ne Var

Yeniden pişecek imajlara bir kayıt karar verir: imajın içerik kaydı, hangi imajın hangi bileşeni
taşıdığını yazar. Bu kayıt eksikse yeniden pişirme kümesi de eksik olur ve eksikliği kimse görmez,
çünkü pişirme başarıyla biter.

```js
// olcum-agi/tazelik.mjs — imajin icerik kaydi ile gercek icerigi arasindaki fark (model)
import { uretec } from "./acilis.mjs";
import { ROL, ORTAM } from "./pisirme.mjs";

const BILESEN = ["cekirdek kutuphane", "sifreleme kutuphanesi", "sikistirma kutuphanesi",
  "ad cozumleyici eklentisi", "sertifika deposu", "saat dilimi verisi", "kabuk",
  "yerellestirme verisi", "matematik kutuphanesi", "surum uyumluluk katmani",
  "kayit kutuphanesi", "metin ayristirici", "ag istemcisi", "zamanlayici"];
const DONGU = 30;                          // tam yeniden pisirme dongusu (gun)
const ROL_MAKINE = [5, 6, 7, 9, 11, 12];   // rol basina makine; toplam 50

// Her rol imaji: kayitta yazan dogrudan bilesenler + kayda girmeyen dolayli bilesenler.
const katalog = (tohum = 20260813) => {
  const r = uretec(tohum);
  return Array.from({ length: ROL }, (_, i) => {
    const kayitli = BILESEN.filter(() => r() < 0.4);
    const dolayli = BILESEN.filter((b) => !kayitli.includes(b) && r() < 0.35);
    return { kayitli, gercek: [...kayitli, ...dolayli], makine: ROL_MAKINE[i] };
  });
};

// Bes ayri guvenlik duzeltmesi: bilesenler yerine konmadan secilir, gun uretecten gelir.
const duzeltme = (tohum = 20260814) => {
  const r = uretec(tohum), havuz = [...BILESEN];
  return Array.from({ length: 5 }, () => ({
    bilesen: havuz.splice(Math.floor(r() * havuz.length), 1)[0],
    gun: 1 + Math.floor(r() * DONGU),
  }));
};

const k = katalog(), d = duzeltme();
const kayitli = k.reduce((t, i) => t + i.kayitli.length, 0);
const gercek = k.reduce((t, i) => t + i.gercek.length, 0);
console.log(`${ROL} rol imaji, ${BILESEN.length} bilesen turu; icerik kaydi ${kayitli} girdi ` +
  `yaziyor, imajlarda ${gercek} girdi var: ${gercek - kayitli} girdi kayit disi ` +
  `(%${(100 * (gercek - kayitli) / gercek).toFixed(0)})\n`);
console.log("duzeltme".padEnd(26) + "kayitta".padEnd(10) + "gercekte".padEnd(10) +
  "kacirilan".padEnd(11) + "yamasiz makine".padEnd(16) + "makine-gunu");
let toplamGun = 0, toplamKacan = 0;
for (const f of d) {
  const kayitta = k.filter((i) => i.kayitli.includes(f.bilesen));
  const gercekte = k.filter((i) => i.gercek.includes(f.bilesen));
  const kacan = gercekte.filter((i) => !kayitta.includes(i));
  const makine = kacan.reduce((t, i) => t + i.makine, 0);
  const gun = makine * (DONGU - f.gun);
  toplamGun += gun; toplamKacan += kacan.length;
  console.log(f.bilesen.padEnd(26) + `${kayitta.length} imaj`.padEnd(10) +
    `${gercekte.length} imaj`.padEnd(10) + `${kacan.length} imaj`.padEnd(11) +
    `${makine}/50`.padEnd(16) + String(gun));
}
console.log(`\nbes duzeltmede kacirilan imaj: ${toplamKacan} (saf pismis yolda ` +
  `${toplamKacan * ORTAM}); yamasiz makine-gunu: ${toplamGun}`);
console.log(`kacirilan imajlar ancak ${DONGU} gunluk tam yeniden pisirmede kapaniyor`);
```

```
6 rol imaji, 14 bilesen turu; icerik kaydi 32 girdi yaziyor, imajlarda 52 girdi var: 20 girdi kayit disi (%38)

duzeltme                  kayitta   gercekte  kacirilan  yamasiz makine  makine-gunu
matematik kutuphanesi     6 imaj    6 imaj    0 imaj     0/50            0
kabuk                     3 imaj    5 imaj    2 imaj     23/50           322
sertifika deposu          2 imaj    2 imaj    0 imaj     0/50            0
sifreleme kutuphanesi     1 imaj    3 imaj    2 imaj     14/50           126
saat dilimi verisi        0 imaj    0 imaj    0 imaj     0/50            0

bes duzeltmede kacirilan imaj: 4 (saf pismis yolda 12); yamasiz makine-gunu: 448
kacirilan imajlar ancak 30 gunluk tam yeniden pisirmede kapaniyor
```

Başlık satırı bu dersin sayısıdır. İçerik kaydı altı imaj için 32 bileşen girdisi yazar; imajların
içinde 52 girdi vardır. **20 girdi, yüzde 38'i kayıt dışıdır** — başka bir bileşenle birlikte gelen
bileşenler. Kayıt yanlış değildir; eksiktir.

Eksikliğin bedeli üçüncü ve dördüncü sütunda görünür. `kabuk` düzeltmesinde kayıt 3 imajın
etkilendiğini söyler, gerçekte 5 imaj etkilenmiştir; **2 imaj yeniden pişirilmez** ve o imajlardan
açılan 23 makine yamasız kalır. `sifreleme kutuphanesi` düzeltmesinde kayıt 1 imaj der, gerçek
3'tür. Beş düzeltmede toplam 4 imaj kaçırılır ve **448 yamasız makine-günü** birikir. Bu makineler
bir sonraki 30 günlük tam yeniden pişirmeye kadar açıkta durur, çünkü onları kimse aramamaktadır.

Son satırın ortam boyutu da vardır: aynı eksiklik saf pişmiş yolda 4 değil **12 imaj** kaçırır.
Ortamı imaja pişirmek yalnız pişirme yükünü üçe katlamaz, kaydın her eksikliğini de üçe katlar.

Bu, üç yolun ölçüsünü tamamlar. Her açılışta yapılandıran yolda yeniden pişirme kümesi diye bir
şey olmadığı için yanlış da hesaplanamaz; düzeltme paket deposuna girdiği anda her makinenin bir
sonraki açılışında gelir. Pişmiş imaj açılışı bağımsızlaştırır ve içeriği dondurur —
karşılığında **envanteri bir kayda çevirir** ve o kaydın eksikliği sessiz bir güvenlik açığıdır.
Kaydın kapsamadığı 20 girdi, bu kursun baştan beri saydığı şeydir.

#### Özet

- Açılış süresi 176 saniyeden 45 saniyeye, dış çağrı sayısı 18'den 2'ye iner; 40 makinelik bir
  açılışta beklenen açılmayan makine 3,2'den 0,2'ye düşer.
- Hiçbir yol sır deposu çağrısından kurtulamaz — hassas değer imaja pişirilemez; pişmiş yol
  bağımlılık sayısını dörtten ikiye indirirken imaj deposunu yeni bir bağımlılık olarak edinir.
- Ortam imaja pişirilirse beş düzeltme 75 imaj pişirmesi, açılışta uygulanırsa 25 pişirme ister;
  pişirici dakika 334'e karşı 117'dir.
- Yayılma penceresi 4,26 güne karşı 4,17 gündür: pişirmenin katkısı 0,09 gün, pencereyi belirleyen
  filonun günde 12 makine değişebilmesidir.
- İçerik kaydı 32 girdi yazar, imajlarda 52 girdi vardır; yüzde 38'lik eksiklik beş düzeltmede
  4 imajı (saf pişmiş yolda 12) yeniden pişirme kümesinin dışında bırakır ve 448 yamasız
  makine-günü biriktirir.
- Pişmiş imaj sapmayı açılıştan kaldırır ama envanteri bir kayda çevirir; o kaydın eksikliği
  sessizdir, çünkü pişirme başarıyla biter.

#### Sonraki Adım

Bu dersin ölçtüğü akış artık bir yapı adımıdır: imaj derlenir, sınanır, sürümlenir ve ortamlara
çıkar. Aynı şey altyapı kaydı için de geçerlidir — plan üretilir, statik denetimden ve politika
taramasından geçer, fiyatlanır, uygulanır. Bu konunun dersleri bu adımları ayrı ayrı saydı, ama
hepsi elle çalıştırıldı.

M22/K04 uygulama değişikliğini kapılardan geçirmiş, kapının neyi yakalayıp neyi kaçırdığını
ölçmüştü; o ölçü tekrarlanmaz, uygulanır. Sonraki ders altyapı değişikliğini aynı hattan geçirir
ve sorar: bu kursta sayılan denetimler bir hatta dizildiğinde kaç değişiklik hangi kapıda durur,
kaç değişiklik bütün kapılardan geçip yine de sapma üretir ve hattın kendisi kaç dakika ekler?

### Hat ile Tümleştirme

Source: https://academia.sh/tr/kurslar/kod-olarak-altyapi/hat-ile-tumlestirme

Altyapı değişikliği bir hattan geçirilir: plan aşaması ile uygulama aşaması ayrılır, arada onay durur. Onaycının okumadan onayladığı plan, plan ile uygulama arasında bayatlayan gerçek, hattın uygulama yetkisinin genişliği ve elle yapılan bir değişikliğin hattın sonraki koşumunda sessizce geri alınması, çakışması ya da hiç görülmemesi sayılır.

Önceki ders imajı pişirdi, ondan önceki ders ayarı uyguladı. İkisi de bir şeyi sormadı: bunları
kim tetikliyor ve kim onaylıyor. Bu kursun bütün ölçümleri elle çalıştırıldı ve elle çalıştırılan
bir akışta plan ile uygulama arasında bir insan durup ikisini art arda görür. Hat ikisini ayırır:
plan bir aşamada üretilir, saklanır, onaya sunulur; uygulama başka bir anda koşar. M22/K04 kapının
ne yakalayıp ne kaçırdığını ölçmüştü; o ölçü tekrarlanmaz, uygulanır.

**AD37.** Hat, bölgesel ölçüm ağının — su sayaçlarından okuma toplayan kurgu yazılımın — altyapı
kaydını iki aşamada koşturur, 30 günde 18 değişiklik geçirir. **AD38.** Planda bir kalem 3 satır
tutar, dakikada 12 satır okunur, 40 satırı geçen planı onaycı okumaz. **AD39.** Tohumlar
`20260901`–`20260903` görünürdür ve sayılar büyüklük mertebesidir.
**AD40.** Sağlayıcı 9 kaynak tipi ve 4 işlem sunar, ortamda 84 kurgu kaynak vardır, dar yetki son
12 koşumdan türetilir. **AD41.** 30 günde 26 elle değişiklik olur; dokunulan kalemin kayıt
kapsamında olma oranı 0,62, kapsamdakinin korumalı olma oranı 0,24, acil düzeltme oranı 0,35'tir.
**AD42.** Tazelik kapısı uygulama anında planı yeniden üretip onaylananla karşılaştırır ve
değişiklik başına 0,5 dakika ekler.

#### Plan, Onay, Uygulama

Onay bir imzadır; ölçülebilir olan, arkasında ne kadar okuma olduğu ve imza ile uygulama arasında
ne kadar süre geçtiğidir — plan bir anın fotoğrafıdır ve bayatlar.

```js
// olcum-agi/hat.mjs — plan asamasi, onay, uygulama asamasi (model; urun degil tur)
export const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;

// kalem basina plan satiri; dakikada okunan satir; onaycinin okumayi biraktigi satir;
// tazelik kapisinin degisiklik basina ekledigi dakika.
const SATIR = 3, HIZ = 12, ESIK = 40, YENIDEN = 0.5;

// 30 gunde hattan gecen 18 altyapi degisikligi; nicelikler ureteçten gelir.
export const degisiklikler = (tohum = 20260901) => {
  const r = uretec(tohum);
  return Array.from({ length: 18 }, () => {
    const kalem = 1 + Math.floor(r() * 14);
    return { kalem, satir: kalem * SATIR + 6, bekleme: Math.round(r() * 470) / 10,
      degisti: r() < 0.3, sapan: 1 + Math.floor(r() * 3) };
  });
};

// kapi=true: uygulama aninda plan yeniden uretilip onaylananla karsilastiriliyor.
export const kosum = (liste, kapi) => {
  const okunmayan = liste.filter((d) => d.satir > ESIK);
  const bayat = liste.filter((d) => d.degisti);
  const sapan = bayat.reduce((t, d) => t + d.sapan, 0);
  return {
    korsan: okunmayan.length, korsanKalem: okunmayan.reduce((t, d) => t + d.kalem, 0),
    dakika: liste.reduce((t, d) =>
      t + (d.satir <= ESIK ? d.satir / HIZ : 0.5) + (kapi ? YENIDEN : 0), 0),
    bayat: bayat.length, korumasiz: kapi ? 0 : sapan, sapan,
    duran: kapi ? bayat.length : 0, uygulanan: 18 - (kapi ? bayat.length : 0),
  };
};

if (import.meta.url === `file://${process.argv[1]}`) {
  const liste = degisiklikler(), top = (a) => liste.reduce((t, d) => t + a(d), 0);
  console.log(`18 degisiklik, ${top((d) => d.kalem)} kalem, ${top((d) => d.satir)} plan satiri; ` +
    `plan ile uygulama arasi ortalama ${(top((d) => d.bekleme) / 18).toFixed(1)} saat\n`);
  console.log("tazelik kapisi".padEnd(16) + "onaylanan".padEnd(11) + "okunmadan".padEnd(11) +
    "okunmayan kalem".padEnd(17) + "gozden gecirme".padEnd(16) + "bayat plan".padEnd(12) +
    "korumasiz kalem".padEnd(17) + "uygulanan");
  for (const kapi of [false, true]) {
    const s = kosum(liste, kapi);
    console.log((kapi ? "var" : "yok").padEnd(16) + "18/18".padEnd(11) +
      `${s.korsan}/18`.padEnd(11) + `${s.korsanKalem}/${top((d) => d.kalem)}`.padEnd(17) +
      `${s.dakika.toFixed(1)} dk`.padEnd(16) + `${s.bayat}/18`.padEnd(12) +
      `${s.korumasiz}/${s.sapan}`.padEnd(17) + `${s.uygulanan}/18`);
  }
  const es = kosum(liste, false);
  console.log(`\nplan ${ESIK} satiri gectiginde onayci okumuyor: ${es.korsan} degisiklik, ` +
    `${es.korsanKalem} kalem denetimsiz onaylandi`);
}
```

```
18 degisiklik, 129 kalem, 495 plan satiri; plan ile uygulama arasi ortalama 28.6 saat

tazelik kapisi  onaylanan  okunmadan  okunmayan kalem  gozden gecirme  bayat plan  korumasiz kalem  uygulanan
yok             18/18      4/18       49/129           29.0 dk         3/18        5/5              18/18
var             18/18      4/18       49/129           38.0 dk         3/18        0/5              15/18

plan 40 satiri gectiginde onayci okumuyor: 4 degisiklik, 49 kalem denetimsiz onaylandi
```

On sekiz değişikliğin on sekizi onaydan geçti; onay oranı hiçbir şey söylemez. Söyleyen sayı
ikinci sütundadır: **4 plan okunmadan onaylandı** ve o dört plan 129 kalemin 49'unu taşıyordu —
kalemlerin yüzde otuz sekizi bakılmadan uygulandı. Bu bir dikkat sorunu değil ölçek sorunudur;
gözden geçirme yükü `bildirimsel-saglama/07`'de sayıldığı gibi doğrusal artar.

İkinci ölçü plan ile uygulama arasındaki süredir: ortalama **28,6 saat** ve bu sürede gerçek
durmaz. On sekiz planın **3'ü uygulandığında bayattı**, onaylanan planda yazmayan 5 kalem
uygulamaya girdi; onaylanan belge ile uygulanan iş aynı değildi.

**Bu kursun kapılarından biri yalnız burada işe yarar.** Biçim denetimi, politika taraması,
altyapı testi ve maliyet tahmini hattın dışında da koşabilir; tazelik kapısı koşamaz. Uygulama
anında planı yeniden üretip **onaylanan planla** karşılaştırmak, onaylanan planın saklanmış bir
belge olmasını gerektirir; elle koşan akışta arada saklanacak bir şey yoktur. Kapı 9 dakika ekliyor,
3 uygulamayı durduruyor ve 5 kalemi kurtarıyor.

#### Hattın Yetkisi

Hat artık uygulamayı kendisi koşuyor ve bunun için altyapı üzerinde bir kimliği var. Yetki genişse
hat her şeyi yapar; o kimlik düşerse her şey yapılabilir hâle gelir.

```js
// olcum-agi/yetki.mjs — hattin altyapi uzerindeki uygulama yetkisi (model)
import { uretec, degisiklikler } from "./hat.mjs";

const TIP = ["makine", "disk", "ag", "alt ag", "guvenlik kurali", "kuyruk", "veri deposu",
  "nesne deposu", "kimlik"];
const ISLEM = ["yarat", "oku", "guncelle", "sil"];
const KAYNAK = [14, 22, 3, 6, 19, 2, 4, 3, 11];   // tip basina kurgu kaynak; toplam 84
const PENCERE = 12;   // yetkinin turetildigi gecmis kosum

// Her degisiklik birkac (tip, islem) ciftine dokunur; cift kumesi ureteçten gelir.
const dokunus = (tohum = 20260902) => {
  const r = uretec(tohum);
  return degisiklikler().map(() => {
    const c = new Set(), n = 1 + Math.floor(r() * 3);
    for (let i = 0; i < n; i++)
      c.add(`${TIP[Math.floor(r() * 9)]}/${ISLEM[Math.floor(r() * 4)]}`);
    return [...c];
  });
};

const d = dokunus();
const hepsi = TIP.flatMap((t) => ISLEM.map((i) => `${t}/${i}`));
const dar = [...new Set(d.slice(0, PENCERE).flat())];
const silmesiz = dar.filter((c) => !c.endsWith("/sil"));
const silinebilir = (y) => TIP.reduce((t, x, i) => t + (y.includes(`${x}/sil`) ? KAYNAK[i] : 0), 0);

console.log(`${TIP.length} kaynak tipi x ${ISLEM.length} islem = ${hepsi.length} islem; ` +
  `${KAYNAK.reduce((a, b) => a + b)} kurgu kaynak\n`);
console.log("yetki".padEnd(22) + "islem".padEnd(9) + "hattan gecen".padEnd(14) +
  "elde kalan".padEnd(12) + "silinebilir kaynak");
for (const [ad, y] of [["genis (hepsi)", hepsi], [`dar (${PENCERE} kosumdan)`, dar],
  ["dar, silmesiz", silmesiz]]) {
  const gecen = d.filter((c) => c.every((x) => y.includes(x))).length;
  console.log(ad.padEnd(22) + `${y.length}/36`.padEnd(9) + `${gecen}/18`.padEnd(14) +
    `${18 - gecen}/18`.padEnd(12) + `${silinebilir(y)}/84`);
}
const yeni = [...new Set(d.slice(PENCERE).flat())].filter((c) => !dar.includes(c));
console.log(`\ndar yetki gecmise bakarak turetildi; sonraki 6 kosumda ${yeni.length} yeni ` +
  `islem cifti cikti: ${yeni.join(", ")}`);
```

```
9 kaynak tipi x 4 islem = 36 islem; 84 kurgu kaynak

yetki                 islem    hattan gecen  elde kalan  silinebilir kaynak
genis (hepsi)         36/36    18/18         0/18        84/84
dar (12 kosumdan)     19/36    14/18         4/18        14/84
dar, silmesiz         18/36    13/18         5/18        0/84

dar yetki gecmise bakarak turetildi; sonraki 6 kosumda 5 yeni islem cifti cikti: alt ag/sil, kuyruk/guncelle, guvenlik kurali/sil, alt ag/yarat, disk/yarat
```

Geniş yetki tabloyu tek satırda bitirir: 36 işlemin 36'sı, 18 değişikliğin 18'i, elde kalan sıfır.
Son sütun karşılığıdır — hattın kimliği ele geçerse **84 kaynağın 84'ü** silinebilir; hat, bu
kursta yazılan hiçbir kaydın vermediği kadar geniş bir erişimdir.

Yetki geçmiş on iki koşuma bakılarak daraltıldığında işlem sayısı **19'a**, silinebilir kaynak
**14'e** iner; bedeli 18 değişikliğin **4'ünün elde kalmasıdır**. Silme yetkisi de alınınca
silinebilir kaynak sıfırlanır, elde kalan 5'e çıkar. Alt satır daraltmanın neden kalıcı olmadığını
yazar: sonraki altı koşumda hiç görülmemiş **5 yeni işlem çifti** çıktı. Geçmişten türetilen yetki
geleceğin işini elde bırakır ve elde kalanda ilk derste sayılan elle yol aynen sürer.

#### Elle Değişiklik Hatta Çarpınca

Hat kurulduktan sonra da makinelere elle dokunulur; soru, o dokunuşa hattın sonraki koşumunda ne
olduğudur.

```js
// olcum-agi/carpisma.mjs — elle degisiklik hattin bir sonraki kosumunda ne oluyor (model)
import { uretec } from "./hat.mjs";

const KOSUM = [1.4, 3.1, 4.0, 6.8, 8.2, 9.9, 12.5, 14.1, 15.0, 17.7,
  19.2, 21.4, 22.8, 24.3, 25.1, 27.6, 28.9, 29.5];   // 18 hat kosumunun gunu (kurgu)
const KAPSAM = 0.62;    // elle dokunulan kalemin kayit kapsaminda olma orani
const KORUMA = 0.24;    // kapsamdaki kalemin korumali/yeniden olusturan olma orani

// 30 gunde elle yapilan 26 degisiklik; her biri tek bir kaleme dokunuyor.
const elle = (tohum = 20260903) => {
  const r = uretec(tohum);
  return Array.from({ length: 26 }, () => {
    const gun = Math.round(r() * 300) / 10;
    const kapsamda = r() < KAPSAM;
    return { gun, kapsamda, korumali: kapsamda && r() < KORUMA, acil: r() < 0.35 };
  });
};

const SONUC = { "sessizce geri alinan": [], "catisan (hat duruyor)": [], "fark edilmeyen": [] };
for (const o of elle()) {
  const sonraki = KOSUM.find((g) => g > o.gun);
  const ad = !o.kapsamda ? "fark edilmeyen"
    : o.korumali ? "catisan (hat duruyor)" : "sessizce geri alinan";
  // Kayit disi kalem hicbir kosumda gorulmez; gecikmesi de yoktur.
  const gecikme = !o.kapsamda || sonraki === undefined ? null : sonraki - o.gun;
  SONUC[ad].push({ ...o, gecikme });
}

console.log("26 elle degisiklik, 30 gun, 18 hat kosumu\n");
console.log("sonuc".padEnd(24) + "adet".padEnd(9) + "acil".padEnd(8) + "gecikme");
for (const [ad, liste] of Object.entries(SONUC)) {
  const g = liste.map((o) => o.gecikme).filter((v) => v !== null);
  console.log(ad.padEnd(24) + `${liste.length}/26`.padEnd(9) +
    String(liste.filter((o) => o.acil).length).padEnd(8) +
    (g.length ? `${(g.reduce((a, b) => a + b, 0) / g.length).toFixed(1)} gun` : "-"));
}
const geri = SONUC["sessizce geri alinan"];
console.log(`\ngeri alinan ${geri.length} degisiklik hata uretmedi, hepsi planda birer satirdi; ` +
  `${geri.filter((o) => o.acil).length}'i acil duzeltmeydi`);
```

```
26 elle degisiklik, 30 gun, 18 hat kosumu

sonuc                   adet     acil    gecikme
sessizce geri alinan    15/26    4       1.2 gun
catisan (hat duruyor)   3/26     1       0.7 gun
fark edilmeyen          8/26     2       -

geri alinan 15 degisiklik hata uretmedi, hepsi planda birer satirdi; 4'i acil duzeltmeydi
```

Üç satır üç ayrı olaydır ve yalnız ortadaki görünür. **3 çatışmada** hat durur ve bir insan karar
verir; bunlar hattın çalıştığının kanıtı sayılır. **15 değişiklik sessizce geri alınır** ve
ortalama 1,2 gün sonra kaybolur; hiçbiri hata üretmez, çünkü hat bunu yapmak için kuruldu. O on
beşin **4'ü acil düzeltmeydi** — kayıtları güncellenmediği için kaydın istediği eski değere
döndüler. `iac-temelleri/05` geri almanın canlı düzeltmeyi bozmasını saymıştı; hat bu geri almayı
hızlandırır ve sessizleştirir.

Her geri alma planda birer satırdı; planların dördü okunmadan onaylandığına göre sessizliğin
kaynağı hat değil, okunmayan plan satırıdır.

Üçüncü satır kursun kuralını son kez yazar: **8 kalem hattın hiç görmediği kalemlerdir.** Kayıt
dışı oldukları için ne geri alınırlar ne çatışırlar; hat kusursuz koşarken orada dururlar.

#### Özet

- On sekiz değişikliğin on sekizi onaydan geçti, ama 4 plan okunmadan onaylandı ve o dört plan
  129 kalemin 49'unu (yüzde 38) taşıyordu; onay oranı bir ölçü değildir.
- Plan ile uygulama arasında ortalama 28,6 saat geçti, 18 planın 3'ü bayattı ve onayda görünmeyen
  5 kalem uygulamaya girdi.
- Tazelik kapısı yalnız hatta anlamlıdır, çünkü onaylanan planın saklanmasını gerektirir; 9 dakika
  ekler, 3 uygulamayı durdurur, 5 kalemi kurtarır.
- Geniş yetkide kimlik düşerse 84 kaynağın 84'ü silinebilir; yetki 19 işleme inince silinebilir
  kaynak 14'e, elde kalan iş 4 değişikliğe gelir, sonraki altı koşumda 5 yeni işlem çifti çıkar.
- 26 elle değişikliğin 15'i sessizce geri alındı (4'ü acil düzeltme), 3'ü çatıştı, 8'i kayıt dışı
  olduğu için hiç görülmedi.

#### Kurs Kapanışı

| Ders | Kayda giren | Kayıt ile gerçek arasındaki fark | Kaydın bedeli ya da sınırı |
|---|---|---|---|
| `iac-temelleri/01` | 8 satır tarif | 9 kalem ayrı | kapsam dışı 5 kalem |
| `iac-temelleri/02` | 9 kalemlik hedef | 6/9'a karşı 2/9 | 5 koruma satırı |
| `iac-temelleri/03` | imaj, 96 olay | 10/12 makine tekil | 3 düzeltme kayboldu |
| `iac-temelleri/04` | 34 kalem, 35 tanım | 6 kalem çakışıyor | 5 kalem hiçbir kayıtta yok |
| `iac-temelleri/05` | 26 kalem | 12 sapma, 6'sı kapanır | kapsam dışı 12 kalem |
| `bildirimsel-saglama/01` | 3 blok, 14 atama | 8 kusur uygulamada | tipin görmediği 9 alan |
| `bildirimsel-saglama/02` | 20 alan | hedefte 36 alan | 16 alan kayıtta yok |
| `bildirimsel-saglama/03` | 8 kaynak, 5 veri kaynağı | tarama 8/13 kalem | 2 dış alan görünmez |
| `bildirimsel-saglama/04` | 14 değişken, 24 değer | 4 yanlış girdi geçti | 6 ortamda 48 elle değer |
| `bildirimsel-saglama/05` | 14 kaynak, 23 kenar | gereklilik 27, görülen 23 | 4 gereklilik kayıtta yok |
| `bildirimsel-saglama/06` | 3 biçim, 10 kaynak | 1'e karşı 6 kalem | 525 makine-saniye kesinti |
| `bildirimsel-saglama/07` | plan 11 kalem | 13 adresin 8'i tutuyor | 30 bölgede 39,6 dakika |
| `bildirimsel-saglama/08` | 24 alanın 20'si | ilk plan 4 kalem | 4 alan kayıt dışı |
| `durum-ve-moduller/01` | 56 alan, durumda 80 | 33 kalemin 4'ü kayıt dışı | kayıpta 3 kalem dönmez |
| `durum-ve-moduller/02` | iki eşzamanlı uygulama | kilitsiz 12 kalem kayboldu | 12 kaynak kayıt dışı |
| `durum-ve-moduller/03` | 620 kalem, 673 kenar | 66 kenar sınır aşıyor | 3 kalem kayıt dışı |
| `durum-ve-moduller/04` | 17 ya da 51 satır | 23'e karşı 4 yanlış ortam | 2 ortam 9'ar alan geride |
| `durum-ve-moduller/05` | modül 20 satır | 4 kalemin 0'ı görüldü | 1 kaynak kayıt dışı |
| `durum-ve-moduller/06` | 9 tanım, 12 sürüm | 9/9 koşan söylenmiyor | 25 sürüm geride |
| `durum-ve-moduller/07` | 7 iz noktası | 14 kopya, işaretle 4 | 3 nokta kapanmıyor |
| `iac-dogrulama/01` | 11 kaynak, 59 satır | üç denetim 16/16 | görülmeyen 4 red |
| `iac-dogrulama/02` | 62 satır, 7 politika | 3 kaçan, 2 yanlış alarm | 3 hesaplanan değer görünmez |
| `iac-dogrulama/03` | 15 kusur | üç seviye 12/15 | 11 artık, kayıtta 0 |
| `iac-dogrulama/04` | 24 kalem, 12 değişiklik | 9'unda 0,51x–1,45x | 11 kalem tahmin edilemez |
| `iac-dogrulama/05` | 1700 kayıt satırı | 1598'e karşı 1530 tutuyor | kapsam dışı 27/61 ayar |
| `iac-dogrulama/06` | içerik kaydı 32 girdi | imajlarda 52 girdi | 20 girdi kayıt dışı |
| `iac-dogrulama/07` | 18 değişiklik, 495 satır | 3 bayat plan, 5 kalem | 8 kalem görülmüyor |

Kursun kuralı budur: **altyapı ancak kaydı kadar yönetilebilir; kaydın kapsamadığı her kalem
sapmanın kaynağıdır.** Son sütunda üç sınır tekrar eder.
**On dokuz satırda sınır kaydın kapsamadığı kalemdir** — kayıt dışı kaynak, sağlayıcı varsayılanı,
görülmeyen gereklilik, hesaplanan değer, dolaylı bileşen. **Beş satırda** sınır kaydın kendi
işletme yüküdür: koruma satırı, gözden geçirme dakikası, geride kalan sürüm, geciken uygulama.
**Üç satırda** sınır geri alınamayan kayıptır: silinen düzeltme, hizmet dışı makine, dönmeyen
kalem. Kaydı büyütmek birinci grubu küçültür, ikincisini büyütür; bu takas hiçbir derste
sıfırlanmadı.

Altyapı artık kayda bağlı: yazılıyor, planlanıyor, denetleniyor, fiyatlanıyor, onaydan geçiyor ve
bir hattın uyguladığı iş oluyor. Ama bu kursun bütün ölçümleri **değişiklik anına** aitti.
Altyapının koşarken ne yaptığını hâlâ kimse görmüyor: hangi makine ne kadar yük taşıyor, hangi
kuyruk birikiyor. **M22/K06 Gözlemlenebilirlik ve İşletim** bu soruyla başlıyor — neyin
ölçüleceğine kim karar veriyor?

