---
title: 'Veri Hatları ve Orkestrasyon'
source: 'https://academia.sh/tr/kurslar/veri-hatlari'
language: tr
updated: '2026-08-17T04:40:10+00:00'
license: 'CC BY-SA 4.0'
lessons: 16
outcomes:
    - 'Veri alma yöntemlerini kaynak sistemin kısıtlarına göre seçer ve her birinin kaçırdığı değişikliği sayar'
    - 'Dönüştürme hatlarını katmanlara ayırır, veri testleriyle sınar ve geri doldurmanın raporu nasıl tutarsızlaştırdığını gösterir'
    - 'Bağımlılıklı iş akışını çizge olarak kurar ve bir görev düştüğünde bulaşma kapsamını ölçer'
---

# Veri Hatları ve Orkestrasyon

Hattı kimin, ne zaman ve hangi sırayla çalıştırdığının ve bir adım düştüğünde geri kalanın ne yaptığının sayılması: tam yükleme, artımlı yükleme ve değişiklik yakalamanın taşıdığı satır ile kaçırdığı değişiklik, sayfalama ve hız sınırı altında bağlayıcının tamamladığı çağrı, konu ve bölüm sayısının tüketici grubuna dağıttığı iş, pencereleme ile durum taşımanın bedeli, en çok bir kez teslimde 19 satırın kaybolup her bölgenin kayması ile en az bir kez teslimde 19 yinelenmenin sunumu hiç değiştirmemesi, işlenmiş verinin operasyonel sisteme dönmesinin açtığı döngü, dönüştürmenin kaynakta mı hedefte mi yapıldığının okunan hücreye etkisi, bağımlılık çözümünün sıraladığı görev, ham, arıtılmış ve sunum katmanlarının sakladığı satır, veri testlerinin yakaladığı bozulma, kısmi geri doldurmanın raporu tam uygulamadan 16,98 ile 29,57 arasında saptırması ve hiçbir denetimin bunu bildirmemesi, 48 düğümlü çizgede bir görev düştüğünde engellenen görev sayısı, zaman, olay ve veri hazırlığı tetikleyicilerinin farkı, yeniden denemenin çoğalttığı satır, tazelik ve hacim göstergelerinin bozulmayı kaç dönem sonra göstermesi, hat kodunun ortam ve sürüm yönetimi.

## Veri Alma

Verinin kaynaktan hatta hangi yöntemle alındığının ve her yöntemin neyi kaçırdığının sayılması: tam yüklemenin taşıdığı satır ile artımlı yüklemenin kaçırdığı geç gelen değişiklik ve değişiklik yakalamanın ikisini de gördüğü durum, sayfalama ve hız sınırı altında bağlayıcının tamamladığı çağrı ile yeniden denemenin eklediği istek, konu ve bölüm sayısının tüketici grubuna dağıttığı iş ile bölüm başına sıra korunumu, pencereleme kararının geç gelen kayıtta değiştirdiği sonuç ve durum taşımanın bedeli, en çok bir kez teslimde 19 satırın kaybolarak her bölgeyi kaydırması ile en az bir kez teslimde 19 yinelenmenin anahtarlı yazım sayesinde sunumu hiç değiştirmemesi, işlenmiş verinin operasyonel sisteme geri yazılmasının açtığı döngü ve o döngünün kaç aşama sonra kendi girdisine dönmesi.

### Alma Türleri

Source: https://academia.sh/tr/kurslar/veri-hatlari/alma-turleri

Üç alma yönteminin taşıdığı satır ile kaçırdığı değişikliğin yan yana sayılması: dönem başına dört görevden kurulu çizge 48 düğüm ve 47 kenar taşıyor, tam yükleme on iki koşumda 15.560 satır taşıyıp hiçbir değişikliği kaçırmıyor, artımlı yükleme 2.360 satır taşıyıp 148 değişikliği kaçırıyor, değişiklik yakalama 2.508 satır taşıyıp hiçbirini kaçırmıyor. Artımlı yüklemenin kaçırdığı 40 geç gelen satır ile 108 düzeltilmemiş satır sunumu her bölgede aşağı çekiyor, ama satır sayısı denetimi 0,0167 oranıyla sessiz kalıyor ve bozulmayı ancak dağılım denetimi 0,3469 ile aşama 4'te yakalıyor.

Önceki kurs on iki derste verinin **nerede** ve **hangi düzende** durduğunu ölçtü: bölümleme,
dosya düzeni, sıkıştırma ve okunan hücre yan yana sayıldı. O düzeni **kimin, ne zaman ve hangi
sırayla doldurduğu** hiç sorulmadı. Bir hat kendiliğinden dolmaz; her dönem birileri onu
tetikler, adımlar bir sırayla koşar ve o sıradan biri düştüğünde geri kalanın ne yapacağı
önceden yazılmış olmalıdır.

Bu kurs hattı **koşum** üzerinden ölçer ve kuralı şudur: bir hattın sayısı bir kez doğru koşması
değil, **yeniden koşturulduğunda aynı sonucu vermesidir**; yeniden koşturulamayan hat
**ölçülmemiş** sayılır. Her derste üç sayı yan yana durur: orkestrasyonsuz taban, orkestrasyonlu
kurulum ve bedel. Bu ders üçlüyü ve hattın görev çizgesini kurar, sonra ilk soruyu sorar:
kaynaktan veri **hangi yöntemle** alınıyor ve o yöntem neyi **kaçırıyor**.

- **VA1.** Kod **kurgudur**; gerçek bir depo, motor, mesaj sistemi ya da orkestratör çağrılmaz.
  Kaynak bir ölçüm ağının kurgu sayaç okumalarıdır: 200 abone, 12 dönem, 2400 satır, tohum
  20260218.
- **VA2.** Hat belirlenimcidir; rastgelelik kitaplığı kullanılmaz ve bütün sayılar koşumdan
  bağımsızdır.
- **VA3.** Taban çizgisi **orkestrasyonsuz koşumdur**: tek elle tetikleme, yeniden deneme yok,
  atomik yazım yok, geri doldurma yok.
- **VA4.** Görev çizgesi dönem başına dört **görev** (task) taşır: `al`, `ayikla`, `tuketim`,
  `ozet`. `tuketim` bir önceki dönemin `ayikla` görevine de bağlıdır.
- **VA5.** Geç gelen satır K01'in `gec_gelen` bozulmasıdır: 2026-07 döneminin 40 satırı o dönemin
  koşumunda yoktur, bir sonraki koşumdan önce gelir. Saldırı değil, üretici tarafı kazasıdır.
- **VA6.** Ana veri düzeltmesi 12 abonenin bölgesini onuncu koşumdan önce düzeltir ve düzeltme o
  abonelerin **bütün dönemlerine** uygulanır.
- **VA7.** Hedef anahtarlıdır: `(abone, donem)` çifti bir satırı tekler, aynı anahtar yeniden
  yazıldığında üzerine yazılır. Yazım bu yüzden **yinelemeye dayanıklıdır** (idempotent).
- **VA8.** Taşınan satır, `al` görevinin on iki koşum boyunca hatta geçirdiği satır sayısının
  toplamıdır. Satır oranının adımı 1/2400, yani 0,0004; sunum üzerinde oran basılmaz.

#### Hattın Görev Çizgesi

Bir hat, birbirine bağlı görevlerden kurulu bir **yönlü çevrimsiz çizgedir** (directed acyclic
graph). Çizge kavramı ve topolojik sıralama Veri Yapıları kursundaki Çizgeler konusunda
ölçüldü; burada algoritma tekrarlanmaz, çizge **kurulur ve kullanılır**. Dönem başına
dört görev vardır ve tek gerçek incelik şudur: tüketim bir **fark** hesabıdır, bu yüzden
`tuketim` görevi kendi döneminin `ayikla` görevine olduğu kadar **bir önceki dönemin** `ayikla`
görevine de bağlıdır. Zincirin dönemler arasına uzanması, ilerideki derslerde bir görev
düştüğünde neden bütün bir kuyruğun beklediğini açıklar.

```python
# KURGUDUR. Gercek bir depo, motor ya da orkestrator cagrilmaz; olcum aginin
# sayac okumalari kurgudur, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- HAT: bes asama, her asama kendi dusurdugu satiri sayar.
def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def okuma_sonucu(kayitlar):
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return sun, len(v), oz


# ---- KURGU gorev cizgesi: donem basina dort gorev.
GOREV = ("al", "ayikla", "tuketim", "ozet")
BAGIMLILIK = {"al": (), "ayikla": ("al",), "tuketim": ("ayikla",), "ozet": ("tuketim",)}


def cizge():
    """`tuketim` bir onceki donemin `ayikla`sina da baglidir."""
    dugum, kenar = [], []
    for d in DONEM:
        for g in GOREV:
            dugum.append((g, d))
            for o in BAGIMLILIK[g]:
                kenar.append(((o, d), (g, d)))
        i = DONEM.index(d)
        if i > 0:
            kenar.append((("ayikla", DONEM[i - 1]), ("tuketim", d)))
    return dugum, kenar


def sirala(dugum, kenar):
    gelen = {u: 0 for u in dugum}
    ardil = {u: [] for u in dugum}
    for a, b in kenar:
        gelen[b] += 1
        ardil[a].append(b)
    hazir = [u for u in dugum if gelen[u] == 0]
    sira = []
    while hazir:
        u = hazir.pop(0)
        sira.append(u)
        for v in ardil[u]:
            gelen[v] -= 1
            if gelen[v] == 0:
                hazir.append(v)
    return sira


dugum, kenar = cizge()
sira = sirala(dugum, kenar)
ham = kaynak_uret()
sun, n, _ = okuma_sonucu(ham)
print("cizge", len(dugum), "dugum", len(kenar), "kenar , sira", len(sira), "gorev")
print("ilk uc gorev", sira[:3])
print("kaynak", len(ham), "satir , tuketim", n, "satir")
print("saglam sunum", sun)
```

```
cizge 48 dugum 47 kenar , sira 48 gorev
ilk uc gorev [('al', '2026-01'), ('al', '2026-02'), ('al', '2026-03')]
kaynak 2400 satir , tuketim 2200 satir
saglam sunum {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
```

Kırk sekiz düğüm, kırk yedi kenar ve tam sıralanabilen bir çizge. Kenar sayısının düğümden bir
eksik olması çizgenin **tek parça bir orman** olduğunu söyler: on iki dönemin `al` görevleri
dışında her düğümün tam bir öncülü vardır. Bu derste ölçülen şey çizgenin şekli değil, en baştaki
düğümün ne yaptığıdır — **`al` görevi kaynaktan neyi istiyor**.

#### Üç Alma Yöntemi

**Tam yükleme** (full load) her koşumda kaynağın o anki tamamını okur ve hedefi baştan yazar.
**Artımlı yükleme** (incremental load) yalnız son alınan dönemden sonraki dönemleri ister; sınır
bir dönem numarasıdır ve her koşumda ilerler. **Değişiklik yakalama** (change data capture)
kaynağın kendi değişiklik akışını okur: ekleme ve güncelleme olaylarını taşır, dokunulmayan
satırı hiç taşımaz.

Aşağıdaki blok kaynağın zaman içindeki hâlini modelliyor ve üç yöntemi aynı on iki koşumda
çalıştırıyor. Kaynağa iki üretici tarafı olayı düşüyor: 2026-07'nin kırk satırı geç geliyor ve on
ikinci abonenin bölgesi onuncu koşumdan önce düzeltiliyor.

```python
# ---- KURGU kaynak sistemin zaman icindeki hali. Iki uretici tarafi olayi:
#  1) K01'in `gec_gelen` bozulmasi: 2026-07'nin 40 satiri o donemin kosumunda
#     yok, bir sonraki kosumdan once geliyor.
#  2) Ana veri duzeltmesi: 12 abonenin bolgesi yanlis girilmis, 10. kosumdan
#     once butun donemleri icin duzeltiliyor.
GEC = [f"A{i:03d}" for i in range(1, 41)]
YANLIS = [f"A{i:03d}" for i in range(101, 113)]


def kaynak_anlik(kosum):
    """Kaynagin `kosum` numarali kosum aninda gorunen hali."""
    kayit = []
    for k in kaynak_uret():
        i = DONEM.index(k["donem"]) + 1
        if i > kosum or (i == 7 == kosum and k["abone"] in GEC):
            continue
        y = dict(k)
        if k["abone"] in YANLIS and kosum < 10:
            y["bolge"] = BOLGE[(BOLGE.index(k["bolge"]) + 1) % 5]
        kayit.append(y)
    return kayit


def tam_yukleme():
    """Her kosumda kaynagin tamami yeniden okunur , hedef silinip yazilir."""
    hedef, tasinan = {}, 0
    for kosum in range(1, 13):
        anlik = kaynak_anlik(kosum)
        tasinan += len(anlik)
        hedef = {(k["abone"], k["donem"]): k for k in anlik}
    return list(hedef.values()), tasinan


def artimli_yukleme():
    """Yalniz son alinan donemden sonraki donemler okunur."""
    hedef, tasinan, sinir = {}, 0, 0
    for kosum in range(1, 13):
        yeni = [k for k in kaynak_anlik(kosum) if DONEM.index(k["donem"]) + 1 > sinir]
        tasinan += len(yeni)
        for k in yeni:
            hedef[(k["abone"], k["donem"])] = k
        sinir = kosum
    return list(hedef.values()), tasinan


def degisiklik_yakalama():
    """Kaynagin degisiklik akisi okunur , ekleme ve guncelleme olaylari tasinir."""
    hedef, tasinan, onceki = {}, 0, {}
    for kosum in range(1, 13):
        anlik = {(k["abone"], k["donem"]): k for k in kaynak_anlik(kosum)}
        olay = [k for ad, k in anlik.items() if onceki.get(ad) != k]
        tasinan += len(olay)
        for k in olay:
            hedef[(k["abone"], k["donem"])] = k
        onceki = anlik
    return list(hedef.values()), tasinan


gercek = {(k["abone"], k["donem"]): k for k in ham}
dogru_sun, _, dogru_oz = okuma_sonucu(ham)
print("yontem      tasinan  hedef  eksik  yanlis  tuketim  satir orani  dagilim eni")
for ad, f in (("tam", tam_yukleme), ("artimli", artimli_yukleme),
              ("degisiklik", degisiklik_yakalama)):
    hed, tasinan = f()
    hd = {(k["abone"], k["donem"]): k for k in hed}
    eksik = len(set(gercek) - set(hd))
    yanlis = sum(1 for a in hd if gercek[a] != hd[a])
    s, t, oz = okuma_sonucu(hed)
    en = max(abs(v - dogru_oz[a]) / max(abs(dogru_oz[a]), 1) for a, v in oz.items())
    print(f"{ad:<11} {tasinan:>7} {len(hd):>6} {eksik:>6} {yanlis:>7} {t:>8} "
          f"{abs(len(hd) - 2400) / 2400:>12.4f} {en:>12.4f}")
print()
for ad, f in (("tam", tam_yukleme), ("artimli", artimli_yukleme),
              ("degisiklik", degisiklik_yakalama)):
    s, _, _ = okuma_sonucu(f()[0])
    print(f"{ad:<11}", {b: round(s.get(b, 0) - dogru_sun[b], 2) for b in dogru_sun})
```

```
yontem      tasinan  hedef  eksik  yanlis  tuketim  satir orani  dagilim eni
tam           15560   2400      0       0     2200       0.0000       0.0000
artimli        2360   2360     40     108     2120       0.0167       0.3469
degisiklik     2508   2400      0       0     2200       0.0000       0.0000

tam         {'merkez': 0.0, 'kuzey': 0.0, 'dogu': 0.0, 'bati': 0.0, 'guney': 0.0}
artimli     {'merkez': -11.18, 'kuzey': -28.1, 'dogu': -39.09, 'bati': -36.91, 'guney': -70.0}
degisiklik  {'merkez': 0.0, 'kuzey': 0.0, 'dogu': 0.0, 'bati': 0.0, 'guney': 0.0}
```

#### Taşınan Satır ile Kaçırılan Değişiklik

Tablonun ilk sütunu tam yüklemenin bedelini yazıyor: on iki koşumda **15.560 satır** taşınmış,
oysa sonuçta hedefte duran satır **2400**. Aynı veriyi altı buçuk kez taşımanın karşılığı, hiçbir
değişikliği kaçırmamasıdır. Geç gelen kırk satır sekizinci koşumda kendiliğinden yerine oturur,
bölge düzeltmesi onuncu koşumda bütün dönemlere yayılır, çünkü her koşum kaynağı yeniden okur.
Tam yüklemenin bir kusuru yoktur; yalnız pahalıdır ve **kaynak büyüdükçe pahalılığı doğrusal
büyür**.

Artımlı yükleme en az satırı taşıyor: **2360**. Bedeli aynı satırda duruyor. Yedinci koşum
dönemin yalnız 160 satırını görüyor; kırk satır sonra geliyor ama sınır artık yedidedir ve o
satırlar bir daha hiç istenmez. Bölge düzeltmesi de aynı nedenle yalnız onuncu koşumdan sonraki
dönemlere iniyor: hedefte on iki abonenin dokuz dönemi, yani **108 satır**, eski bölgesiyle
kalıyor. Toplam **148 kaçırılan değişiklik** ve bunların 40'ı eksik satır, 108'i yanlış satırdır.
Artımlı yükleme neyi kaçırdığını **bilmez** — sınırın gerisinde bir şeyin değiştiğini görecek bir
düzeneği yoktur.

Değişiklik yakalama iki olayı da görüyor ve bunun için taşıdığı satır **2508**. Artımlı
yüklemeden yalnız **148 satır** fazla, tam yüklemeden **13.052 satır** az. Fark tam olarak
kaçırılan değişiklik sayısı kadardır ve bu tesadüf değildir: değişiklik yakalamanın taşıdığı şey
**değişikliğin kendisidir**. Bedeli satırda değil kaynaktadır; kaynağın değişiklik akışını
yayımlıyor olması gerekir ve bu yayımı yapmayan bir kaynakta yöntem hiç kurulamaz.

#### Kaçırılan Değişikliği Hangi Denetim Görüyor

Artımlı yüklemenin hedefi sunumu her bölgede aşağı çekiyor: en küçük sapma merkezde **−11,18**, en
büyüğü güneyde **−70,00**. Bu sapmayı görecek biri var mıdır? Veri Mühendisliğine Giriş kursunda
kurulan denetim kümesi bu soruya iki farklı cevap veriyor. Satır sayısı denetiminin oranı
**0,0167**; öntanımlı tolerans
0,05 olduğu için denetim **yeşil kalıyor**. Şema, tür ve tekillik denetimleri de yeşildir, çünkü
eksik satırların şeması bozuk değil, sadece yokturlar. Bölge kümesi de tamdır: on iki abonenin
bölgesi yanlış ama yanlış olan bölge de listede vardır.

Yakalayan tek denetim **dağılım denetimidir** ve özet katmanında en büyük hücre sapması **0,3469**
ile 0,25 eşiğini aşar. Bu denetim aşama 4'te koşar; yani bozulma alma, ayıklama, tekilleştirme ve
tüketim hesabından geçtikten sonra görünür. Yakalama gecikmesi **4 aşamadır** ve o ana kadar 2120
satırlık ara tablo çoktan yazılmıştır. Yöntem seçimi burada bir ölçü seçimine dönüşür: artımlı
yükleme taşınan satırı 6,6 kat düşürür, karşılığında 148 değişikliği kaçırır ve bu kaçağı yalnız
en pahalı denetim, en geç aşamada bildirir.

#### Özet

- Hat, dönem başına dört görevden kurulu bir yönlü çevrimsiz çizgedir: 48 düğüm, 47 kenar ve tam
  sıralanabilen bir koşum sırası. `tuketim` bir önceki dönemin `ayikla` görevine bağlıdır, bu
  yüzden zincir dönemler arasına uzanır.
- Tam yükleme hiçbir değişikliği kaçırmaz ve bedelini taşınan satırla öder: 2400 satırlık bir
  hedef için on iki koşumda 15.560 satır taşınır.
- Artımlı yükleme en az satırı taşır (2360) ama sınırın gerisinde olan biteni göremez: 40 geç
  gelen satır ile 108 düzeltilmemiş satır, toplam 148 değişiklik kaçar.
- Değişiklik yakalama iki olayı da görür ve artımlı yüklemeden yalnız kaçırılan değişiklik sayısı
  kadar, 148 satır fazla taşır; koşulu, kaynağın değişiklik akışını yayımlıyor olmasıdır.
- Kaçırılan değişikliği satır sayısı denetimi 0,0167 oranıyla görmez; yalnız dağılım denetimi
  0,3469 ile ve aşama 4 gecikmeyle bildirir.

#### Sonraki Adım

Bu derste `al` görevi kaynağı **tek bir hamlede** okudu: `kaynak_anlik` çağrıldı ve satırlar
elde oldu. Kaynak sistemler böyle okunmaz. Bir uçnokta veriyi sayfa sayfa verir, pencere başına
kaç çağrı yapılabileceğini sınırlar ve çağrılardan bir kısmı geçici olarak düşer. Sonraki ders
aynı `al` görevini bu kısıtların altına sokar ve iki şeyi sayar: **kısıt altında tamamlanan çağrı
ve alınan satır**. Ölçülecek takas şudur: sayfa boyunu büyütmek çağrı sayısını düşürür, ama düşen
tek bir çağrının kaçırdığı satır aynı oranda büyür.

### Bağlayıcılar ve API Tüketimi

Source: https://academia.sh/tr/kurslar/veri-hatlari/baglayicilar-ve-api-tuketimi

Sayfalama ve hız sınırı altında bağlayıcının tamamladığı çağrı ile aldığı satırın sayılması: pencere başına sekiz çağrı kotasıyla 25 satırlık sayfada 96 çağrının 83'ü tamamlanıyor ve 2075 satır alınıyor, 100 satırlık sayfada 24 çağrının 20'si tamamlanıyor ve 2000 satır alınıyor, yani düşen çağrı başına kaçan satır sayfa boyuyla birlikte 25'ten 100'e çıkıyor. Yeniden deneme küçük sayfada kotayı yiyor ve alınan satırı 2075'ten 2025'e düşürüyor, 112 sayfa bir pencere geciktiriyor, 2026-12 dönemi hiç alınmıyor; buna karşılık sunum sapması −374'ten −125'e iniyor, çünkü kayıp dağınık olmaktan çıkıp kuyruğun sonunda toplanıyor. Bağlayıcının kendi çağrı defteri eksiği gecikmesiz bildiriyor: beklenen 96, tamamlanan 83.

Önceki ders `al` görevini tek bir hamle olarak ele aldı: kaynağın o anki hâli çağrıldı ve satırlar
elde oldu. Kaynak sistemler böyle okunmaz. Veriyi bir uçnokta üzerinden veren bir kaynak onu
**sayfa sayfa** teslim eder, belirli bir pencerede kaç çağrı yapılabileceğini **sınırlar** ve
çağrılardan bir kısmı geçici olarak düşer. `al` görevinin bir kez koşması artık tek bir işlem
değil, onlarca çağrıdan oluşan bir dizidir.

Bu dersin ölçüsü iki sayıdır: **kısıt altında tamamlanan çağrı** ve **alınan satır**. Sayfalama ve
hız sınırının kendisi Web API Tasarımı, SQL Temelleri ve Model API'leri ile Uygulama Geliştirme
kurslarında ölçüldü; yordam burada tekrarlanmaz. Burada sorulan tek şey şudur: bir **bağlayıcı**
(connector) bu kısıtların altında hattın istediği satırların kaçını getirebiliyor ve getiremediği
satırın hangi izi kalıyor.

- **VA9.** Kod **kurgudur**; gerçek bir uçnokta, bağlayıcı ürünü ya da orkestratör çağrılmaz.
  Kaynak yine 200 abone, 12 dönem, 2400 satır, tohum 20260218.
- **VA10.** Uçnokta bir dönemin satırlarını **sayfa boyu** kadar parça hâlinde verir ve dönem
  başına kaç satır bulunduğunu bildirir; bağlayıcı bu yüzden kaç sayfa isteyeceğini önceden bilir.
- **VA11.** Hız sınırı **pencere başına kota** olarak modellenir: her dönemin bir penceresi vardır
  ve o pencerede en çok `kota` çağrı tamamlanabilir. Sığmayan sayfa bir sonraki pencereye kalır;
  bu bir **dönem** gecikmedir. Süre ham olarak yazılmaz.
- **VA12.** Geçici hata payı 0,15'tir ve tohumdan türetilir; aynı sayfanın aynı denemesi her
  koşumda aynı sonucu verir. Hat belirlenimcidir.
- **VA13.** Yeniden deneme hakkı **kotadan yenir**: başarısız bir çağrının tekrarı da bir çağrıdır.
  Geri çekilme yordamı Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda ölçüldü, burada
  tekrarlanmaz.
- **VA14.** Hakkı tükenen sayfa **bırakılır** ve bağlayıcı yoluna devam eder; o sayfanın satırları
  hiç alınmaz.
- **VA15.** Taban çizgisi orkestrasyonsuz koşumdur: yeniden deneme yok, kuyruk yok, tek elle
  tetikleme. Yeniden deneme bu dersin eklediği tek düzenektir.
- **VA16.** Satır oranının adımı 1/2400, yani 0,0004; sunum üzerinde oran basılmaz.

#### Kısıt Altında Bir Bağlayıcı

Aşağıdaki blok ortak tanımı kurar ve üzerine sayfalı bir uçnokta ile onu tüketen bir bağlayıcı
ekler. Bağlayıcının tek bir kuyruğu vardır: her dönem o dönemin sayfaları kuyruğa girer, pencere
kotası bitene kadar sayfalar çekilir, kalanlar bir sonraki pencereye devrolur.

```python
# KURGUDUR. Gercek bir ucnokta, baglayici urunu ya da orkestrator cagrilmaz;
# olcum aginin sayac okumalari kurgudur, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- HAT: bes asama, her asama kendi dusurdugu satiri sayar.
def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def okuma_sonucu(kayitlar):
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return sun, len(v)


# ---- KURGU ucnokta: sayfali okuma , pencere basina kota , gecici hata.
def sayfa(kayitlar, donem, no, boyu):
    h = [k for k in kayitlar if k["donem"] == donem]
    return h[no * boyu:(no + 1) * boyu]


def cagri_duser(donem, no, hak, pay):
    """Gecici hata tohumdan turetilir , kosumdan bagimsizdir."""
    r = uretec(TOHUM + 13 * DONEM.index(donem) + 3 * no + 101 * hak)
    return r() < pay


def baglayici(kayitlar, boyu, kota, pay=0.15, deneme=0):
    """Her donemin bir penceresi var ; pencerede en cok `kota` cagri tamamlanir.
    Sigmayan sayfa bir sonraki pencereye kalir , bu bir donem gecikmedir."""
    adet = (200 + boyu - 1) // boyu
    alinan, tamam, basarisiz, geciken, kuyruk = [], 0, 0, 0, []
    for d in DONEM:
        kuyruk += [(d, n) for n in range(adet)]
        kalan = kota
        while kuyruk and kalan > 0:
            gd, n = kuyruk[0]
            gecti, hak = False, 0
            while hak <= deneme and kalan > 0:
                kalan -= 1
                if cagri_duser(gd, n, hak, pay):
                    basarisiz += 1
                    hak += 1
                    continue
                tamam += 1
                gecti = True
                break
            if gecti or hak > deneme:               # ya alindi ya da hak bitti
                kuyruk.pop(0)
                if gecti:
                    alinan.extend(sayfa(kayitlar, gd, n, boyu))
        geciken += len(kuyruk)                      # bir pencere daha bekleyen sayfa
    return alinan, tamam, basarisiz, geciken, len(kuyruk)


ham = kaynak_uret()
dogru_sun, dogru_n = okuma_sonucu(ham)
print("kota 8 cagri/pencere , gecici hata payi 0.15")
print("boyu  sayfa  deneme  beklenen  tamam  basarisiz  geciken  alinmayan  alinan  tuketim")
for boyu in (25, 50, 100):
    for deneme in (0, 2):
        al, tm, bs, gc, kal = baglayici(ham, boyu, 8, 0.15, deneme)
        s, t = okuma_sonucu(al)
        adet = (200 + boyu - 1) // boyu
        print(f"{boyu:>4} {adet:>6} {deneme:>7} {12 * adet:>9} {tm:>6} {bs:>10} "
              f"{gc:>8} {kal:>10} {len(al):>7} {t:>8}")
```

```
kota 8 cagri/pencere , gecici hata payi 0.15
boyu  sayfa  deneme  beklenen  tamam  basarisiz  geciken  alinmayan  alinan  tuketim
  25      8       0        96     83         13        0          0    2075     1575
  25      8       2        96     81         15      112         15    2025     1825
  50      4       0        48     39          9        0          0    1950     1300
  50      4       2        48     48         12        0          0    2400     2200
 100      2       0        24     20          4        0          0    2000     1400
 100      2       2        24     24          6        0          0    2400     2200
```

#### Sayfa Boyu ile Çağrı Başına Kaçan Satır

Yeniden denemesiz üç satır ilk takası veriyor. Sayfa boyu 25 iken bir dönem sekiz sayfa eder,
on iki dönem **96 çağrı** ister; boyu 100'e çıkınca aynı iş **24 çağrıya** iner. Çağrı sayısı
sayfa boyuyla ters orantılı düşer ve bu, hız sınırı olan bir kaynakta doğrudan kazançtır.

Kazancın karşılığı aynı satırlarda yazılı. Boyu 25'te 13 çağrı düşüyor ve 325 satır kaçıyor; boyu
50'de 9 çağrı düşüyor ama 450 satır kaçıyor; boyu 100'de yalnız 4 çağrı düşüyor ve yine 400 satır
kaçıyor. Çarpım her satırda birebir tutuyor: kaçan satır **düşen çağrı sayısı ile sayfa boyunun
çarpımıdır**. Sayfa boyunu dörde katlamak çağrı sayısını dörtte bire indirir ve **tek bir
başarısızlığın bedelini dört katına çıkarır**. Bağlayıcı ayarı bu iki eğrinin kesiştiği yerde
seçilir ve hangisinin ağır bastığı kaynağın hata payına bağlıdır.

Tüketim sütunu bedelin hattın içinde nasıl büyüdüğünü gösteriyor. Boyu 50'de 450 satır kaçıyor ama
tüketim satırı 2200'den **1300**'e düşüyor: kaçan her satır kendi dönemindeki tüketimi düşürmekle
kalmıyor, **bir sonraki dönemin** tüketimini de hesaplanamaz kılıyor, çünkü tüketim bir fark
hesabıdır. 450 satırlık bir eksik, 900 satırlık bir kayba dönüşüyor.

#### Yeniden Denemenin Kotayı Yemesi

Bir pencerenin taşıyabileceği en fazla satır, kota ile sayfa boyunun çarpımıdır. Kota sekiz iken
boyu 25 pencere başına **200 satır**, boyu 50 **400 satır**, boyu 100 **800 satır** tavanı verir.
Bir dönemin talebi ise tam 200 satırdır. Boyu 25'te tavan ile talep **birebir eşittir** ve
bağlayıcının hiçbir boşluğu yoktur; boyu 50'de tavan talebin iki katı, boyu 100'de dört katıdır.
Yeniden denemenin kendine yer bulup bulamayacağını belirleyen şey, hata payı değil bu boşluktur.

Yeniden deneme boyu 50 ve boyu 100 satırlarında **bedavaya yakındır**: 48 ve 24 çağrı kotanın
altında kaldığı için başarısız çağrılar tekrarlanabiliyor ve alınan satır 2400'e, tüketim 2200'e
çıkıyor. Sapma sıfırlanıyor.

Boyu 25 satırında aynı düzenek tersine dönüyor. Beklenen 96 çağrı zaten kotanın tam sınırındadır;
yeniden denemeler kotadan yediği için her pencerede birkaç sayfa yetişemiyor ve kuyruk büyüyor.
Sonuç, yeniden deneme **olmadan** alınan 2075 satıra karşılık yeniden deneme **ile** alınan
**2025 satırdır**. Düzeneği eklemek alınan satırı düşürmüştür. Yanına iki sayı daha yazılıyor:
**112 sayfa** en az bir pencere gecikmiş ve **15 sayfa** on iki pencere bittiğinde hâlâ kuyrukta
beklemektedir. Bu kursun ikinci iddiası tam olarak budur: orkestrasyon düzeneği de bozar ve
bozduğunu ancak kendi sayacına bakan görür.

#### Bağlayıcının Kendi Çağrı Defteri

Sıradaki blok üç kurulumu yan yana koyup kaybın **nereye** düştüğünü soruyor.

```python
TAM_KUME = set((b, d) for b in BOLGE for d in DONEM)
print("boyu kota deneme  alinan  satir orani  bolum_donem  donem basina alinan satir")
for boyu, kota, deneme in ((25, 8, 0), (25, 8, 2), (25, 12, 2)):
    al, tm, bs, gc, kal = baglayici(ham, boyu, kota, 0.15, deneme)
    say = [sum(1 for k in al if k["donem"] == d) for d in DONEM]
    gor = set((k["bolge"], k["donem"]) for k in al)
    print(f"{boyu:>4} {kota:>4} {deneme:>6} {len(al):>7} {(2400 - len(al)) / 2400:>12.4f} "
          f"{str(gor != TAM_KUME):>12}  {say}")
print()
for boyu, kota, deneme in ((25, 8, 0), (25, 8, 2), (25, 12, 2)):
    s, t = okuma_sonucu(baglayici(ham, boyu, kota, 0.15, deneme)[0])
    print(f"boyu {boyu} kota {kota} deneme {deneme}",
          {b: round(s.get(b, 0) - dogru_sun[b], 2) for b in dogru_sun})
```

```
boyu kota deneme  alinan  satir orani  bolum_donem  donem basina alinan satir
  25    8      0    2075       0.1354        False  [175, 175, 175, 150, 175, 125, 200, 175, 200, 175, 150, 200]
  25    8      2    2025       0.1562         True  [200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 25, 0]
  25   12      2    2400       0.0000        False  [200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200]

boyu 25 kota 8 deneme 0 {'merkez': -373.72, 'kuzey': -302.19, 'dogu': -278.0, 'bati': -190.91, 'guney': -367.28}
boyu 25 kota 8 deneme 2 {'merkez': -124.85, 'kuzey': -118.25, 'dogu': -82.06, 'bati': -78.39, 'guney': -105.05}
boyu 25 kota 12 deneme 2 {'merkez': 0.0, 'kuzey': 0.0, 'dogu': 0.0, 'bati': 0.0, 'guney': 0.0}
```

İkinci satırın dönem dizisi dersin en ters sonucudur. Yeniden denemeli kurulum **daha az satır
alıyor** ama sunum sapması **daha küçük**: −374 … −191 yerine −125 … −78. Sebep kaybın miktarında
değil **yerindedir**. Yeniden denemesiz kurulumda kayıp on iki döneme dağılmıştır ve her dönemin
toplamını aşağı çeker; yeniden denemeli kurulumda ilk on dönem **eksiksiz** alınmış, kuyruk
büyüdüğü için 2026-11 yalnız 25 satır almış ve 2026-12 **hiç alınmamıştır**. Bölge ortalaması var
olan dönemler üzerinden hesaplandığı için sunum düzelmiş görünür. Sunulan sayının iyileşmesi
verinin iyileştiği anlamına gelmez.

Kaybı kim bildiriyor sorusunun cevabı da iki satırda ayrılıyor. Dağınık kayıpta dönem duyarlı
bütünlük denetimi **yeşil kalıyor**, çünkü her bölge her dönemde hâlâ görünüyor; yakalayan tek
şey 0,1354 oranıyla satır sayısı denetimidir. Kuyruk kaybında ise aynı denetim **ateşliyor**, çünkü
bir dönemin hiçbir satırı yoktur. İkisinden de ucuzu hattın içinde değil bağlayıcının kendi
defterindedir: beklenen 96 çağrıya karşılık 83 tamamlanmıştır ve bu fark **hiçbir aşama
beklemeden**, çağrı biter bitmez bilinir. Kotayı sekizden on ikiye çıkarmak son satırı üretir —
96 çağrının hepsi tamamlanır, 2400 satır alınır, sapma boşalır ve geciken sayfa kalmaz.

#### Özet

- Bir bağlayıcının işi kısıt altında sayılır: sayfa boyu 25 iken 96 çağrı gerekir, 100 iken 24;
  çağrı sayısı sayfa boyuyla ters orantılıdır ve hız sınırı olan bir kaynakta doğrudan kazançtır.
- Kaçan satır, düşen çağrı sayısı ile sayfa boyunun çarpımıdır: 13 çağrı 325 satır, 9 çağrı 450
  satır, 4 çağrı 400 satır. Sayfa boyunu büyütmek tek bir başarısızlığın bedelini aynı oranda
  büyütür.
- Kaçan satır hattın içinde büyür: 450 satırlık eksik, tüketim bir fark hesabı olduğu için 900
  satırlık kayba dönüşür.
- Yeniden deneme kotanın altında bedavadır ve 2400 satırın hepsini geri getirir; kotanın sınırında
  kotayı yer, 112 sayfayı geciktirir ve alınan satırı 2075'ten 2025'e düşürür.
- Kayıp kuyruğun sonunda toplandığında sunum sapması küçülür (−125 … −78) ama son iki dönem çöker:
  2026-11 yalnız 25 satır alır, 2026-12 hiç alınmaz. Bunu satır sayısı değil dönem duyarlı bütünlük
  denetimi görür, en ucuzunu ise bağlayıcının çağrı defteri gecikmesiz bildirir.

#### Sonraki Adım

Bu dersteki bağlayıcı kaynağı **çekti**: ne zaman okuyacağına kendi karar verdi, kotasını kendi
harcadı ve kaçırdığını kendi defterinden bildi. Kaynağın veriyi kendiliğinden **ittiği** bir
düzenek bu dengeyi tersine çevirir. Kayıtlar bir **konuya** yazılır, konu **bölümlere** ayrılır ve
bölümleri bir **tüketici grubu** okur. Sonraki ders bu düzeneği iki sayı üzerinden ölçer: bölüm
sayısının tüketicilere dağıttığı iş ve bölüm başına sıra korunumu. Ölçülecek takas şudur: bölüm
sayısı arttıkça paralellik artar, ama aynı abonenin kayıtları farklı bölümlere düştüğünde tüketim
hesabının dayandığı sıra kırılır.

### Mesaj Sistemleri

Source: https://academia.sh/tr/kurslar/veri-hatlari/mesaj-sistemleri

Bölüm sayısının tüketici grubuna dağıttığı iş ile bölüm başına sıra korunumunun yan yana sayılması: dört tüketicili bir grupta tek bölüm üç tüketiciyi boşta bırakıyor, dört bölüm hepsini çalıştırıyor. Abone anahtarıyla bölümlenen konuda hiçbir abone dağılmıyor ve tüketim her bölüm sayısında 2200 satır kalıyor, ama yük 708 ile 492 arasında eğriliyor. En dengeli anahtar olan kayıt anahtarı yükü 621 ile 583 arasına sıkıştırıyor ve 200 abonenin hepsini dağıtıyor: tüketim dört bölümde 527 satıra düşüyor, sunum her bölgede 616 ile 977 arasında kayıyor. Dönem anahtarı ise iki bölümde 2200 satırın 1.200'ünü kaybederken sunumu yalnız −15,29 ile +16,15 arasında oynatıyor.

Önceki iki ders alma işini **çekme** üzerinden ele aldı: hat kaynağa gitti, ne isteyeceğine kendi
karar verdi ve kotasını kendi harcadı. Kaynağın veriyi kendiliğinden **ittiği** düzenek bu
dengeyi tersine çevirir. Kayıtlar bir **konuya** (topic) yazılır, konu **bölümlere** ayrılır ve
bölümleri bir **tüketici grubu** (consumer group) okur. Kuyruk, yayımla–abone ol düzeni, konum ve
teslim semantiği Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda ölçüldü; yordam burada
tekrarlanmaz.

Bu dersin ölçüsü iki sayıdır: **bölüm sayısının tüketiciye dağıttığı iş** ve **bölüm başına sıra
korunumu**. İkisi aynı ayarın iki yüzüdür. Bölüm sayısı arttıkça paralellik artar; ama aynı
abonenin kayıtları farklı bölümlere düştüğünde, tüketim hesabının dayandığı sıra kırılır ve hat
satır kaybeder.

- **VA17.** Kod **kurgudur**; gerçek bir mesaj sistemi, akış işleyici ya da orkestratör
  çağrılmaz. Kaynak yine 200 abone, 12 dönem, 2400 satır, tohum 20260218.
- **VA18.** Konuya yazım sırası dönem dönemdir; bir dönemin bütün abone kayıtları yazıldıktan
  sonra sonraki dönem başlar.
- **VA19.** Bir kayıt **bölüm anahtarına** göre tek bir bölüme düşer ve aynı anahtar her zaman
  aynı bölüme gider. Bölüm içindeki **konum** (offset) yazım sırasını korur; bölümler arasında
  böyle bir sıra yoktur.
- **VA20.** Bir bölümü grup içinde **tek bir tüketici** okur. Bölüm sayısı bu yüzden aynı gruptaki
  etkin tüketici sayısının tavanıdır. Grupta dört tüketici vardır.
- **VA21.** Üç bölüm anahtarı sınanır: `abone`, `kayit` (abone ile dönemin birleşimi) ve `donem`.
  Üçü de kaynağın kendi alanlarıdır; hiçbiri uydurma değildir.
- **VA22.** Her tüketici tüketim hesabını **yalnız kendi bölümündeki kayıtlarla** yapar; bir
  abonenin önceki dönemi başka bir bölümdeyse o satır hesaplanamaz ve düşer.
- **VA23.** Taban çizgisi orkestrasyonsuz koşumdur; sağlam hatta tüketim 2200 satırdır ve sunum
  sapması boştur.
- **VA24.** Bölüm sayısı ve boşta tüketici tam sayıdır, oran basılmaz. Satır oranının adımı
  1/2400, yani 0,0004; sunum üzerinde oran basılmaz.

#### Konu, Bölüm ve Tüketici Grubu

Aşağıdaki blok ortak tanımı kurar ve üzerine bölümlü bir konu ile onu okuyan bir tüketici grubu
ekler. Her kurulum için beş sayı basılıyor: en çok ve en az yüklenen tüketicinin satır sayısı,
boşta kalan tüketici sayısı, birden çok bölüme dağılmış abone sayısı ve bölümlerden toplanan
tüketim satırı.

```python
# KURGUDUR. Gercek bir mesaj sistemi, akis isleyici ya da orkestrator
# cagrilmaz; olcum aginin sayac okumalari kurgudur, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- HAT: bes asama, her asama kendi dusurdugu satiri sayar.
def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def okuma_sonucu(kayitlar):
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return sun, len(v)


# ---- KURGU konu: bolumler , bolum anahtari , tuketici grubu.
def dagit(anahtar, p):
    """Anahtardan bolum numarasi. Ayni anahtar her zaman ayni bolume duser."""
    x = 0
    for c in anahtar:
        x = (x * 131 + ord(c)) & M32
    return int(uretec(TOHUM + x)() * p)


def bolumle(kayitlar, p, anahtar):
    """Konuya yazim sirasi donem donem ; her kayit anahtarina gore bir bolume yazilir.
    Bolum icindeki konum yazim sirasini korur."""
    bolum = {b: [] for b in range(p)}
    for k in sorted(kayitlar, key=lambda k: (k["donem"], k["abone"])):
        ad = {"abone": k["abone"], "kayit": k["abone"] + k["donem"],
              "donem": k["donem"]}[anahtar]
        b = dagit(ad, p)
        bolum[b].append(dict(k, konum=len(bolum[b])))
    return bolum


def tuketici_grubu(bolum, c):
    """Bir bolumu grup icinde tek bir tuketici okur ; bolum sayisi tavandir."""
    atama = {t: [] for t in range(c)}
    for b in bolum:
        atama[b % c].append(b)
    return atama


ham = kaynak_uret()
dogru_sun, dogru_n = okuma_sonucu(ham)
print("tuketici grubu 4 tuketici , tuketim saglam hatta", dogru_n, "satir")
print("anahtar  bolum  en cok  en az  bosta  dagilan abone  hesaplanan tuketim")
for anahtar in ("abone", "kayit", "donem"):
    for p in (1, 2, 4, 8):
        bl = bolumle(ham, p, anahtar)
        at = tuketici_grubu(bl, 4)
        yuk = [sum(len(bl[b]) for b in at[t]) for t in at]
        yer = {}
        for b in bl:
            for k in bl[b]:
                yer.setdefault(k["abone"], set()).add(b)
        tk = sum(len(a3_tuketim(bl[b])[0]) for b in bl)
        print(f"{anahtar:<8} {p:>6} {max(yuk):>7} {min(yuk):>6} "
              f"{sum(1 for y in yuk if y == 0):>6} "
              f"{sum(1 for v in yer.values() if len(v) > 1):>14} {tk:>19}")
```

```
tuketici grubu 4 tuketici , tuketim saglam hatta 2200 satir
anahtar  bolum  en cok  en az  bosta  dagilan abone  hesaplanan tuketim
abone         1    2400      0      3              0                2200
abone         2    1200      0      2              0                2200
abone         4     708    492      0              0                2200
abone         8     696    528      0              0                2200
kayit         1    2400      0      3              0                2200
kayit         2    1211      0      2            200                1076
kayit         4     621    583      0            200                 527
kayit         8     624    572      0            200                 268
donem         1    2400      0      3              0                2200
donem         2    1600      0      2            200                1000
donem         4     800    400      0            200                 200
donem         8    1000    200      0            200                 200
```

#### Bölüm Sayısının Dağıttığı İş

İlk sütun çifti anahtardan bağımsız bir gerçeği yazıyor: tek bölümlü bir konuda dört tüketicinin
**üçü boşta** durur, iki bölümde ikisi boşta durur, dörde çıkıldığında hiçbiri boşta kalmaz. Bölüm
sayısı bir tercih değil bir **tavandır**; gruba tüketici eklemek bölüm sayısını aşan noktada
hiçbir şey kazandırmaz. Sekiz bölümde de dört tüketici çalışır, ama her biri iki bölüm okur.

İkinci gözlem yükün nasıl bölündüğüdür. Abone anahtarında dört bölüm 708 ile 492 satır arasında
eğriliyor, yani en yüklü tüketici en boş olanın 1,44 katı iş yapıyor. Kayıt anahtarında aynı
kurulum 621 ile 583 arasına sıkışıyor: bu üç anahtarın **en dengelisidir**, çünkü abone ile dönemi
birleştiren anahtar 2400 farklı değer üretir ve bölümlere neredeyse eşit yayılır. Dönem anahtarı en
kötüsüdür; on iki farklı değeri dört bölüme dağıtınca yük 800 ile 400 arasında, sekiz bölümde 1000
ile 200 arasında açılır. Yalnız bu üç satıra bakan biri kayıt anahtarını seçerdi.

#### Bölüm Başına Sıra Korunumu

Son iki sütun seçimi tersine çeviriyor. Abone anahtarında **hiçbir abone dağılmıyor** ve tüketim
her bölüm sayısında **2200 satır** kalıyor. Kayıt anahtarında **200 abonenin hepsi** dağılıyor ve
tüketim dört bölümde 527, sekiz bölümde 268 satıra iniyor. Mekanizma tek cümledir: bir bölüm
kendi içinde sırayı korur, bölümler arasında sıra yoktur ve tüketim bir abonenin **ardışık iki
döneminin farkıdır**. İki dönem farklı bölümlere düşerse o satırı hesaplayacak tüketici yoktur.

```python
print("anahtar  bolum  hesaplanan tuketim  sunum sapmasi")
for anahtar, p in (("abone", 4), ("kayit", 4), ("donem", 2), ("donem", 4)):
    bl = bolumle(ham, p, anahtar)
    oz, tk = {}, 0
    for b in bl:                                    # her tuketici kendi bolumunu okur
        v, _ = a3_tuketim(bl[b])
        tk += len(v)
        o, _ = a4_ozet(v)
        for ad, s in o.items():
            oz[ad] = oz.get(ad, 0) + s
    s, _ = a5_sun(oz)
    print(f"{anahtar:<8} {p:>6} {tk:>18}  ",
          {b: round(s.get(b, 0) - dogru_sun[b], 2) for b in dogru_sun})

bl = bolumle(ham, 4, "abone")
print()
print("abone anahtarli bolum 0 :", len(bl[0]), "kayit , son konum", bl[0][-1]["konum"])
print("ilk uc kayit :", [(k["abone"], k["donem"], k["konum"]) for k in bl[0][:3]])
ab = bl[0][0]["abone"]
print(ab, "kayitlarinin bolumleri :",
      sorted(set(b for b in bl for k in bl[b] if k["abone"] == ab)))
kl = bolumle(ham, 4, "kayit")
print(ab, "kayit anahtarinda :", sorted(set(b for b in kl for k in kl[b] if k["abone"] == ab)))
```

```
anahtar  bolum  hesaplanan tuketim  sunum sapmasi
abone         4               2200   {'merkez': 0.0, 'kuzey': 0.0, 'dogu': 0.0, 'bati': 0.0, 'guney': 0.0}
kayit         4                527   {'merkez': -977.27, 'kuzey': -960.55, 'dogu': -686.91, 'bati': -616.0, 'guney': -801.1}
donem         2               1000   {'merkez': 16.15, 'kuzey': -7.35, 'dogu': 12.84, 'bati': -15.29, 'guney': 4.65}
donem         4                200   {'merkez': 180.55, 'kuzey': 57.45, 'dogu': 124.64, 'bati': -24.09, 'guney': 25.45}

abone anahtarli bolum 0 : 492 kayit , son konum 491
ilk uc kayit : [('A005', '2026-01', 0), ('A007', '2026-01', 1), ('A014', '2026-01', 2)]
A005 kayitlarinin bolumleri : [0]
A005 kayit anahtarinda : [0, 1, 2, 3]
```

Son dört satır düzeneği tek bir abone üzerinde gösteriyor. Abone anahtarında A005'in on iki
kaydının hepsi bölüm 0'dadır ve o bölümü okuyan tüketici on ikisini de sırayla görür. Kayıt
anahtarında aynı abonenin kayıtları dört bölüme birden dağılmıştır; hiçbir tüketici o abonenin
ardışık iki dönemini birlikte görmez. Bölümün içindeki konum kusursuz artmaya devam eder — bölüm 0
kendi 492 kaydını 0'dan 491'e sıralar. Sıra bozulmamıştır; **yanlış yerde korunmuştur**.

Konumun ikinci işi kursun kuralına dokunur. Bir tüketici grubu bölüm başına tek bir sayı tutar: o
bölümde nereye kadar okuduğunu. Grup düştüğünde ya da yeniden kurulduğunda okumaya o konumdan
devam eder ve konum geriye alındığında aynı kayıtlar **yeniden** okunur. Alma adımının yeniden
koşturulabilir olmasının dayanağı budur; çekmeli bir bağlayıcıda aynı şeyi yapabilmek için
kaynağın eski satırları hâlâ veriyor olması gerekir. Bölüm 0'ın son konumu 491 olduğuna göre o
bölümü baştan okumak 492 kaydı yeniden getirir ve hedef yazımı yinelemeye dayanıklı olduğu sürece
ikinci koşum ilkiyle aynı sonucu verir.

Buradan çıkan kural, en dengeli anahtarın en yanlış anahtar olabileceğidir. Bölüm anahtarı bir yük
dağıtma ayarı gibi görünür, oysa **tüketicinin hangi kayıtları birlikte görebileceğini** belirler.
Doğru anahtar, hesabın gruplama alanıdır: tüketim abone başına hesaplandığı için anahtar abone
olmalıdır. Bunun bedeli 1,44 katlık yük eğriliğidir ve 2200 satırın tamamı karşılığında ucuzdur.

#### Sunulan Sayının Yanıltması

Dönem anahtarının iki satırı dersin en sessiz sonucudur. İki bölümde tüketim 2200'den **1000
satıra** düşüyor, yani 1200 satır kayboluyor, ama sunum sapması **−15,29 ile +16,15** arasında
kalıyor: bin küsur birimlik değerler üzerinde yüzde ikiye bile varmayan bir oynama. Kayıt
anahtarının dört bölümlü kurulumu ise daha az satır kaybediyor gibi durmaz — 527 satırla daha
fazla kaybeder — ve sunumu **−616 ile −977** arasında yıkar.

Fark kayıp miktarında değil kaybın **şeklindedir**. Özet katmanı her bölge ve dönem çifti için
toplam alır, sunum katmanı bu toplamların dönem ortalamasını alır. Dönem anahtarı bütün bir dönemi
birden düşürür: o dönemin hücresi hiç oluşmaz ve ortalama kalan dönemler üzerinden alınır, her biri
kendi içinde doğrudur. Kayıt anahtarı ise her dönemin içinden rastgele satır düşürür: hücreler
oluşur ama eksik toplarlar ve ortalama topluca aşağı iner. Aynı düzeneğin daha büyük kaybı daha
küçük bir sapma üretebilir. Bir bölümleme kararının doğruluğu sunulan sayıya bakarak ölçülemez;
ölçüsü hesaplanan tüketim satırıdır.

#### Özet

- Bölüm sayısı aynı gruptaki etkin tüketici sayısının tavanıdır: tek bölümde dört tüketicinin üçü,
  iki bölümde ikisi boşta kalır; dört bölümde hepsi çalışır.
- Bölüm anahtarı bir yük dağıtma ayarı değil, tüketicinin hangi kayıtları birlikte görebileceğini
  belirleyen bir karardır. Bölüm kendi içinde sırayı korur, bölümler arasında sıra yoktur.
- Abone anahtarı hiçbir aboneyi dağıtmaz ve tüketimi her bölüm sayısında 2200 satırda tutar;
  bedeli 708 ile 492 arasında, yani 1,44 katlık bir yük eğriliğidir.
- En dengeli anahtar en yanlış anahtar olabilir: kayıt anahtarı yükü 621 ile 583 arasına sıkıştırır
  ama 200 abonenin hepsini dağıtır ve dört bölümde tüketimi 527 satıra indirir.
- Dönem anahtarı iki bölümde 2200 satırın 1.200'ünü kaybederken sunumu yalnız −15,29 ile +16,15
  arasında oynatır, çünkü tam dönem düşürmek ortalamayı bozmaz. Bölümleme kararının ölçüsü sunulan
  sayı değil hesaplanan tüketim satırıdır.

#### Sonraki Adım

Bu üç derste veri hatta **alındı**: yöntem seçildi, kaynağın kısıtı sayıldı, konu bölümlendi ve
tüketici grubuna dağıtıldı. Her üçünde de dönem kapalı bir kutu gibi ele alındı — bir dönemin
bütün kayıtları o dönem işlenirken elde sayıldı. Bir kayıt kendi dönemi kapandıktan **sonra**
gelebilir ve o zaman sonucu hangi pencerenin sahipleneceği bir karar hâline gelir. Sonraki ders
akış işlemeyi bu soruyla ölçer: pencere kararı geç gelen kayıtta sonucu nasıl değiştiriyor ve
pencereler arasında **durum taşımanın** bedeli nedir. Kalan derslerin ortak sorusu ise burada
hiç sorulmadı: veri hatta alınırken **kaybolup kaybolmadığı**.

### Akış İşleme

Source: https://academia.sh/tr/kurslar/veri-hatlari/akis-isleme

Pencere kararının ve durum taşımanın sayıya çevrilmesi: dönem penceresi kapandığı anda kapatıldığında bir dönem geciken 40 kayıt düşüyor ve 55 özet hücresinin 10'u kirleniyor, pencere bir dönem daha açık tutulduğunda düşen satır 0'a iniyor ama 2026-07 hücresi 2026-08'de yazılıyor ve taşınan durum 400 girdiden 600'e çıkıyor. Tolerans gecikmeye yapılan bir bahistir: aynı kayıtlar iki dönem geciktiğinde tolerans 1 sonucu tolerans 0 ile birebir aynı oluyor. Dokuz kurulumun hiçbirinde sunulan beş değer değişmiyor, yalnız kirli hücre sayısı değişiyor. Pencere hiç kapanmazsa durum 2400'e çıkıyor ve kip toplu işlemeye dönüyor; denetim noktası olmadan kaybedilen durumda 200 satır hesaplanamıyor, özet 50 hücreye düşüyor ve sunum her bölgede kayıyor.

Önceki üç derste veri hatta alındı: hangi yöntemle alınacağı, kaynağın kısıtı altında nasıl
alınacağı ve mesajların hangi bölümden geleceği yazıldı. Alınan satırın hatta **eksiksiz** varıp
varmadığı sorulmadı. Bu ders kaybın ilk biçimini ölçer ve o biçim taşıma hatası değildir: **kapanmış
bir pencereye geç gelen kayıttır**.

Toplu işlemede pencere sorusu yoktur, çünkü dönem kapandıktan sonra bakılır. Akış işlemede kaynak
bitmez; bir toplam almak için akışın bir yerinden kesilmesi gerekir. Kesme kararı üç şeyi birden
belirler: hangi kaydın hesaba girdiğini, sonucun hangi dönemde yazıldığını ve koşumun ne kadar
**durum** taşıdığını. Bu ders o üç sayıyı yan yana koyar.

- **VA41.** Kod **kurgudur**; gerçek bir akış işleyici, mesaj sistemi ya da orkestratör çağrılmaz.
  Kaynak M29'un kurgu sayaç okumalarıdır: 200 abone, 12 dönem, 2400 satır, tohum 20260218.
- **VA42. Toplu ile akış kipinin karşılaştırması tekrarlanmaz**; Veri Mühendisliğine Giriş
  kursundaki Toplu ve Akış İşleme dersinde ölçüldü. Buradaki taban çizgisi o dersin akış kipidir;
  eklenen tek şey **pencerenin ne kadar açık kaldığıdır**.
- **VA43.** Pencere bir dönemdir. **İlerleme damgası** (watermark) kapanmış sayılan en son dönemi
  gösterir; **tolerans**, pencerenin kapanmadan önce kaç dönem daha açık kaldığıdır.
- **VA44.** Geç gelen kayıt "hiç gelmedi" değil **bir dönem sonra geldi** sayılır: 2026-07
  döneminin kırk okuması 2026-08'de geliyor. Bozulma sınıfı ortak tanımın `gec_gelen` sınıfıdır.
- **VA45.** Taşınan durum iki parçadır: açık pencerelerde bekleyen satırlar ve abone başına son
  endeks. Ölçülen büyüklük ikisinin toplamının koşum boyunca gördüğü **en büyük değerdir**.
- **VA46.** Akış kipinde tüketim durumdan hesaplanır: satırın tüketimi, aynı abonenin durumda duran
  son endeksinden çıkarılır. Durumda karşılığı olmayan satır hesaplanamaz.
- **VA47.** Gecikme **dönem** olarak yazılır, ham süre yazılmaz. Özet hücresi oranının adımı 1/55,
  yani 0,0182; sunum üzerinde oran basılmaz, değerin kendisi yazılır.
- **VA48.** Denetim noktası, taşınan durumun koşum dışında saklanmasıdır. Yordamı bu derste
  yazılmaz; yalnız **kaybının bedeli** sayılır.

#### Pencere, Tolerans ve Durum

Aşağıdaki blok ortak tanımı kurar ve üzerine tek bir koşucu ekler. `akis_pencere` her dönemde o
dönemde **gelen** satırları alır, ilerleme damgasının kapattığı pencereye ait olanları düşürür,
kalanları açık pencerelerde bekletir ve sırası gelen pencereyi kapatıp özet hücrelerini yazar.

```python
# KURGUDUR. Gercek bir akis isleyici , mesaj sistemi ya da orkestrator cagrilmaz;
# olcum aginin sayac okumalari kurgudur , tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):                              # toplu kip: donem farkindan tuketim
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def gelisli(kayit, gecikme):
    """Her satira bir gelis donemi verir. gec_gelen sinifi: 2026-07 doneminin
    A001..A040 okumalari `gecikme` donem sonra geliyor."""
    veri = []
    for k in kayit:
        i = DONEM.index(k["donem"])
        g = gecikme if k["donem"] == "2026-07" and k["abone"] <= "A040" else 0
        veri.append(dict(k, gelis=DONEM[i + g]))
    return veri


def akis_pencere(veri, tolerans, sifirla=None):
    """Donem penceresi tolerans kadar donem daha acik kalir , sonra kapanir.
    sifirla: bu donemin basinda tasinan durum kaybolur (denetim noktasi yok)."""
    tampon, son_endeks, hucre, yazim = {}, {}, {}, {}
    dusen, durum, hesaplanamayan = 0, 0, 0

    def kapat(p, i):
        nonlocal hesaplanamayan
        v, _ = a1_ayikla(tampon.pop(p, []))
        v, _ = a2_tekille(v)
        c = []
        for k in v:
            if k["abone"] in son_endeks:
                c.append(dict(k, m3=k["endeks"] - son_endeks[k["abone"]]))
            elif DONEM.index(p) > 0:
                hesaplanamayan += 1
        for k in v:
            son_endeks[k["abone"]] = k["endeks"]    # durum: abone basina son endeks
        o, _ = a4_ozet(c)
        if o:
            hucre.update(o)
            yazim[p] = DONEM[i]

    for i, d in enumerate(DONEM):
        if d == sifirla:
            son_endeks.clear()
        for k in veri:
            if k["gelis"] != d:
                continue
            if DONEM.index(k["donem"]) < i - tolerans:
                dusen += 1                          # ilerleme damgasi pencereyi kapatti
                continue
            tampon.setdefault(k["donem"], []).append(k)
        durum = max(durum, sum(len(v) for v in tampon.values()) + len(son_endeks))
        if i - tolerans >= 0:
            kapat(DONEM[i - tolerans], i)
    for j in range(max(0, len(DONEM) - tolerans), len(DONEM)):
        kapat(DONEM[j], len(DONEM) - 1)
    return {"dusen": dusen, "durum": durum, "hucre": hucre, "yazim": yazim,
            "hesaplanamayan": hesaplanamayan}


ham = kaynak_uret()
v, _ = a1_ayikla(ham)
v, _ = a2_tekille(v)
v, _ = a3_tuketim(v)
TABAN, _ = a4_ozet(v)


def kirli(h):
    return sum(1 for ad in set(h) | set(TABAN) if h.get(ad) != TABAN.get(ad))


def fark(h):
    s, t = a5_sun(h)[0], a5_sun(TABAN)[0]
    d = {b: round(s[b] - t[b], 2) for b in t if round(s.get(b, 0) - t[b], 2)}
    return d if d else "bos"


print("taban: tuketim satiri", len(v), ", ozet hucresi", len(TABAN))
print("taban sunum", a5_sun(TABAN)[0])
print()
print("40 satirin gecikmesi  tolerans  dusen  durum  kirli/55  07 yazildi  sunum farki")
for gc in (0, 1, 2):
    for t in (0, 1, 2):
        r = akis_pencere(gelisli(ham, gc), t)
        print(f"{gc:>15} donem {t:>9} {r['dusen']:>6} {r['durum']:>6}"
              f" {kirli(r['hucre']):>9}  {r['yazim']['2026-07']:>9}  {fark(r['hucre'])}")
```

```
taban: tuketim satiri 2200 , ozet hucresi 55
taban sunum {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}

40 satirin gecikmesi  tolerans  dusen  durum  kirli/55  07 yazildi  sunum farki
              0 donem         0      0    400         0    2026-07  bos
              0 donem         1      0    600         0    2026-08  bos
              0 donem         2      0    800         0    2026-09  bos
              1 donem         0     40    400        10    2026-07  bos
              1 donem         1      0    600         0    2026-08  bos
              1 donem         2      0    800         0    2026-09  bos
              2 donem         0     40    400        10    2026-07  bos
              2 donem         1     40    600        10    2026-08  bos
              2 donem         2      0    800         0    2026-09  bos
```

#### Pencere Kararının Değiştirdiği Sonuç

İlk üç satır kararın **bedelini** kayıp olmadan gösterir. Hiçbir kayıt geç gelmese bile tolerans 1
taşınan durumu 400 girdiden 600'e, tolerans 2 800'e çıkarıyor ve 2026-07 hücresini bir ya da iki
dönem geç yazıyor. Bu bedel **her koşumda** ödenir; geç kayıt gelip gelmemesi onu değiştirmez. Bir
pencereyi açık tutmak, gelmemiş bir kayıt için yer ayırmaktır ve o kayıt gelmese de yer ayrılmıştır.

Ortadaki üç satır karşılığını veriyor. Kırk okuma bir dönem geç geldiğinde tolerans 0 onları
**düşürüyor**: pencere kapanmıştır ve ilerleme damgası geri alınmaz. Sonuç 55 özet hücresinin
**10'unun** kirlenmesidir — 2026-07 hücreleri eksik kalıyor, 2026-08 hücreleri ise şişiyor, çünkü o
kırk abonenin tüketimi iki dönemlik farktan hesaplanıyor. Bir pencerede düşen kayıt yalnız kendi
hücresini değil, **ardından gelen dönemin hücresini de** bozuyor; sebebi tüketimin durumdan
hesaplanmasıdır. Tolerans 1'de aynı kayıtlar hâlâ açık pencereyi buluyor: düşen satır **0**, kirli
hücre **0**.

Son üç satır toleransın ne olduğunu tanımlıyor: bir tahmindir. Aynı kırk kayıt iki dönem
geciktiğinde tolerans 1 onları da düşürüyor ve sonuç tolerans 0'la **birebir aynı** oluyor — 40
düşen satır, 10 kirli hücre. Tolerans, gecikmenin ne kadar olacağına yapılan bir bahistir; bahis
tutmadığında ödenen bedel geri alınmaz, üstelik doğru cevap alınamadan ödenmiştir. Tolerans 2 bu
kez kazanıyor, ama tolerans 2 satırlarının hepsinde durum 800 girdiye çıkmıştır.

Son sütun dersin uyarısıdır. Dokuz satırın hepsinde **sunum farkı boştur**. On kirli hücreye rağmen
sunulan beş bölge değeri tabanla birebir aynıdır, çünkü 2026-07'de eksilen 2026-08'de fazlalanıyor
ve on bir dönemin ortalaması oynamıyor. Pencere kararının yanlış olduğunu gösteren tek kanıt
**hücre düzeyindeki karşılaştırmadır**; çıktıya bakan hiçbir gözlem bu kararı sorgulayamaz. Doğru
cevabı veren satırların da bir bedeli var ve o bedel aynı tabloda duruyor: 2026-07 hücresi
toleransla birlikte bir ya da iki dönem geç yazılıyor. Pencereyi açık tutmak sonucu geciktirmektir.

#### Durumun İki Bedeli

Toleransı büyütmek doğruluğu bir yere kadar satın alır ve bunun sınırı vardır. Aşağıdaki bloğun ilk
tablosu sınırı gösteriyor: pencere hiç kapanmazsa ne oluyor. İkinci tablo durumun asıl niteliğini
ölçüyor — taşınan durum bir bellek büyüklüğü değil, hattın **doğru cevap üretebilmesinin
koşuludur**. Akış kipinde tüketim durumdaki son endeksten hesaplanır; durum kaybolursa satır
hesaplanamaz. İkinci tablo tolerans 1 kurulumunu iki kez koşturuyor: durum koşumlar arasında
saklanmışken ve 2026-08 başında kaybolmuşken.

```python
print("pencere            tolerans  durum  dusen  kirli/55  07 yazildi")
for ad, t in (("her donem kapanir", 1), ("hic kapanmaz", 11)):
    r = akis_pencere(gelisli(ham, 1), t)
    print(f"{ad:<18} {t:>8} {r['durum']:>6} {r['dusen']:>6} {kirli(r['hucre']):>9}"
          f"  {r['yazim']['2026-07']:>9}")

print()
print("durum         hesaplanamayan  hucre  kirli/55")
for ad, sf in (("saklandi", None), ("kayboldu", "2026-08")):
    r = akis_pencere(gelisli(ham, 1), 1, sifirla=sf)
    print(f"{ad:<14} {r['hesaplanamayan']:>13} {len(r['hucre']):>6} {kirli(r['hucre']):>9}")
    print("   sunum", a5_sun(r["hucre"])[0])
```

```
pencere            tolerans  durum  dusen  kirli/55  07 yazildi
her donem kapanir         1    600      0         0    2026-08
hic kapanmaz             11   2400      0         0    2026-12

durum         hesaplanamayan  hucre  kirli/55
saklandi                   0     55         0
   sunum {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
kayboldu                 200     50         5
   sunum {'merkez': 1325.6, 'kuzey': 1208.7, 'dogu': 873.7, 'bati': 835.2, 'guney': 1068.2}
```

İlk tablo toleransın üst sınırını veriyor. Pencere hiç kapanmazsa hiçbir kayıt düşmez ve hiçbir
hücre kirlenmez, ama taşınan durum **2400 girdiye**, yani kaynağın tamamına çıkar ve 2026-07 hücresi
son dönemde, 2026-12'de yazılır. Bu satır artık akış işleme değildir; bütün veriyi biriktirip sonda
bir kez işleyen bir toplu koşumdur. **Pencere, akış işlemeyi akış yapan şeydir**: kapanmayan bir
pencere kipin bütün tazelik kazancını geri verir.

İkinci tablo durumun kırılganlığını ölçüyor. Durum kaybolduğunda 2026-08 penceresindeki **200
satırın hiçbiri** hesaplanamıyor, çünkü hiçbir abonenin önceki endeksi elde yoktur. Özet 55 hücreden
**50'ye** iniyor: bir dönem hiç yazılmıyor. Bunun sunuma etkisi bu kez görünür — beş bölge değerinin
hepsi kayıyor, üstelik iki yönde birden, merkez yükselirken kuzey düşüyor. Sebebi bölge
ortalamasının artık on bir değil **on** dönem üzerinden alınmasıdır; düşen dönemin ortalamadan farkı
işareti belirler.

İki bedel yan yana konduğunda akış kipinin tuhaf bir dengesi görünür. Geç gelen kayıt sunumu hiç
oynatmadan on hücreyi bozdu; kaybolan durum sunumu oynattı ama beş hücreyi **eksik** bırakarak, yani
sayılabilir bir iz düşürerek. Eksik hücre bir denetim için sorulabilir bir sorudur, yanlış hücre
değildir. Durumun koşum dışında saklanması — **denetim noktası** (checkpoint) — bu ikinci bedeli
ortadan kaldırır, birincisini, yani pencere kararını kaldırmaz.

İki bedelin ortak yanı, ikisinin de **pencerenin bir sonucu** olmasıdır. Toplu koşumda böyle bir
soru yoktur: her koşum kaynağın tamamını yeniden okur, geç gelen satır bir sonraki koşumda
kendiliğinden hesaba girer ve taşınacak bir durum kalmaz. Akış kipi okunan satırı düşürdüğü için bu
bedava düzeltmeyi kaybeder; onun yerine iki karar bırakır — pencere ne kadar açık kalacak ve durum
nerede saklanacak. Bu iki kararın hiçbiri veriye bakılarak verilemez; **gecikmenin dağılımına** ve
koşumun kesilme sıklığına bakılarak verilir. Bir hattın sayısı bu yüzden yalnız kodundan okunmaz.

#### Özet

- Akış işlemede pencere kararı üç şeyi birden belirler: hangi kaydın hesaba girdiğini, sonucun hangi
  dönemde yazıldığını ve koşumun ne kadar durum taşıdığını.
- Tolerans 0'da bir dönem geciken 40 kayıt düşüyor ve 55 özet hücresinin 10'u kirleniyor; tolerans
  1'de düşen satır ve kirli hücre 0'a iniyor, bedeli 2026-07 hücresinin bir dönem geç yazılmasıdır.
- Tolerans gecikmeye yapılan bir bahistir: aynı kayıtlar iki dönem geciktiğinde tolerans 1 sonucu
  tolerans 0 ile birebir aynı oluyor, üstelik 600 girdilik durum ödenmiş olarak.
- Toleransın bedeli koşuma bağlı değildir: taşınan durum geç kayıt olsun olmasın 400, 600 ve 800
  girdidir; pencere hiç kapanmazsa durum 2400'e, yani kaynağın tamamına çıkar.
- Dokuz kurulumun hiçbirinde sunulan beş değer değişmiyor; on kirli hücrenin tek tanığı hücre
  düzeyindeki karşılaştırmadır.
- Denetim noktası olmadan kaybedilen durum 200 satırı hesaplanamaz kılıyor, özeti 55 hücreden 50'ye
  düşürüyor ve sunumu her bölgede oynatıyor.

#### Sonraki Adım

Bu derste kaybın kaynağı hattın kendi kararıydı: pencere kapandıktan sonra gelen kayıt bilerek
düşürüldü, kaç tanesinin düştüğü sayıldı ve karar değiştirildiğinde sayı sıfıra indi. Kayıt hatta
**ulaşmıştı**; yalnız geç ulaşmıştı. Bir kayıt hiç ulaşmazsa ya da iki kez ulaşırsa sayının ne
olacağı sorulmadı. Alma ile hattın arasındaki teslim, ders boyunca kusursuz varsayıldı: gönderilen
her satır tam olarak bir kez varmış gibi işlendi. Sonraki ders bu varsayımı kaldırır ve üç teslim
kipini yan yana koyar: satırın kaybolabildiği, iki kez varabildiği ve anahtarla yazıldığı kurulumlar.
Ölçülecek soru şudur: kayıp ile yinelenmeden hangisi sunulan sayıyı bozar, hangisi bir denetime
takılır ve ikisinin **onarılabilirliği** neden aynı değildir.

### Teslim Garantileri

Source: https://academia.sh/tr/kurslar/veri-hatlari/teslim-garantileri

Üç teslim kipinin aynı hat üzerinde sayıya çevrilmesi: en çok bir kez teslimde 2026-07 döneminin 19 satırı kayboluyor, tüketim 2200'den 2162'ye iniyor, 55 özet hücresinin 10'u kirleniyor ve sunum her bölgede kayıyor (−9,18 … −28,36); en az bir kez teslimde 19 satır iki kez varıyor ama tüketim 2200'de kalıyor ve sunum farkı boş çıkıyor, çünkü tekilleştirme aşaması yinelenmeyi yutuyor; tam bir kez teslimde anahtarlı yazım ikisini de sıfırlıyor. Asıl bulgu denetim sütunundadır: yinelenmeyi tekillik denetimi aşama 0'da yakalıyor, kayıp ise hiçbir denetime takılmıyor — satır oranı 0,0079 ile 0,05 eşiğinin, en büyük hücre sapması 0,2488 ile 0,25 eşiğinin altında kalıyor. Kayıp sessiz, yinelenme onarılabilirdir.

Önceki ders kaybın bir biçimini ölçtü, ama orada kayıt hatta **ulaşmıştı**: yalnız kapanmış bir
pencereye geç ulaşmış ve bir kararla düşürülmüştü. Bu ders varsayımı kaldırır: satır gönderiliyor
ve **hiç varmıyor**, ya da bir kez gönderiliyor ve **iki kez varıyor**.

Bu iki olay taşımanın kaçınılmaz iki ucudur. Gönderen onay almadığında ya vazgeçer ya tekrar dener;
vazgeçerse kayıp, tekrar denerse yinelenme olur. Taşımanın bu konuda verdiği söze **teslim
garantisi** (delivery guarantee) denir: **en çok bir kez** (at-most-once) satırın kaybolabildiği,
**en az bir kez** (at-least-once) iki kez varabildiği, **tam bir kez** (exactly-once) ikisinin de
olmadığı kiptir. Üçüncüsü çoğu zaman taşımada değil **yazmada** durur: satır bir anahtarla yazılır,
aynı anahtar ikinci kez geldiğinde yazım yapılmaz. Ders üç kipi aynı hat üzerinde koşturur ve sorar:
hangisi sunulan sayıyı bozar, hangisi bir denetime takılır.

- **VA49.** Kod **kurgudur**; hiçbir mesaj sistemi, akış işleyici ya da orkestratör çağrılmaz.
  Kaynak M29'un kurgu sayaç okumalarıdır: 200 abone, 12 dönem, 2400 satır, tohum 20260218.
- **VA50. Teslim semantiğinin yordamı tekrarlanmaz**; M16'nın Önbellekleme, Kuyruklar ve
  Eşzamansız İşleme kursunda ölçüldü. Eklenen tek şey kipin **hattın sunumuna** ne yaptığıdır.
- **VA51.** Kayıp ve yinelenme tek bir dönemde, 2026-07'de, satır başına 0,10 payla modellenir.
  Üreteç tohumludur; o dönemin 200 satırından 19'u etkilenir.
- **VA52.** Teslim kipi **taşımanın** özelliğidir; hattın beş aşaması üç kipte de aynıdır.
- **VA53. Tam bir kez** burada bir taşıma özelliği değil, **anahtarlı yazım** olarak modellenir:
  (abone, dönem) çifti daha önce yazılmışsa ikinci yazım hiç yapılmaz.
- **VA54.** Denetim kümesi ortak tanımdan alınır; satır sayısı payı 0,05, dağılım payı 0,25'tir.
- **VA55.** Hattın tekilleştirme aşaması bu derste bir **seçenektir**; kapatılınca yinelenmenin
  nereye gittiği ölçülür.
- **VA56.** Sayılan birimler satır, kayıp satır, yinelenen satır, kirli hücre ve sunum sapmasıdır.
  Satır oranının adımı 1/2400, yani 0,0004; sunum üzerinde oran basılmaz.

#### Üç Kip, Tek Hat

Aşağıdaki blok ortak tanımı kurar, üzerine `teslim` işlevini ve ortak denetim kümesini ekler.
`teslim` yalnız 2026-07 dönemine dokunur: satırı düşürür, kopyasını ekler ya da anahtarı görülmüş
satırı hiç yazmaz.

```python
# KURGUDUR. Gercek bir mesaj sistemi , akis isleyici ya da orkestrator cagrilmaz;
# olcum aginin sayac okumalari kurgudur , tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):                              # donem farkindan tuketim
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def teslim(kayitlar, kip, donem="2026-07", pay=0.10):
    """En cok bir kez: kayip. En az bir kez: yinelenme. Tam bir kez: anahtarli yazim."""
    r = uretec(TOHUM + 7)
    cikti, kayip, yineleme = [], 0, 0
    gorulen = set()
    for k in kayitlar:
        if k["donem"] != donem:
            cikti.append(k)
            continue
        if kip == "en_cok_bir":
            if r() < pay:
                kayip += 1
                continue
            cikti.append(k)
        elif kip == "en_az_bir":
            cikti.append(k)
            if r() < pay:
                cikti.append(dict(k))
                yineleme += 1
        else:                                       # tam_bir: anahtarli yazim
            ad = (k["abone"], k["donem"])
            if r() < pay and ad in gorulen:
                yineleme += 0
                continue
            gorulen.add(ad)
            cikti.append(k)
    return cikti, kayip, yineleme


def d_sema(veri, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in veri)


def d_tekillik(veri):
    ad = [(k["abone"], k["donem"]) for k in veri]
    return len(ad) != len(set(ad))


def d_satir_sayisi(veri, beklenen, pay=0.05):
    return abs(len(veri) - beklenen) / beklenen > pay


def d_bolum_tamligi_donem(veri):
    gor = set((k["bolge"], k["donem"]) for k in veri)
    return gor != set((b, d) for b in BOLGE for d in DONEM)


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


ham = kaynak_uret()


def kos(kayit, tekille=True):
    v, _ = a1_ayikla(kayit)
    if tekille:
        v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return len(v), oz, sun


N, TABAN, TSUN = kos(ham)


def kirli(oz):
    return sum(1 for ad in set(oz) | set(TABAN) if oz.get(ad) != TABAN.get(ad))


def fark(sun):
    d = {b: round(sun[b] - TSUN[b], 2) for b in TSUN if round(sun.get(b, 0) - TSUN[b], 2)}
    return d if d else "bos"


DENETIM = [("sema", lambda c, o: d_sema(c)), ("tekillik", lambda c, o: d_tekillik(c)),
           ("satir", lambda c, o: d_satir_sayisi(c, 2400)),
           ("bolum_donem", lambda c, o: d_bolum_tamligi_donem(c)),
           ("dagilim", lambda c, o: d_dagilim(o, TABAN))]

print("taban: satir 2400 , tuketim", N, ", ozet hucresi", len(TABAN))
print("taban sunum", TSUN)
print()
print("kip         satir  kayip  yineleme  tuketim  kirli/55  atesleyen denetim")
for kip in ("en_cok_bir", "en_az_bir", "tam_bir"):
    c, kayip, yin = teslim(ham, kip)
    n, oz, sun = kos(c)
    at = [ad for ad, f in DENETIM if f(c, oz)] or ["yok"]
    print(f"{kip:<11} {len(c):>5} {kayip:>6} {yin:>9} {n:>8} {kirli(oz):>9}  {','.join(at)}")
    print("   sunum farki", fark(sun))

print()
print("anahtarli yazimin tuttugu anahtar: donem basina",
      len({(k["abone"], k["donem"]) for k in ham if k["donem"] == "2026-07"}),
      ", tum kosum icin", len({(k["abone"], k["donem"]) for k in ham}))
```

```
taban: satir 2400 , tuketim 2200 , ozet hucresi 55
taban sunum {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}

kip         satir  kayip  yineleme  tuketim  kirli/55  atesleyen denetim
en_cok_bir   2381     19         0     2162        10  yok
   sunum farki {'merkez': -9.18, 'kuzey': -22.64, 'dogu': -9.81, 'bati': -28.36, 'guney': -20.28}
en_az_bir    2419      0        19     2200         0  tekillik
   sunum farki bos
tam_bir      2400      0         0     2200         0  yok
   sunum farki bos

anahtarli yazimin tuttugu anahtar: donem basina 200 , tum kosum icin 2400
```

#### Kaybın ve Yinelenmenin Simetrisizliği

Tablonun iki satırı birbirinin aynadaki görüntüsü: biri 19 satır eksiltiyor, öbürü 19 satır
çoğaltıyor. Sonuçları hiç benzemiyor.

**En çok bir kez** teslimde 2026-07 dönemine ait 19 satır hiç varmıyor. Tüketim satırı 2200'den
**2162'ye** iniyor, yani kayıp **38** satıra mal oluyor: eksik okuma kendi döneminin tüketimini
düşürdüğü gibi, önceki endeksi ortadan kaldırdığı için bir sonraki dönemin tüketimini de
hesaplanamaz kılıyor. 55 özet hücresinin **10'u** kirleniyor ve sunulan beş bölge değerinin
**hepsi** kayıyor: batıda −28,36, merkezde −9,18. Önceki derste de tam 10 hücre kirlenmiş ama sunum
hiç oynamamıştı; fark, orada temmuzda eksilenin ağustosta fazlalanması, burada **iki dönemin de
eksilmesidir**.

**En az bir kez** teslimde aynı 19 satır iki kez varıyor. Hattın tekilleştirme aşaması ikinci
kopyayı düşürüyor; tüketim **2200'de** kalıyor, kirli hücre **0**, sunum farkı **boş**.

**Tam bir kez** kipinde anahtarlı yazım aynı anahtarın ikinci yazımını hiç yapmıyor. Kaynakta her
(abone, dönem) çifti bir kez bulunduğu için çıktı 2400 satırda kalıyor ve sayılacak bir yinelenme
oluşmuyor — kipin yinelenme sayacı bu yüzden sıfırdır. Bu kip taşımanın değil **yazmanın**
özelliğidir; taşıma yine iki kez getirebilir, yazım bunu görünmez kılar.

Bedava olmadığı son satırda yazılı. Yazımın aynı anahtarı ikinci kez tanıyabilmesi için onu
**hatırlaması** gerekir: bir dönem için **200**, koşumun tamamı için **2400** anahtar. Tam bir kez
bir taşıma güvencesi değil, **durum tutmanın satın aldığı** bir güvencedir; tutulmayan anahtar için
garanti yoktur.

#### Kayıp Neden Sessiz

Asıl bulgu son sütundadır. Sunulan beş değeri her bölgede kaydıran kip **hiçbir denetime
takılmıyor**; hiçbir değeri değiştirmeyen kip **tekillik denetimini** ateşliyor. Aşağıdaki blok
kaybın hangi eşiklerin altından geçtiğini yazıyor.

```python
c, kayip, _ = teslim(ham, "en_cok_bir")
n, oz, sun = kos(c)
print("kayip", kayip, "satir ; satir orani",
      round(abs(len(c) - 2400) / 2400, 4), "< esik 0.05")
print("en buyuk ozet hucresi sapmasi",
      round(max(abs(oz[a] - TABAN[a]) / abs(TABAN[a]) for a in TABAN if a in oz), 4),
      "< esik 0.25")
print("bolge kumesi eksiksiz mi:", not d_bolum_tamligi_donem(c))
print("ayni denetim pay 0.005 ile:", d_satir_sayisi(c, 2400, pay=0.005))

print()
print("kayip payi  kayip  tuketim  en buyuk hucre sapmasi  satir orani  atesleyen")
for p in (0.02, 0.05, 0.10, 0.20, 1.00):
    c, kayip, _ = teslim(ham, "en_cok_bir", pay=p)
    n, oz, sun = kos(c)
    enh = round(max(abs(oz[a] - TABAN[a]) / abs(TABAN[a]) for a in TABAN if a in oz), 4)
    at = [ad for ad, f in DENETIM if f(c, oz)] or ["yok"]
    print(f"{p:<11} {kayip:>5} {n:>8} {enh:>23} {round(abs(len(c) - 2400) / 2400, 4):>12}"
          f"  {','.join(at)}")

print()
print("tekillestirme asamasi kapali:")
print("kip         tuketim  kirli/55  sunum farki")
for kip in ("en_cok_bir", "en_az_bir", "tam_bir"):
    c, _, _ = teslim(ham, kip)
    n, oz, sun = kos(c, tekille=False)
    print(f"{kip:<11} {n:>8} {kirli(oz):>9}  {fark(sun)}")
```

```
kayip 19 satir ; satir orani 0.0079 < esik 0.05
en buyuk ozet hucresi sapmasi 0.2488 < esik 0.25
bolge kumesi eksiksiz mi: True
ayni denetim pay 0.005 ile: True

kayip payi  kayip  tuketim  en buyuk hucre sapmasi  satir orani  atesleyen
0.02            4     2192                  0.1441       0.0017  yok
0.05           10     2180                  0.2094       0.0042  yok
0.1            19     2162                  0.2488       0.0079  yok
0.2            39     2122                  0.3953       0.0163  dagilim
1.0           200     1800                     0.0       0.0833  satir,bolum_donem

tekillestirme asamasi kapali:
kip         tuketim  kirli/55  sunum farki
en_cok_bir      2162        10  {'merkez': -9.18, 'kuzey': -22.64, 'dogu': -9.81, 'bati': -28.36, 'guney': -20.28}
en_az_bir       2219         5  {'merkez': 6.19, 'kuzey': 12.27, 'dogu': 3.64, 'bati': 18.36, 'guney': 6.45}
tam_bir         2200         0  bos
```

İlk dört satır sessizliğin nasıl kurulduğunu gösteriyor. Satır sayısı denetimi 2400 beklerken 2381
buluyor; oran **0,0079**, eşik 0,05, yani denetim eşiğin altı kat altında kalıyor. Dağılım denetimi
en çok bozulan hücrede **0,2488** sapma görüyor ve 0,25 eşiğinin **kıl payı** altında kalıyor.
Bölüm bütünlüğü hiçbir şey göremez, çünkü kayıp satırlar beş bölgeye dağılmıştır. Kayıp bir alan
silmez, bir tür bozmaz, bir anahtar çoğaltmaz; yalnız **daha az** satır bırakır — denetimler ise
satırın biçimine bakar. Kusur kodda değil **paydadır**: dördüncü satır aynı denetimin pay
0,005 ile ateşlediğini yazıyor. Kayıp ölçülemez değildir, ölçülmemiştir.

İkinci tablo kayıp payını süpürüyor ve sessizliğin bir nokta değil bir **bant** olduğunu gösteriyor.
Pay 0,02'den 0,20'ye çıkarken kaybolan satır 4'ten 39'a, en büyük hücre sapması 0,1441'den 0,3953'e
tırmanıyor: etki payla düzenli büyüyor, ama dağılım denetimi onu ancak **0,20'de** görüyor. Satır
sayısı denetimi bandın **hiçbir yerinde** ateşlemiyor; sebebi aritmetiktir, kayıp tek bir döneme
sıkışmıştır ve o dönem kaynağın 200/2400'ü, yani 0,0833'üdür.

Son satır dersin en aykırı bulgusudur. 2026-07 hiç varmadığında iki denetim **birden** ateşliyor ve
kalan hücrelerin sapması **0,00**'dır: 2026-07 ile 2026-08 hücreleri yanlış hesaplanmamış, hiç
yazılmamıştır ve geriye kalan 45 hücre kusursuzdur. **Tam kayıp kısmi kayıptan gürültülüdür.**

#### Onarımın Yeri ve Bedeli

Üçüncü tablo yinelenmenin neden zararsız kaldığını açıklıyor. Tekilleştirme kapatıldığında en az
bir kez teslim artık soğurulmuyor: tüketim **2219'a** çıkıyor, 5 hücre kirleniyor ve sunum her
bölgede **yukarı** kayıyor. Yinelenme kendiliğinden zararsız değildir; onu zararsız kılan aşamadır
ve bedeli yine bir anahtar kümesidir.

Aynı tablonun ilk satırı simetrisizliği tamamlıyor. Tekilleştirme kapalıyken en çok bir kez teslimin
sonucu **hiç değişmiyor**: 2162 satır, 10 kirli hücre, aynı sapmalar. Hattın hiçbir aşaması kaybı
onarmıyor, çünkü onaracak bir şey yok — kaybolan satır hiçbir yerde iz bırakmıyor. Yinelenmiş satır
ise iz bırakır: fazladan bir kayıttır, anahtarı vardır, sayılabilir ve silinebilir.

Dersin omurgası bu cümledir: **kayıp sessiz, yinelenme onarılabilirdir**. Yinelenme gürültülüdür;
hem bir denetimi ateşler hem de bir aşama tarafından geri alınabilir. Kayıp gürültüsüzdür ve geri
alınamaz; onu görmenin tek yolu **gönderilen satır sayısını bilmek**, yani hattın dışında bir sayaç
tutmaktır. En az bir kezin tercih edilme sebebi budur: yinelenmeyi kabul edip yazımı yinelemeye
dayanıklı kılmak, kaybı kabul edip onu aramaktan ucuzdur.

#### Özet

- En çok bir kez teslimde 2026-07 döneminin 19 satırı kayboluyor, tüketim 2200'den 2162'ye iniyor,
  55 hücrenin 10'u kirleniyor ve sunulan beş değerin hepsi kayıyor (−9,18 … −28,36).
- En az bir kez teslimde 19 satır iki kez varıyor, tekilleştirme aşaması ikinci kopyayı düşürüyor
  ve tüketim, kirli hücre ile sunum farkı taban değerinde kalıyor.
- Kaybı hiçbir denetim yakalamıyor: satır oranı 0,0079 ile 0,05 payının, en büyük hücre sapması
  0,2488 ile 0,25 payının altında kalıyor. Yinelenmeyi tekillik denetimi hattın girişinde yakalıyor.
- Kayıp payı süpürüldüğünde sessizlik bir bant çıkıyor: dağılım denetimi ancak 0,20'de ateşliyor,
  satır sayısı denetimi hiçbir payda ateşlemiyor, çünkü bir dönem kaynağın yalnız 0,0833'üdür.
  Dönemin tamamı kaybolduğunda iki denetim birden ateşliyor — tam kayıp kısmi kayıptan gürültülüdür.
- Tekilleştirme kapatıldığında yinelenme zararsız olmaktan çıkıyor (tüketim 2219, sunum her bölgede
  yukarı); kaybın sonucu hiç değişmiyor. Tam bir kezin bedeli de aynı yerdedir: dönem başına 200,
  koşum başına 2400 anahtar.
- Kayıp sessiz, yinelenme onarılabilirdir: yinelenmenin izi vardır ve silinebilir, kaybın izi yoktur
  ve ancak hattın dışında tutulan bir sayaçla görülebilir.

#### Sonraki Adım

Beş derste veri hep **tek yönde** aktı: kaynak sistemden hatta, hattan özete, özetten sunuma. Her
ölçü bu yönü varsayıyordu — alma yöntemi kaynağı okuyor, teslim kipi satırı taşıyor, pencere kararı
onu bir dönemde topluyordu. Bir soru sorulmadı: hattın ürettiği değer **geri döndüğünde** ne olur.
Sunum katmanındaki bir sayı çoğu kez orada kalmaz; operasyonel bir sisteme ya da sahada okuma yapan
bir uygulamaya yazılır, çünkü asıl işini orada yapar. O yazım hattın kendi girdisine dokunduğu anda
ok bir çembere kapanır. Sonraki ders çemberi ölçer: geri yazılan değer kaç aşama sonra kendi
girdisine döner ve bir sonraki koşumda sayıyı ne yapar.

### Ters ETL

Source: https://academia.sh/tr/kurslar/veri-hatlari/ters-etl

İşlenmiş verinin operasyonel sisteme geri yazılmasının açtığı çemberin sayılması: sunum katmanında hesaplanan bölge eşiği geri yazıldığında zincir al, ayikla, tekille, tuketim, ozet, sun ve geri yazım üzerinden yedi aşama sonra kendi girdisine dönüyor. Geri yazılan değerin bir sonraki koşumda yaptığı ölçülüyor: birinci koşumun yazdığı eşik ikinci koşumda 1129 okumayı kırpıyor, sunum her koşumda kayıyor (koşumlar arası en büyük fark 47,00, sonra 10,90 ve 14,36) ve döngü dördüncü koşumda duruyor. Durduğu yer taban değildir: tüketim satırı 2200'de kalırken 55 özet hücresinin 55'i kirleniyor, sunum −19,27 … −59,46 sapıyor ve en büyük hücre sapması 0,1788 ile 0,25 eşiğinin altında kaldığı için hiçbir denetim ateşlemiyor. Geri yazılan değer hattın girdisinden ayrı tutulduğunda her koşum aynı sonucu veriyor.

Önceki beş derste veri hep tek yönde aktı: kaynak sistemden hatta, hattan özete, özetten sunuma. Bu
yön bütün ölçülerin sessiz varsayımıydı. Oysa sunum katmanındaki bir sayı çoğu kez orada kalmaz.
Bölge başına beklenen tüketim, bir abonenin risk sınıfı ya da bir bölgenin eşiği, asıl işini bir
gösterge tablosunda değil **sahada** yapar: okuma yapan uygulama onu kullanır, faturalama onu
uygular, uyarı düzeneği ona bakar. İşlenmiş verinin operasyonel sisteme geri yazılmasına **ters
ETL** denir ve bu bir hattın en meşru çıktılarından biridir.

Meşru olması zararsız olduğu anlamına gelmiyor. Geri yazılan değer, operasyonel sistemin
davranışını değiştirirse, o sistemin ürettiği kayıt da değişir; o kayıt hattın **girdisidir**. Ok
bu noktada bir çembere kapanır. Bu ders çemberi iki sayıyla ölçer: kaç aşama sonra kapandığı ve
kapandıktan sonra bir sonraki koşumun ne verdiği.

- **VA57.** Kod **kurgudur**; hiçbir operasyonel sistem, bulut servisi, satıcı ya da orkestratör
  çağrılmaz. Kaynak M29'un kurgu sayaç okumalarıdır: 200 abone, 12 dönem, 2400 satır, tohum
  20260218.
- **VA58.** Ters ETL tek bir değerle modellenir: **bölge başına beklenen tüketim eşiği**. Sunum
  katmanında hesaplanır, operasyonel sisteme yazılır ve katsayısı 1,2'dir.
- **VA59.** Operasyonel sistemin o eşikle yaptığı şey kurgudur ama sıradandır: saha uygulaması
  eşiği aşan artışı **kaydetmez**, önceki endeksin üstüne eşik kadar ekler ve okumayı doğrulamaya
  bırakır. Kaydedilen değer budur.
- **VA60.** Zincir bir çizge olarak yazılır. **Çizge algoritması tekrarlanmaz**; Veri Yapıları
  kursundaki çizge konusunda ölçüldü. Burada zincir yalnız **yürünür** ve uzunluğu sayılır.
- **VA61.** Koşumlar arasında kaynağın kendisi değişmez. Değişen tek şey operasyonel sistemin
  **kaydettiği** endekstir; gerçek sayaç okumaları sabittir.
- **VA62.** Denetim eşikleri öncekiyle aynıdır: satır sayısı payı 0,05, dağılım payı 0,25.
- **VA63.** Sayılan birimler çevrimdeki aşama, kırpılan okuma, tüketim satırı, kirli özet hücresi,
  sunum sapması ve **koşumlar arası fark**tır. Süre yazılmaz.
- **VA64.** Kursun kuralı bu derste doğrudan sınanır: bir hattın sayısı, yeniden koşturulduğunda
  **aynı sonucu vermesidir**.

#### Çemberin Uzunluğu

Aşağıdaki blok ortak tanımı kurar, üzerine üç şey ekler: zinciri yürüyen `cevrim`, operasyonel
sistemin kaydını üreten `al` ve sunum katmanından eşiği yazan `geri_yaz`. Sonra hattı beş kez
koşturur; her koşum bir öncekinin yazdığı eşikle başlar.

```python
# KURGUDUR. Gercek bir operasyonel sistem , bulut servisi ya da orkestrator
# cagrilmaz; olcum aginin sayac okumalari kurgudur , tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):                              # donem farkindan tuketim
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ZINCIR = {"al": "ayikla", "ayikla": "tekille", "tekille": "tuketim",
          "tuketim": "ozet", "ozet": "sun", "sun": "geri_yaz", "geri_yaz": "al"}


def cevrim(bas):
    """Zinciri yurur ve baslangica donene kadar gecilen asamalari toplar."""
    yol, u = [bas], ZINCIR[bas]
    while u != bas:
        yol.append(u)
        u = ZINCIR[u]
    return yol


def al(ham, esik):
    """KURGU operasyonel sistem: esigi asan artisi kaydetmez , esik kadar ekler."""
    kayit, son, kirpilan = [], {}, 0
    for k in ham:
        e, onceki, s = k["endeks"], son.get(k["abone"]), esik.get(k["bolge"])
        if onceki is not None and s is not None and e - onceki > s:
            e = onceki + s
            kirpilan += 1
        son[k["abone"]] = e
        kayit.append(dict(k, endeks=e))
    return kayit, kirpilan


def hat(kayit):
    v, _ = a1_ayikla(kayit)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return v, oz, sun


def geri_yaz(v, kat=1.2):
    """Ters ETL: bolge basina beklenen tuketim esigi operasyonel sisteme yazilir."""
    top, say = {}, {}
    for k in v:
        top[k["bolge"]] = top.get(k["bolge"], 0) + k["m3"]
        say[k["bolge"]] = say.get(k["bolge"], 0) + 1
    return {b: int(round(kat * top[b] / say[b])) for b in top}


ham = kaynak_uret()
yol = cevrim("al")
_, TABAN, TSUN = hat(al(ham, {})[0])
print("cevrim:", " -> ".join(yol), "-> al")
print("cevrimdeki asama sayisi", len(yol), ", taban tuketim 2200 , ozet hucresi", len(TABAN))
print()
print("kosum  kirpilan  esik dogu   merkez    kuzey     dogu     bati    guney  onceki farki")
esik, onceki = {}, None
for i in range(1, 6):
    kayit, kirp = al(ham, esik)
    v, oz, sun = hat(kayit)
    f = "-" if onceki is None else max(abs(round(sun[b] - onceki[b], 2)) for b in sun)
    print(f"{i:>5} {kirp:>9} {esik.get('dogu', 0):>10}  " +
          " ".join(f"{sun[b]:>8}" for b in ("merkez", "kuzey", "dogu", "bati", "guney")) +
          f" {f:>12}")
    onceki, esik = sun, geri_yaz(v)
```

```
cevrim: al -> ayikla -> tekille -> tuketim -> ozet -> sun -> geri_yaz -> al
cevrimdeki asama sayisi 7 , taban tuketim 2200 , ozet hucresi 55

kosum  kirpilan  esik dogu   merkez    kuzey     dogu     bati    guney  onceki farki
    1         0          0   1321.45  1212.55   881.36   833.09  1065.55            -
    2      1129         31   1274.45  1177.73   853.45   813.82  1033.09         47.0
    3      1207         30   1263.55  1167.45   845.55   813.82  1025.36         10.9
    4      1225         30   1263.55  1153.09   845.55   813.82  1025.36        14.36
    5      1225         30   1263.55  1153.09   845.55   813.82  1025.36          0.0
```

#### Yedi Aşama Sonra Kendi Girdisi

İlk satır çemberi veriyor. Zincir `al` düğümünden çıkıyor, hattın beş aşamasını geçiyor, ters ETL
yazımına giriyor ve oradan **yeniden `al`'a** dönüyor: **yedi aşama**. Bu sayının kendisi bir uyarı
değildir; uyarı olan, zincirin artık bir **çizge çevrimi** olmasıdır. Ters ETL eklenmeden önce
zincir altı düğüm ve beş kenardı, başı ile sonu belliydi. Yedinci kenar eklendiği anda hiçbir düğüm
"önce çalışır" olmaktan çıkıyor; her düğümün bir öncülü var.

Bunun pratikteki karşılığı, hattın artık **koşumdan bağımsız** bir tanımının olmamasıdır. "Hattın
çıktısı nedir" sorusunun cevabı bir sayı değil, bir **koşum numarasıdır**. Tablonun son sütunu bunu
sayıyor: ikinci koşum birinciden 47,00, üçüncü ikinciden 10,90, dördüncü üçüncüden 14,36 sapıyor.
Aynı kaynakla, aynı kodla, aynı tohumla koşan hat üç kez üç ayrı cevap veriyor.

Mekanizma orta sütunlarda okunuyor. Birinci koşum eşiksiz koşuyor ve doğru cevabı veriyor; sonunda
doğu bölgesi için 31 eşiğini operasyonel sisteme yazıyor. İkinci koşumda saha uygulaması bu eşiği
kullanıyor ve **1129 okumayı** kırpıyor: eşiği aşan artışlar kaydedilmiyor. Kırpılmış okumalarla
hesaplanan ortalama düşüyor, düşen ortalama daha küçük bir eşik üretiyor, küçülen eşik üçüncü
koşumda **1207** okumayı kırpıyor. Döngü kendi kendini besliyor ve her turda girdiyi biraz daha
daraltıyor.

Yedinci kenarın nerede durduğuna dikkat etmek gerekir: hattın kodunda değil. Hat yalnız bir değer
yazar ve orada biter; kırpma kararı operasyonel sistemin içindedir. Çevrim iki sistemin arasında
kurulur ve **hiçbirinin kodunda görünmez**. Hattın çizgesine bakan biri altı düğümlü, düzgün bir
zincir görür; saha uygulamasına bakan biri makul bir doğrulama kuralı görür. Çemberi ancak ikisini
aynı anda çizen biri görebilir.

#### Döngünün Durduğu Yer

Beşinci satır döngünün durduğunu söylüyor: kırpılan okuma 1225'te sabitleniyor ve koşumlar arası
fark **0,00** oluyor. Dördüncü koşumdan sonra hat yeniden koşturulduğunda **aynı sonucu veriyor**.
Aşağıdaki blok durulan koşumun ne olduğunu ölçüyor ve aynı hattı geri yazımın girdiye dokunmadığı
kurulumda koşturuyor.

```python
kayit, kirp = al(ham, esik)
v, oz, sun = hat(kayit)
print("durulan kosum: tuketim", len(v), ", kirli hucre",
      sum(1 for a in set(oz) | set(TABAN) if oz.get(a) != TABAN.get(a)))
print("tabandan sunum sapmasi", {b: round(sun[b] - TSUN[b], 2) for b in TSUN})
print("en buyuk ozet hucresi sapmasi",
      round(max(abs(oz[a] - TABAN[a]) / abs(TABAN[a]) for a in TABAN), 4), "< esik 0.25")

print()
print("geri yazilan esik girdiye dokunmuyor:")
print("kosum  kirpilan   merkez    kuzey     dogu     bati    guney  onceki farki")
esik, onceki = {}, None
for i in range(1, 4):
    kayit, kirp = al(ham, {})
    v, oz, sun = hat(kayit)
    f = "-" if onceki is None else max(abs(round(sun[b] - onceki[b], 2)) for b in sun)
    print(f"{i:>5} {kirp:>9}  " +
          " ".join(f"{sun[b]:>8}" for b in ("merkez", "kuzey", "dogu", "bati", "guney")) +
          f" {f:>12}")
    onceki, esik = sun, geri_yaz(v)
print("operasyonel sisteme yazilan esik", esik)
```

```
durulan kosum: tuketim 2200 , kirli hucre 55
tabandan sunum sapmasi {'merkez': -57.9, 'kuzey': -59.46, 'dogu': -35.81, 'bati': -19.27, 'guney': -40.19}
en buyuk ozet hucresi sapmasi 0.1788 < esik 0.25

geri yazilan esik girdiye dokunmuyor:
kosum  kirpilan   merkez    kuzey     dogu     bati    guney  onceki farki
    1         0   1321.45  1212.55   881.36   833.09  1065.55            -
    2         0   1321.45  1212.55   881.36   833.09  1065.55          0.0
    3         0   1321.45  1212.55   881.36   833.09  1065.55          0.0
operasyonel sisteme yazilan esik {'merkez': 32, 'kuzey': 32, 'dogu': 31, 'bati': 32, 'guney': 32}
```

İlk üç satır durulan koşumun ne olduğunu söylüyor ve cevap rahatsız edicidir. Tüketim satırı
**2200**, yani taban değeriyle birebir aynı; hiçbir satır düşmemiş, hiçbir satır çoğalmamıştır.
Buna karşılık 55 özet hücresinin **55'i** kirlidir: her bölgenin her dönemi yanlış hesaplanmıştır.
Sunum −19,27 ile −59,46 arasında sapmıştır. Ve hiçbir denetim ateşlemez: satır sayısı tamdır,
şema tamdır, anahtarlar tekildir, her bölge her dönemde vardır ve en büyük hücre sapması **0,1788**
ile 0,25 payının altındadır.

Bu tablo kursun kuralının nasıl okunması gerektiğini de düzeltiyor. Dördüncü koşumdan sonra hat
yeniden koşturulduğunda aynı sonucu veriyor, yani kural sağlanıyor — ama sonuç yanlış. Kural bir
**gerek** koşuldur, yeter koşul değil: yeniden koşturulamayan hat ölçülmemiştir, yeniden
koşturulabilen hat ise ancak **kendi çıktısını yeniden okumuyorsa** ölçülmüştür. Döngülü hatta
kararlılık, doğruluğun değil **yakınsamanın** işaretidir; hat kendi ürettiği kısıtla dengeye
gelmiştir.

Bu bozulmayı görebilecek tek denetim, birinci tablonun son sütununda zaten duruyor: aynı dönemin
sunum değerini bir önceki koşumunkiyle karşılaştıran bir denetim. O denetim ikinci, üçüncü ve
dördüncü koşumda ateşlerdi, çünkü sırasıyla 47,00, 10,90 ve 14,36 fark görürdü. Beşinci koşumdan
sonra ise **susardı**. Yani koşum karşılaştırması bir döngüyü ancak **oturmadan önce** yakalar;
oturduktan sonra hattın bütün göstergeleri yeşildir ve sunulan değer kalıcı olarak yanlıştır. Bir
döngü, yakalanma penceresi kendi kendine kapanan tek bozulma biçimidir.

İkinci tablo çözümü gösteriyor ve çözüm ters ETL'den vazgeçmek değildir. Eşik yine hesaplanıyor,
yine operasyonel sisteme yazılıyor — merkez 32, kuzey 32, doğu 31, batı 32, güney 32 — ama saha
uygulaması onu **kaydı değiştirmek için** değil yalnız doğrulamaya çağırmak için kullanıyor. Ham
okuma olduğu gibi saklanıyor. Kırpılan okuma **0**, koşumlar arası fark **0,00** ve üç koşum da
tabanı veriyor. Ters ETL bir çıktıdır; hattın girdisi olduğu anda çıktı olmaktan çıkar. Ayrımı
kuran şey değerin yazılıp yazılmaması değil, **geri yazılan alanın hattın okuduğu alan olup
olmamasıdır**.

#### Özet

- Ters ETL, işlenmiş verinin operasyonel sisteme geri yazılmasıdır ve geri yazılan değer o sistemin
  ürettiği kaydı etkiliyorsa hattın zinciri çembere kapanır: `al` düğümünden çıkan yol yedi aşama
  sonra kendi girdisine döner.
- Birinci koşumun yazdığı eşik ikinci koşumda 1129, üçüncüde 1207, dördüncüde 1225 okumayı kırpıyor;
  aynı kaynak ve aynı kodla koşumlar arası fark 47,00, 10,90 ve 14,36 çıkıyor.
- Döngü dördüncü koşumda duruyor ama durduğu yer taban değildir: tüketim satırı 2200'de kalırken
  55 özet hücresinin 55'i kirleniyor ve sunum −19,27 … −59,46 sapıyor.
- Bu bozulmayı hiçbir denetim yakalamıyor; satır sayısı, şema, tekillik ve bölüm bütünlüğü tamdır
  ve en büyük hücre sapması 0,1788 ile 0,25 payının altında kalır.
- Koşumları karşılaştıran bir denetim döngüyü ikinci, üçüncü ve dördüncü koşumda görürdü; beşinciden
  sonra susardı. Bir döngünün yakalanma penceresi kendi kendine kapanır.
- Aynı eşik hesaplanıp yazıldığı hâlde ham kayda dokunmadığında kırpılan okuma 0, koşumlar arası
  fark 0,00 olur: belirleyici olan değerin yazılması değil, hattın kendi çıktısını okuyup okumadığıdır.

#### Sonraki Adım

Altı derste veri hatta **alındı**. Alma yönteminin taşıdığı satır ile kaçırdığı değişiklik sayıldı,
kaynağın kısıtı altında tamamlanan çağrı ölçüldü, mesajın hangi bölümden geleceği ve bölüm sayısının
sıraya ne yaptığı yazıldı, pencere kararının geç gelen kayıtta değiştirdiği sonuç ile durum taşımanın
bedeli çıkarıldı, üç teslim kipinin kaybı ve yinelenmesi ayrı ayrı sayıldı, geri yazımın açtığı
çember ölçüldü. Hepsinin ortak yanı şudur: **satır hatta girdi**. Girdikten sonra ne hâle getirildiği
hiç sorulmadı. Bütün bu derslerde hattın beş aşaması hazır bir kutu gibi çağrıldı; hangi dönüşümün
nerede yapılacağı, aynı hesabın kaynakta mı hedefte mi yürütüleceği, ham satırın hangi katmana kadar
saklanacağı ve bir dönüşümün **doğru** olup olmadığının nasıl yazılacağı tanımlanmadı. Sonraki konu
bunu yapar: dönüştürmenin yerini seçer, katmanları ayırır ve her katmanın hangi bozulmayı
durdurabildiğini sayar.

## Dönüştürme

Dönüştürmenin nerede, hangi katmanda ve hangi güvenceyle yapıldığının sayılması: dönüştürmenin kaynakta mı hedefte mi yapıldığının taşınan satıra ve okunan hücreye etkisi, sorgu tabanlı modellemede bağımlılık çözümünün sıraladığı görev ile döngü kaçırıldığında olan, ham, arıtılmış ve sunum katmanlarının her birinin sakladığı satır ve bir bozulmanın hangi katmanda durdurulabildiği, veri testlerinin ve beklenti tanımlarının yakaladığı bozulma sınıfı ile hiçbirinin yakalamadığı kalıntı, kural değişikliğinin yalnız iki döneme uygulanmasının raporu tam uygulamadan 16,98 ile 29,57 arasında saptırması ve iki koşumun da kendi içinde tutarlı görünerek hiçbir denetime takılmaması.

### ETL ve ELT

Source: https://academia.sh/tr/kurslar/veri-hatlari/etl-ve-elt

Dönüştürmenin kaynakta mı hedefte mi koşacağının sayılması: sınır kaynağa alındığında hedefe taşınan satır 2400'den 55'e, taşınan hücre 14400'den 165'e iniyor, ama üç kurulumun da okuduğu toplam hücre 33110'da sabit kalıyor ve yalnızca okuyanın kim olduğu değişiyor; karma sınır taşınan satırı 2200'e düşürürken taşınan hücreyi 15400'e çıkarıyor; bir raporlama kuralı değiştiğinde hedefte yeniden hesaplanabilen kural sayısı ham veriyi saklayan kurulumda 3, karma kurulumda 1, kaynakta dönüştüren kurulumda 0 oluyor ve son kurulum kaynağa dönmek zorunda kalıp yalnızca üç dönemi geri hesaplayabiliyor.

Bu kursun ilk altı dersi veriyi hatta **aldı**: kaynaktan çekildi, sıraya girdi, teslim edildi ve
işlenmiş hâliyle geri döndü. Hepsinin ortak varsayımı tek cümleyle yazılabilir: veri bir yerden
alınır ve bir yere konur. Hiçbirinde verinin **ne hâle getirildiği** sorulmadı.

Bu ders o soruyu ilk kararla açar: dönüştürme **nerede** koşacak. Kaynağın yanında mı, yoksa veri
hedefe taşındıktan sonra orada mı. İki yanıt iki kalıbın adıdır ve aralarındaki fark bir takastır:
kaynakta dönüştürmek **taşınan satırı** düşürür, hedefte dönüştürmek **ham veriyi korur** ve
yeniden hesaplamayı mümkün kılar. Bu derste ikisi yan yana koşar.

- **DN1.** Kaynak, hat ve bölünme **kurgudur**; gerçek bir depo, sorgu motoru ya da dönüştürme
  çerçevesi çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **DN2.** **ETL** çıkar–dönüştür–yükle, **ELT** çıkar–yükle–dönüştür demektir. İkisi de birer
  kalıp adıdır, ürün adı değildir; aralarındaki tek fark dönüşümün hangi tarafta koştuğudur.
- **DN3.** Hattın beş aşaması tek bir **sınırla** ikiye ayrılır. Sınırdan önceki aşamalar kaynak
  tarafında, kalanlar hedefte koşar. ETL sınırı **4**, ELT sınırı **0**'dır.
- **DN4.** Okunan hücre bir modeldir: her aşama, girdisindeki her satırın bildirilen sayıda alanını
  okur. Sayı bir donanım ölçümü değil, aşamanın **iş yükünün** ölçüsüdür.
- **DN5.** Taban çizgisi **bölünmemiş hattır**: sınır kararı yok, yeniden hesaplama sorulmamış.
- **DN6.** Kaynak operasyonel bir sistemdir ve yalnızca **son üç dönemi** saklar; hedef on iki
  dönemin tamamını saklar.
- **DN7.** Depolama düzeni, bölümleme ve maliyet Veri Depolama Mimarileri kursunda ölçüldü ve
  **tekrarlanmaz**. Buradaki soru satırın nerede durduğu değil, dönüşümün nerede koştuğudur.
- **DN8.** Çözünürlük: satır oranının adımı **0,0004**. **Sunum üzerinde oran basılmaz**, değerin
  kendisi yazılır.

#### Sınır Nerede Duruyor

Hattın beş aşaması sabittir: ayıklama, tekilleştirme, tüketim hesabı, özet ve sunum. Değişen tek
şey bu zincirin **nerede kesildiğidir**. Sınır sıfırdaysa hiçbir aşama kaynakta koşmaz, ham satırlar
olduğu gibi hedefe taşınır ve beş aşamanın tamamı orada işler. Sınır dörtteyse dört aşama kaynağın
yanında koşar, hedefe yalnızca özet tablosu gider.

Sınırın seçilmesi bir mimari tercih değil, iki büyüklük arasında yapılan bir takastır. Aşağıdaki kod
o iki büyüklüğü sayar: sınırdan geçen satır ve her iki tarafın okuduğu hücre.

```python
# KURGUDUR. Sayac okumalari, bes asamali hat ve bolunme ders icinde tanimlanir;
# gercek bir depo, sorgu motoru ya da donusturme cercevesi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- HAT: bes asama, her biri kendi dusurdugu satiri sayar
def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]

# ---- Okunan hucre: her asama girdisindeki her satirin su kadar alanini okur.
OKUNAN_ALAN = {"ayikla": 5, "tekille": 2, "tuketim": 4, "ozet": 3, "sun": 2}


def hucre(veri):
    """Ozet sozlugu hucre basina uc deger tasir , kayit ise kendi alanlarini."""
    if isinstance(veri, dict):
        return len(veri) * 3
    return sum(len(k) for k in veri)


def bolunmus_hat(kayit, sinir):
    """Ilk `sinir` asama kaynak tarafinda , kalani hedefte kosar.
    Sinirdan gecen sey `sinir` asamasinin ciktisidir."""
    okunan, veri, tasinan = {"kaynak": 0, "hedef": 0}, kayit, kayit
    for i, (ad, f) in enumerate(ASAMA):
        okunan["kaynak" if i < sinir else "hedef"] += len(veri) * OKUNAN_ALAN[ad]
        veri, _ = f(veri)
        if i + 1 == sinir:
            tasinan = veri
    return veri, okunan, tasinan
```

#### Taşınan Satır ve Okunan Hücre

İkinci blok aynı kaynağı üç sınırla koşturur. `tasinan` sınırdan geçen satırı, `tas.hucre` o
satırların taşıdığı alan sayısını, `kaynak` ile `hedef` iki tarafın okuduğu hücreyi verir. Son sütun
sunulan beş değerin tabanla aynı kalıp kalmadığını yazar.

```python
KAYNAK_KAYIT = kaynak_uret()
KURULUM = [("hedefte (ELT)", 0), ("karma", 3), ("kaynakta (ETL)", 4)]
TABAN, _, _ = bolunmus_hat(KAYNAK_KAYIT, 0)

print("taban sunum:", TABAN)
print(f"{'kurulum':<16}{'sinir':>6}{'tasinan':>9}{'tas.hucre':>11}"
      f"{'kaynak':>8}{'hedef':>7}{'sunum':>8}")
for ad, n in KURULUM:
    sunum, ok, tas = bolunmus_hat(KAYNAK_KAYIT, n)
    print(f"{ad:<16}{n:>6}{len(tas):>9}{hucre(tas):>11}{ok['kaynak']:>8}{ok['hedef']:>7}"
          f"{'ayni' if sunum == TABAN else 'degisti':>8}")
```

```
taban sunum: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
kurulum          sinir  tasinan  tas.hucre  kaynak  hedef   sunum
hedefte (ELT)        0     2400      14400       0  33110    ayni
karma                3     2200      15400   26400   6710    ayni
kaynakta (ETL)       4       55        165   33000    110    ayni
```

Son sütun üç kurulumda da **aynı**dır: sınır nereye konursa konsun sunulan beş değer birebir aynı
çıkıyor. Kararın çıktı üzerinde hiçbir etkisi yok; bütün etkisi çıktının **nasıl** üretildiğinde.

İlk iki sütun beklenen takası verir. Kaynakta dönüştüren kurulum hedefe **55 satır** ve **165 hücre**
taşıyor; hedefte dönüştüren kurulum **2400 satır** ve **14400 hücre**. Taşınan satırdaki oran
43,6'dır ve bu, ağ üzerinden taşınan verinin ölçülebilir tek büyüklüğüdür.

Karma satır bu takasın düz bir doğru olmadığını gösterir. Sınır üçe alındığında taşınan satır
2400'den **2200**'e iniyor, ama taşınan hücre 14400'den **15400**'e **çıkıyor**. Nedeni tüketim
aşamasının her satıra `m3` alanını eklemesidir: satır sayısı azalırken satır başına alan artıyor.
**Taşınan satırı düşürmek taşınan veriyi düşürmek anlamına gelmez.**

Son iki sütun dersin en önemli sayısıdır. Üç kurulumun okuduğu hücre toplamı sırasıyla 33110, 26400
ile 6710 toplamı olan 33110 ve 33000 ile 110 toplamı olan 33110'dur. **Üçü de aynıdır.** Sınır
kararı hesaplama işini yok etmiyor, yalnızca **kimin yaptığını** değiştiriyor. Kaynakta dönüştürmek
33000 hücrelik okumayı operasyonel sistemin üstüne yıkar; hedefte dönüştürmek aynı yükü hedefe
taşır. Bir kurulumu "ucuz" diye seçmek, yükün nereye bindiğini görmemektir.

İki tarafın bu yükü karşılama biçimi de aynı değildir. Kaynak operasyonel bir sistemdir ve asıl işi
dönüşüm koşturmak değil, kendi uygulamasına hizmet etmektir; 33000 hücrelik okuma orada, o hizmetle
aynı kaynağı paylaşarak koşar. Hedef ise yalnızca bu iş için ayrılmıştır. Dolayısıyla eşit görünen
iki sayı eşit değildir: kaynakta okunan hücre başka bir işten çalınır, hedefte okunan hücre çalınmaz.
Dönüşüm zinciri her büyüdüğünde bu fark büyür, çünkü eklenen her aşama sınırın hangi tarafındaysa o
tarafın yükünü artırır. Dönüşümün SQL ile yazılıyor olması bu kararı değiştirmez: dil bir seçim,
sınır ayrı bir seçimdir ve aynı sorgu iki tarafta da yazılabilir.

#### Yeniden Hesaplamanın Bedeli

Buraya kadar iki kurulum arasında ölçülebilir bir üstünlük yok. Fark, hat **ikinci kez** koştuğunda
ortaya çıkar. Bir raporlama kuralı değişir: işyeri tüketimi bundan sonra 1,05 katsayıyla
raporlanacak. Kural değişikliğinin hedefte uygulanabilmesi, hedefte **hangi aşamanın çıktısının**
durduğuna bağlıdır.

```python
KURAL = {"isyeri katsayisi 1,05": 3, "tekillestirme anahtari": 1, "birim duzeltmesi": 0}
KAYNAK_SAKLAMA = 3        # operasyonel kaynak yalnizca son uc donemi tutuyor

print(f"{'kural':<24}{'gereken sinir':>14}" + "".join(f"{a:>16}" for a, _ in KURULUM))
for kural, g in KURAL.items():
    print(f"{kural:<24}{g:>14}" + "".join(f"{('evet' if n <= g else 'hayir'):>16}"
                                          for _, n in KURULUM))
print(f"{'hedefte hesaplanabilen':<24}{'':>14}"
      + "".join(f"{sum(1 for g in KURAL.values() if n <= g):>16}" for _, n in KURULUM))
print()


def isyeri_kurali(sinir):
    """Hedef yalnizca kendi elindeki veriyle yeniden hesaplamaya calisir."""
    _, _, tas = bolunmus_hat(KAYNAK_KAYIT, sinir)
    if sinir > KURAL["isyeri katsayisi 1,05"]:
        return None, "evet", len(KAYNAK_KAYIT), KAYNAK_SAKLAMA
    v = tas
    if not any("m3" in k for k in v):
        v, _ = a1_ayikla(v)
        v, _ = a2_tekille(v)
        v, _ = a3_tuketim(v)
    yeni = [dict(k, m3=k["m3"] * (1.05 if k["tip"] == "isyeri" else 1.0)) for k in v]
    oz, _ = a4_ozet(yeni)
    sun, _ = a5_sun(oz)
    return sun, "hayir", 0, len(DONEM)


print(f"{'kurulum':<16}{'kaynaga donus':>15}{'yeniden okunan':>16}{'donem':>7}  sunum")
for ad, n in KURULUM:
    sun, donus, okunan, donem = isyeri_kurali(n)
    print(f"{ad:<16}{donus:>15}{okunan:>16}{donem:>7}  {sun if sun else 'hedefte yok'}")
print()
TASINABILIR = len(KAYNAK_KAYIT) // len(DONEM) * KAYNAK_SAKLAMA
print("siniri 4'ten 0'a kaydirmak: tasinacak gecmis", len(KAYNAK_KAYIT),
      " kaynaktan gelebilen", TASINABILIR,
      " kalici olarak yok", len(KAYNAK_KAYIT) - TASINABILIR)
```

```
kural                    gereken sinir   hedefte (ELT)           karma  kaynakta (ETL)
isyeri katsayisi 1,05                3            evet            evet           hayir
tekillestirme anahtari               1            evet           hayir           hayir
birim duzeltmesi                     0            evet           hayir           hayir
hedefte hesaplanabilen                               3               1               0

kurulum           kaynaga donus  yeniden okunan  donem  sunum
hedefte (ELT)             hayir               0     12  {'merkez': 1357.32, 'kuzey': 1235.57, 'dogu': 902.13, 'bati': 859.22, 'guney': 1095.81}
karma                     hayir               0     12  {'merkez': 1357.32, 'kuzey': 1235.57, 'dogu': 902.13, 'bati': 859.22, 'guney': 1095.81}
kaynakta (ETL)             evet            2400      3  hedefte yok

siniri 4'ten 0'a kaydirmak: tasinacak gecmis 2400  kaynaktan gelebilen 600  kalici olarak yok 1800
```

Üç kural üç ayrı sınır ister. İşyeri katsayısı tüketim satırına, tekilleştirme anahtarının
değiştirilmesi tekilleştirmeden önceki satıra, birim düzeltmesi ise ham endeksin kendisine
dokunur. Bir hedef, ancak kendi elindeki çıktı o sınırdan **önce** duruyorsa kuralı uygulayabilir.
Sayı budur: hedefte hesaplanabilen kural ELT kurulumunda **3**, karma kurulumda **1**, ETL
kurulumunda **0**.

İkinci tablo bunun ne demek olduğunu yazar. İlk iki kurulum kaynağa hiç dönmeden, **0 satır**
yeniden taşıyarak on iki dönemin tamamını yeniden hesaplıyor ve yeni sunumu üretiyor. Üçüncü kurulum
hedefte yalnızca 55 özet hücresi tuttuğu için işyeri ile mesken ayrımını göremiyor; kuralı
uygulamanın tek yolu kaynağa dönüp **2400 satırı** yeniden okumak. Kaynak operasyonel bir sistemdir
ve son üç dönemi saklar: yeniden hesaplanabilen dönem **3**, geri kalan **dokuz dönem** hiçbir
biçimde geri getirilemez.

Bu, kursun kuralının ilk somut karşılığıdır: bir hattın sayısı yeniden koşturulduğunda aynı sonucu
vermesidir ve **yeniden koşturulamayan hat ölçülmemiş sayılır**. Kaynakta dönüştüren kurulum ilk
koşumda kusursuz çalışıyor, taşınan satırı 43,6 kat düşürüyor ve doğru sunumu üretiyor. Dokuz dönemi
yeniden hesaplayamadığı an ise o sayıların hiçbiri savunulabilir olmaktan çıkıyor.

Bedelin öbür yüzü de yazılmalıdır. Ham veriyi hedefte saklamak 2400 satırı iki yerde birden tutmak
demektir ve hedefin okuduğu hücre 110'dan 33110'a çıkar. ELT kurulumu yeniden hesaplama hakkını
bedava almıyor; **hedefte 301 kat daha fazla hücre okuyarak** satın alıyor.

#### Sınırı Sonradan Kaydırmak

Sınırın bir kez seçilip bırakıldığı varsayımı da yanlıştır. Hedefin kapasitesi büyüdükçe ya da
yeniden hesaplama ihtiyacı ortaya çıktıkça sınır kaynağa doğru değil, hedefe doğru kaydırılmak
istenir. Blok sonundaki son satır bu taşınmanın bedelini yazar.

Sınırı dörtten sıfıra almak, hedefte bugüne kadar hiç durmamış olan ham satırların oraya
getirilmesini gerektirir: **2400 satır**. Bunların kaynaktan gelebilecek olanı yalnızca saklanan üç
dönemin satırlarıdır, yani **600**. Kalan **1800 satır** hiçbir yerde durmuyor ve hiçbir biçimde geri
getirilemez. Sınır kaydırıldıktan sonra hat yeni kurallarla ileriye doğru koşabilir, ama dokuz
dönemlik geçmişi yeniden hesaplaması artık kalıcı olarak imkânsızdır.

Buradan çıkan sonuç sınırın nereye konacağından daha keskindir: **sınır kararı, geçmişe dönük olarak
düzeltilemeyen tek karardır.** Yanlış katsayı düzeltilebilir, düşen satır yeniden alınabilir, kusurlu
bir denetim değiştirilebilir. Saklanmamış ham satır ise geri gelmez ve onunla birlikte, o dönemlere
ait bütün sayıların yeniden koşturulabilirliği de gider.

#### Özet

- Bir dönüştürme kararı, beş aşamalı zincirin **nerede kesildiğinden** ibarettir. ETL sınırı 4,
  ELT sınırı 0'dır ve üç kurulum da birebir aynı sunumu üretir.
- Kaynakta dönüştürmek hedefe **55 satır ve 165 hücre** taşır, hedefte dönüştürmek **2400 satır ve
  14400 hücre**; taşınan satırdaki oran 43,6'dır.
- Taşınan satırı düşürmek taşınan veriyi düşürmez: karma sınır satırı 2200'e indirirken taşınan
  hücreyi 15400'e çıkarır, çünkü tüketim aşaması her satıra bir alan ekler.
- Üç kurulumun okuduğu toplam hücre **33110'da sabittir**. Sınır kararı işi yok etmez, yalnızca
  kaynak ile hedef arasında yer değiştirir.
- Bir kural değiştiğinde hedefte yeniden hesaplanabilen kural sayısı **3, 1 ve 0**'dır; kaynakta
  dönüştüren kurulum kaynağa dönmek zorunda kalır ve kaynak üç dönem sakladığı için **dokuz dönem**
  geri getirilemez.
- Ham veriyi hedefte saklamanın bedeli hedefte okunan hücrenin 110'dan 33110'a çıkmasıdır; yeniden
  hesaplama hakkı bu bedelle satın alınır.

#### Sonraki Adım

Bu dersin üç kurulumu da tek bir varsayımı paylaşıyordu: dönüşüm beş aşamalı, sırası sabit ve elle
yazılabilir bir zincirdir. Gerçek bir dönüştürme katmanı tek bir zincir değil, birbirine referans
veren onlarca ayrı çıktıdır ve hangisinin hangisinden önce koşacağı elle tutulan bir listede
yazılıdır. O liste her yeni çıktı eklendiğinde bozulur. Sonraki ders dönüşümleri birbirine referans
veren **model**ler olarak yazar, bağımlılığı referansın kendisinden çıkarır ve iki sayıyı yan yana
koyar: bağımlılık çözümünün sıraladığı görev ile elle tutulan sıranın **kaçırdığı bağımlılık**.

### Dönüştürme Çerçeveleri

Source: https://academia.sh/tr/kurslar/veri-hatlari/donusturme-cerceveleri

Dönüşümlerin birbirine başvuran modeller olarak yazılmasının ve sıranın nereden geldiğinin sayılması: on bir model ve on üç bildirilen bağımlılık üzerinde çözümleyici sırası 0 ihlal verirken elle tutulan yazılma sırası 1 bağımlılığı ihlal edip 7 modeli, alfabetik sıra 5 bağımlılığı ihlal edip 10 modeli eksik girdiyle koşturuyor; ihlalli koşum hiçbir hata üretmeden sunumu her bölgede 17,46 ile 28,37 arasında düşürüyor; sorgu metnine bir başvuru daha eklendiğinde çözümleyici 11 modelin yalnız 3'ünü sıralayıp koşumu başlatmıyor, elle sıra ise on birini de koşturup döngünün bir kenarını sessizce kırıyor; bildirilmemiş tek bir okuma çizgede hiç görünmediği için çözümleyici 0 ihlal bildirirken gerçek ihlal 1 oluyor.

Önceki ders dönüşümün nerede koşacağını sordu ve üç kurulumu yan yana koydu. Üçü de tek bir
varsayımı paylaşıyordu: dönüşüm beş aşamalı, sırası sabit ve tek bir kişinin aklında tutabileceği
bir zincirdir. Bir hedefte biriken dönüşüm katmanı ise tek bir zincir değildir; birbirine referans
veren onlarca ayrı çıktıdır ve bunların bir kısmı hiç sunuma bağlanmaz.

Bu ders o çıktıları **model** olarak yazar. Bir model adı olan tek bir çıktıdır ve tanımı bir
sorgudur. Ölçülen iki şey vardır: bağımlılık çözümünün sıraladığı görev ve **elle tutulan sıranın
kaçırdığı bağımlılık**. İkincisinin bedeli bu derste hata olarak değil, hatasız üretilmiş yanlış bir
sayı olarak görünür.

- **DN9.** Model kaydı, sorgu metinleri ve çözümleyici **kurgudur**; hiçbir dönüştürme çerçevesi,
  sorgu motoru ya da veri ambarı ürünü çağrılmaz. Tohum **20260218**.
- **DN10.** Bir modelin bağımlılığı sorgu metnindeki `ref(...)` çağrılarından **çıkarılır**; ayrı
  bir listede tutulmaz. Referansı yazmak ile bağımlılığı bildirmek tek bir iştir.
- **DN11.** Modeller bir **yönlü çevrimsiz çizgenin (directed acyclic graph)** düğümleridir.
  Topolojik sıralama algoritması Bilgisayar Bilimi müfredatının Veri Yapıları kursunda, Çizgeler
  konusunda ölçüldü ve **tekrarlanmaz**; burada çizge kurulur ve kullanılır.
- **DN12.** Ölçülen koşum **ilk koşumdur**: hiçbir tablo daha önce yazılmamıştır. Sırası gelmeden
  okunan bir model **boş tablo** okur ve hata vermez.
- **DN13.** Taban çizgisi **elle tutulan sıradır**: bağımlılık çözümü yok, ihlal sayacı yok, döngü
  denetimi yok.
- **DN14.** Üretici son dönemin işyeri okumalarını yeniden okumuş ve endeksleri 12 fazla bulmuştur.
  Bu bir bozulma değil, sıradan bir **üretici düzeltmesidir**.
- **DN15.** Sayılan birimler görev, ihlal edilen bağımlılık ve **bozulan model**dir. Bozulan model,
  eksik girdiyle koşan modelin kendisi ve ondan erişilen bütün modellerdir.
- **DN16.** Çözünürlük: model **11**, bildirilen bağımlılık **13**, sunum **5 değer**. **Sunum
  üzerinde oran basılmaz**, değerin kendisi yazılır.

#### Modelin Bağımlılığı Kendi Metnindedir

Elle tutulan bir sıra listesi ile çözümlenmiş bir çizge arasındaki fark, bağımlılığın **nerede
yazıldığıdır**. Listede sıra ayrı bir belgedir: sorgu bir yerde, sıra başka bir yerde durur ve
ikisini tutarlı tutmak insanın işidir. Referans tabanlı yazımda ise bağımlılık sorgunun **içindedir**:
bir modelin başka bir modeli okuması, o modeli `ref(...)` ile adlandırmasıyla aynı şeydir. Çizge bu
adlardan çıkarılır, dolayısıyla sorgu değiştiği anda çizge de değişir.

İlk blok önceki derslerin kurgu hattını kurar ve üzerine sıralamayı ekler.

```python
# KURGUDUR. Sayac okumalari, bes asamali hat ve model kaydi ders icinde tanimlanir;
# gercek bir donusturme cercevesi, sorgu motoru ya da veri ambari cagrilmaz.
import re

TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def sirala(dugum, kenar):
    """Yonlu cevrimsiz cizgede calistirma sirasi."""
    gelen = {u: 0 for u in dugum}
    ardil = {u: [] for u in dugum}
    for a, b in kenar:
        gelen[b] += 1
        ardil[a].append(b)
    hazir = [u for u in dugum if gelen[u] == 0]
    sira = []
    while hazir:
        u = hazir.pop(0)
        sira.append(u)
        for v in ardil[u]:
            gelen[v] -= 1
            if gelen[v] == 0:
                hazir.append(v)
    return sira, ardil
```

On bir model bir sözlükte durur. `duzeltme` en son eklenendir: üretici son dönemin işyeri
okumalarını yeniden okumuş, düzeltilmiş endeksleri ayrı bir çıktı olarak yayımlamıştır ve `tuketim`
artık onu da okur.

```python
SORGU = {
    "ham_okuma":    "select * from kaynak",
    "ayiklanmis":   "select * from ref('ham_okuma') where zorunlu alanlar dolu",
    "tekil":        "select * from ref('ayiklanmis') group by abone , donem",
    "duzeltme":     "select abone , donem , yeni_endeks from ref('ayiklanmis')",
    "tuketim":      "select * from ref('tekil') left join ref('duzeltme')",
    "bolge_donem":  "select bolge , donem , sum(m3) from ref('tuketim')",
    "bolge_sunum":  "select bolge , avg(toplam) from ref('bolge_donem')",
    "tip_donem":    "select tip , donem , sum(m3) from ref('tuketim')",
    "bolge_tip":    "select * from ref('bolge_donem') join ref('tip_donem')",
    "abone_yillik": "select abone , sum(m3) from ref('tuketim')",
    "tazelik":      "select count(*) from ref('ham_okuma') , ref('bolge_sunum')",
}
REF = re.compile(r"ref\('(\w+)'\)")


def cizge_kur(sorgu):
    """Kenarlar sorgu metnindeki referanslardan cikarilir , ayri bir listeden degil."""
    dugum = list(sorgu)
    kenar = [(o, m) for m, s in sorgu.items() for o in dict.fromkeys(REF.findall(s))]
    return dugum, kenar


def ihlal(sira, kenar):
    """Verilen sirada once kosmasi gereken hangi model sonra kosuyor."""
    yer = {u: i for i, u in enumerate(sira)}
    return [(a, b) for a, b in kenar if yer[a] > yer[b]]


def bozulan(kenar, ihlaller):
    """Eksik girdiyle kosan model: ihlalin hedefi ve ondan erisilen her sey."""
    ardil = {}
    for a, b in kenar:
        ardil.setdefault(a, []).append(b)
    yigin = [b for _, b in ihlaller]
    kapali = set(yigin)
    while yigin:
        u = yigin.pop()
        for v in ardil.get(u, []):
            if v not in kapali:
                kapali.add(v)
                yigin.append(v)
    return kapali


DUGUM, KENAR = cizge_kur(SORGU)
SIRA, _ = sirala(DUGUM, KENAR)
YAZILMA = ["ham_okuma", "ayiklanmis", "tekil", "tuketim", "bolge_donem", "bolge_sunum",
           "tazelik", "abone_yillik", "tip_donem", "bolge_tip", "duzeltme"]
SIRALAR = [("cozumleyici", SIRA), ("elle , yazilma sirasi", YAZILMA),
           ("elle , alfabetik", sorted(DUGUM))]

print("model:", len(DUGUM), " bildirilen bagimlilik:", len(KENAR))
print("cozumleyicinin sirasi:", SIRA)
print()
print(f"{'sira':<24}{'model':>7}{'ihlal':>7}{'bozulan':>9}  ihlali doguran model")
for ad, s in SIRALAR:
    iv = ihlal(s, KENAR)
    print(f"{ad:<24}{len(s):>7}{len(iv):>7}{len(bozulan(KENAR, iv)):>9}  {[a for a, _ in iv]}")
```

```
model: 11  bildirilen bagimlilik: 13
cozumleyicinin sirasi: ['ham_okuma', 'ayiklanmis', 'tekil', 'duzeltme', 'tuketim', 'bolge_donem', 'tip_donem', 'abone_yillik', 'bolge_sunum', 'bolge_tip', 'tazelik']

sira                      model  ihlal  bozulan  ihlali doguran model
cozumleyici                  11      0        0  []
elle , yazilma sirasi        11      1        7  ['duzeltme']
elle , alfabetik             11      5       10  ['ham_okuma', 'tuketim', 'tuketim', 'tip_donem', 'tuketim']
```

Çözümleyici on bir modeli sıralıyor ve on üç bağımlılığın hiçbirini ihlal etmiyor. İlgi çekici olan
öbür iki satırdır. Yazılma sırası, modellerin depoya eklendiği tarih sırasıdır ve `duzeltme`
eklenene kadar **kusursuzdu**: on modelin sırası doğruydu. Yeni model listenin sonuna yazıldı,
çünkü yeni şeyler oraya yazılır. Onu okuyan `tuketim` ise listenin dördüncü sırasındadır. **Tek bir
ihlal** budur ve o tek ihlal on bir modelin **yedisini** eksik girdiyle koşturur.

Alfabetik sıra daha kötüdür ve daha dürüsttür: beş bağımlılığı ihlal eder, on modeli bozar. Kaynak
okumasının kendisi ikinci sıraya düşen `ayiklanmis`tan sonra koştuğu için zincirin tamamı boşta
kalır. İki satır arasındaki fark ihlal sayısı değil, ihlalin **görünürlüğüdür**.

#### Sıra Bozulduğunda Ne Oluyor

İhlal sayısı bir sayaçtır; asıl soru koşumun ne ürettiğidir. Üçüncü blok her sırayı gerçekten
koşturur. Sırası gelmemiş bir modelin tablosu henüz yazılmamıştır, dolayısıyla onu okuyan model
**boş tablo** okur ve hata vermez.

```python
def topla(kayit, alan, ikili=True):
    t = {}
    for k in kayit:
        ad = (k[alan], k["donem"]) if ikili else k[alan]
        t[ad] = t.get(ad, 0) + k["m3"]
    return t


def pay(bolge_donem, tip_donem):
    """Bolgenin donem toplamindaki payi."""
    donem = {}
    for (t, dn), v in tip_donem.items():
        donem[dn] = donem.get(dn, 0) + v
    return {(b, dn): round(v / donem[dn], 4) for (b, dn), v in bolge_donem.items()
            if donem.get(dn)}


# Uretici son donemin isyeri okumalarini yeniden okudu: endeks 12 fazla cikti.
ISLEV = {
    "ham_okuma": lambda d: kaynak_uret(),
    "ayiklanmis": lambda d: a1_ayikla(d["ham_okuma"])[0],
    "tekil": lambda d: a2_tekille(d["ayiklanmis"])[0],
    "duzeltme": lambda d: {(k["abone"], k["donem"]): k["endeks"] + 12
                           for k in d["ayiklanmis"]
                           if k["donem"] == "2026-12" and k["tip"] == "isyeri"},
    "tuketim": lambda d: a3_tuketim([dict(k, endeks=d["duzeltme"].get(
        (k["abone"], k["donem"]), k["endeks"])) for k in d["tekil"]])[0],
    "bolge_donem": lambda d: a4_ozet(d["tuketim"])[0],
    "bolge_sunum": lambda d: a5_sun(d["bolge_donem"])[0],
    "tip_donem": lambda d: topla(d["tuketim"], "tip"),
    "abone_yillik": lambda d: topla(d["tuketim"], "abone", ikili=False),
    "bolge_tip": lambda d: pay(d["bolge_donem"], d["tip_donem"]),
    "tazelik": lambda d: {"ham": len(d["ham_okuma"]), "bolge": len(d["bolge_sunum"])},
}
LISTE = ("ham_okuma", "ayiklanmis", "tekil", "tuketim")
BOS = {ad: ([] if ad in LISTE else {}) for ad in SORGU}


def kostur(sira):
    """Ilk kosum: sirasi gelmeden okunan model bos tablo okur."""
    depo = dict(BOS)
    for ad in sira:
        depo[ad] = ISLEV[ad](depo)
    return depo


for ad, s in SIRALAR:
    print(f"{ad:<24}{kostur(s)['bolge_sunum']}")
```

```
cozumleyici             {'merkez': 1349.82, 'kuzey': 1232.18, 'dogu': 898.82, 'bati': 853.82, 'guney': 1090.64}
elle , yazilma sirasi   {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
elle , alfabetik        {}
```

Üçüncü satır beklenen başarısızlıktır: alfabetik sıra hiç sunum üretmiyor. Boş bir sözlük hiç kimseyi
yanıltmaz; ilk bakan kişi bir şeyin yanlış gittiğini görür.

İkinci satır dersin asıl bulgusudur. Yazılma sırası **beş bölge için de eksiksiz bir sunum**
üretiyor. Değerler makul aralıkta, bölgeler arası ilişki doğru, hiçbir alan boş değil ve koşum
hatasız tamamlanmış. Tek sorun sayıların yanlış olmasıdır: `duzeltme` tablosu `tuketim`den sonra
yazıldığı için düzeltilmiş endeksler hesaba hiç girmiyor. Doğru sunumla arasındaki fark her bölgede
17,46 ile 28,37 arasında. Bu, kursun üçüncü iddiasının bu dersteki hâlidir: **çıktıya bakmak
yetmez.** Yanlış sıra çökmedi, uyarmadı, boş bırakmadı; yalnızca sessizce eski veriyi kullandı.

Farkın kaynağı tek bir modeldir, ama etkisi tek bir modelle sınırlı değildir. Bozulan model sayısı
yedidir: `tuketim` eksik girdiyle koştuğu için ondan beslenen bölge özeti, bölge sunumu, tip özeti,
bölge–tip payı, abone yıllığı ve tazelik raporu da eski sayıyı taşır. Bir bağımlılığı kaçırmanın
bedeli, kaçırılan bağımlılığın **aşağısında kalan her şeydir**.

#### Çözümleyicinin Göremediği İki Şey

Çözümleyici bağımlılığı sorgudan çıkarır, dolayısıyla sorgunun söylediğinden fazlasını bilemez. İki
durum bunun sınırını gösterir: sorgunun **fazla** söylediği durum ve **eksik** söylediği durum.

```python
DONGULU = dict(SORGU, duzeltme=SORGU["duzeltme"] + " where bolge in ref('bolge_sunum')")
D2, K2 = cizge_kur(DONGULU)
S2, _ = sirala(D2, K2)
print("dongulu kayit  model:", len(D2), " bagimlilik:", len(K2))
print("cozumleyici    siralanan:", len(S2), " siralanamayan:", len(D2) - len(S2),
      " siralananlar:", S2)
print("elle sira      kosan model:", len(YAZILMA),
      " kirilan kenar:", len(ihlal(YAZILMA, K2)))
print()

GERCEK = KENAR + [("bolge_sunum", "abone_yillik")]
print("bildirilen bagimlilik:", len(KENAR), " gercek bagimlilik:", len(GERCEK))
print("cozumleyicinin bildirdigi ihlal:", len(ihlal(SIRA, KENAR)),
      " gercek ihlal:", len(ihlal(SIRA, GERCEK)),
      " bozulan model:", len(bozulan(GERCEK, ihlal(SIRA, GERCEK))))
```

```
dongulu kayit  model: 11  bagimlilik: 14
cozumleyici    siralanan: 3  siralanamayan: 8  siralananlar: ['ham_okuma', 'ayiklanmis', 'tekil']
elle sira      kosan model: 11  kirilan kenar: 1

bildirilen bagimlilik: 13  gercek bagimlilik: 14
cozumleyicinin bildirdigi ihlal: 0  gercek ihlal: 1  bozulan model: 1
```

Birinci durumda `duzeltme` modeline yayımlanmış bölge sunumuna bakan bir koşul eklenmiştir. Bu
sıradan bir istektir ve tek bir referanstır, ama çizgeye bir çevrim sokar: bölge sunumu tüketimden,
tüketim düzeltmeden, düzeltme de bölge sunumundan gelmektedir. Çözümleyici on bir modelin yalnızca
**üçünü** sıralayabiliyor, **sekizi** sıralanamıyor ve koşum hiç başlamıyor. Elle tutulan sıra ise
on bir modeli de koşturuyor ve döngünün bir kenarını **kırılan kenar sayısı 1** olarak, hiçbir yere
yazmadan geçiyor. **Doğrusal bir liste bir döngüyü döngü olarak gösteremez**; yalnızca döngünün bir
kenarını seçip sessizce kırar. Çözümleyicinin koşmayı reddetmesi bir arıza değil, döngünün tek
görünür kanıtıdır.

İkinci durum çözümleyicinin kendi bedelidir. Bir model, başka bir modelin çıktısını `ref(...)`
yerine tablonun fiziksel adıyla okursa bu okuma sorgu metninde başvuru olarak **görünmez**. Gerçek
bağımlılık on dörttür, bildirilen on üç. Çözümleyici kendi çizgesini kusursuz sıralar ve **0 ihlal**
bildirir; gerçek ihlal **1**, bozulan model **1**'dir. Çözümleyicinin ürettiği güvence çizgenin
doğruluğuna değil, **bildirimin eksiksizliğine** dayanır. Bildirilmeyen her okuma, yeşil bir raporun
altında saklanır.

#### Özet

- Bir modelin bağımlılığı ayrı bir sıra listesinde değil, kendi sorgu metnindeki `ref(...)`
  çağrılarında durur; çizge o metinden çıkarıldığı için sorgu değiştiğinde çizge de değişir.
- On bir model ve on üç bağımlılıkta çözümleyici **0 ihlal** verir; yazılma sırası **1**, alfabetik
  sıra **5** bağımlılığı ihlal eder ve sırasıyla **7** ile **10** modeli eksik girdiyle koşturur.
- Sonradan eklenen tek bir model elle tutulan sırayı bozmaya yeter, çünkü yeni model listenin sonuna
  yazılır ama çizgede yukarıda durur.
- İhlalli koşum hata vermez: beş bölge için eksiksiz bir sunum üretir ve doğru sunumdan her bölgede
  17,46 ile 28,37 arasında sapar. Boş çıkan alfabetik koşum, yanlış sayı üreten koşumdan daha az
  tehlikelidir.
- Sorguya tek bir başvuru eklemek çizgeye çevrim sokabilir: çözümleyici 11 modelin 3'ünü sıralayıp
  koşumu başlatmaz, elle sıra on birini de koşturup döngünün bir kenarını sessizce kırar.
- Çözümleyicinin güvencesi bildirimin eksiksizliğine dayanır: bildirilmemiş tek bir okuma gerçek
  bağımlılığı 14'e çıkarır, çözümleyici yine **0 ihlal** bildirir ve gerçek ihlal 1 olur.

#### Sonraki Adım

Bu dersin on bir modeli tek bir düzlemde duruyordu: hepsi eşit ölçüde kalıcı, hepsi eşit ölçüde
sorgulanabilir. Gerçek bir dönüştürme katmanında bu modeller **katmanlara** ayrılır ve katmanların
her biri farklı bir söz verir. Ham katman kaynağı olduğu gibi saklar, arıtılmış katman kuralları
uygular, sunum katmanı yalnızca yayımlanacak olanı taşır. Bu ayrımın bedeli aynı verinin birden çok
kez saklanmasıdır; karşılığı ise bir bozulmanın **aşağıya inmeden durdurulabilmesidir**. Sonraki ders
her katmanın sakladığı satırı sayar ve önceki kursun altı bozulma sınıfını bu üç katmana dağıtarak
her birinin hangi katmanda durdurulabildiğini ölçer.

### Aşamalı Katmanlar

Source: https://academia.sh/tr/kurslar/veri-hatlari/asamali-katmanlar

Ham, arıtılmış ve sunum katmanlarının her birinin sakladığı satırın ve bir bozulmanın hangi katmanda durdurulabildiğinin sayılması: sağlam hat ham katmanda 2400, arıtılmış katmanda 2200 satır ve sunum katmanında 55 özet hücresi bırakıyor; altı bozulma sınıfının dördü ham katmanda, biri arıtılmış katmanda, biri de ancak sunum katmanında duruyor ve bunlardan üçü sunulan değeri hiç değiştirmiyor; üç katmanlı kurulum 4655 satır saklayıp altı sınıfın altısını da durdururken iki katmanlı kurulum 2455 satırla aynı altısını durduruyor ama birim değişimini bir katman geç yakalıyor, tek katmanlı kurulum ise 55 hücre saklayıp yalnız iki sınıfı durduruyor ve dördünü hiç görmüyor.

Önceki ders on bir modeli tek bir düzlemde tuttu: hepsi eşit ölçüde kalıcı, hepsi eşit ölçüde
sorgulanabilir. O düzlemde bir modelin çıktısına bakan biri, o sayının kaynaktan mı yoksa dört
dönüşüm sonrasından mı geldiğini ayırt edemez. Kalıcı bir dönüştürme katmanında modeller bu yüzden
tek bir düzlemde durmaz; birbirinin üstüne binen **aşamalı katmanlara (staging layer)** ayrılır ve
her katman farklı bir söz verir.

Bu ders üç katmanı kurar ve iki şeyi ölçer: her katmanın **sakladığı satır** ve bir bozulmanın
**hangi katmanda durdurulabildiği**. İkinci soru birincisine bağlıdır, çünkü bir kural yalnızca
okuyabildiği tabloya bakabilir. Önceki kursta tanımlanan altı bozulma sınıfı bu üç katmana dağıtılır.

- **DN17.** Katmanlar, kurallar ve bozulma sınıfları **kurgudur**; gerçek bir depo, tablo biçimi ya
  da veri testi kitaplığı çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **DN18.** Üç katman: **ham** kaynağı olduğu gibi saklar, **arıtılmış** ayıklama, tekilleştirme ve
  tüketim hesabını uygular, **sunum** özet ile yayımlanan değeri taşır. Her katman kendi tablosunu
  bırakır ve sonraki katman öncekini okur.
- **DN19.** Bir kural yalnızca **okuduğu katmanın tablosu saklanıyorsa** koşabilir. Katman
  kaldırıldığında o sınırdaki kurallar da kalkar.
- **DN20.** Altı bozulma sınıfı Veri Mühendisliğine Giriş kursunda tanımlandı ve **tekrarlanmaz**;
  burada girdi olarak kullanılır. Hiçbiri saldırı değildir, hepsi üretici tarafı kazasıdır ve her
  biri tek bir dönemi bozar.
- **DN21.** Yakalama gecikmesini aşama olarak ölçen yordam da o kursta ölçüldü ve tekrarlanmaz.
  Buradaki soru kaç aşama sonra değil, **hangi katmanda** durdurulduğudur.
- **DN22.** Taban çizgisi **tek katmanlı hattır**: yalnızca sunum saklanır, ara tablo yok, ham satır
  yok.
- **DN23.** Sayılan birimler saklanan satır, özet hücresi ve **durdurulan bozulma sınıfıdır**.
- **DN24.** Çözünürlük: satır oranının adımı **0,0004**, özet hücresinin **0,0182**. **Sunum
  üzerinde oran basılmaz**, değerin kendisi yazılır.

#### Üç Katman, Üç Ayrı Söz

Katmanlar birbirinin kopyası değildir; her biri farklı bir soruyu yanıtlayabilir hâle gelir ve
karşılığında bir şeyi kaybeder. **Ham katman** kaynaktan geleni değiştirmeden saklar: alan adları,
türler ve yinelenen satırlar olduğu gibi durur. Bu katmanda bir alanın adına ya da bir değerin
türüne bakılabilir, ama tüketimden söz edilemez, çünkü tüketim henüz hesaplanmamıştır.

**Arıtılmış katman** ayıklama, tekilleştirme ve tüketim hesabını uygular. Burada artık satır başına
bir tüketim değeri vardır ve o değerin işaretine, büyüklüğüne, dağılımına bakılabilir. Karşılığında
düşen satırlar kaybolmuştur: arıtılmış katmana bakan biri neyin neden düştüğünü göremez.

**Sunum katmanı** yalnızca yayımlanacak olanı taşır. Beş değer ve onları üreten elli beş özet
hücresi. Bu katmanda tek yapılabilecek şey bugünkü sayıyı dünkü sayıyla karşılaştırmaktır.

```python
# KURGUDUR. Sayac okumalari, katmanlar ve kurallar ders icinde tanimlanir;
# gercek bir depo, tablo bicimi ya da veri testi kitapligi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti",
           "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000
            y["birim"] = "m3"
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


# ---- UC KATMAN: her biri kendi tablosunu birakir
KATMAN = ["ham", "aritilmis", "sunum"]


def katmanla(kayit):
    v = kayit
    for f in (a1_ayikla, a2_tekille, a3_tuketim):
        v, _ = f(v)
    ozet, _ = a4_ozet(v)
    sunum, _ = a5_sun(ozet)
    return {"ham": kayit, "aritilmis": v, "ozet": ozet, "sunum": sunum}
```

Kurallar katmanların **sınırlarına** yerleşir ve her biri yalnızca kendi katmanının tablosunu okur.
Alan, tür ve anahtar kuralları ham katmanı okur. Satır sayısı ve tüketimin işareti arıtılmış katmanı
okur; ikincisi ham katmanda **yazılamaz**, çünkü orada tüketim yoktur. Dağılım kuralı özeti okur.

```python
def d_sema(v, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in v)


def d_tip(v):
    return any(not isinstance(k.get("endeks"), int) for k in v)


def d_tekillik(v):
    ad = [(k["abone"], k["donem"]) for k in v]
    return len(ad) != len(set(ad))


def d_bolum_donem(v):
    gor = set((k["bolge"], k["donem"]) for k in v)
    return gor != set((b, d) for b in BOLGE for d in DONEM)


def d_negatif(v):
    """Endeks artan oldugu icin saglam hatta tuketim hic negatif olmaz."""
    return any(k["m3"] < 0 for k in v)


def d_satir_sayisi(v, beklenen, pay=0.05):
    return abs(len(v) - beklenen) / beklenen > pay


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


KURAL = [
    ("sema", "ham", lambda kt, t: d_sema(kt["ham"])),
    ("tip", "ham", lambda kt, t: d_tip(kt["ham"])),
    ("tekillik", "ham", lambda kt, t: d_tekillik(kt["ham"])),
    ("bolum_donem", "ham", lambda kt, t: d_bolum_donem(kt["ham"])),
    ("satir_sayisi", "aritilmis", lambda kt, t: d_satir_sayisi(kt["aritilmis"], t["satir"])),
    ("negatif", "aritilmis", lambda kt, t: d_negatif(kt["aritilmis"])),
    ("dagilim", "sunum", lambda kt, t: d_dagilim(kt["ozet"], t["ozet"])),
]


def duran(kt, taban, acik=KATMAN):
    """Bozulmayi ilk durduran katman ve o katmanda atesleyen kural."""
    for kat in KATMAN:
        if kat not in acik:
            continue
        for ad, k, f in KURAL:
            if k == kat and f(kt, taban):
                return kat, ad
    return "-", "-"


KAYNAK_KAYIT = kaynak_uret()
T = katmanla(KAYNAK_KAYIT)
TABAN = {"satir": len(T["aritilmis"]), "ozet": T["ozet"], "sunum": T["sunum"]}
print("saglam hat  ham:", len(T["ham"]), " aritilmis:", len(T["aritilmis"]),
      " ozet hucresi:", len(T["ozet"]), " sunum:", len(T["sunum"]))
print("saglam hatta atesleyen kural:", duran(T, TABAN)[1])
print()

print(f"{'bozulma':<15}{'ham':>6}{'aritilmis':>11}{'ozet':>6}{'sunum':>9}"
      f"{'duran katman':>14}{'kural':>14}")
for s in BOZULMA:
    bozuk, _ = boz(KAYNAK_KAYIT, s)
    kt = katmanla(bozuk)
    kat, kural = duran(kt, TABAN)
    print(f"{s:<15}{len(kt['ham']):>6}{len(kt['aritilmis']):>11}{len(kt['ozet']):>6}"
          f"{('ayni' if kt['sunum'] == TABAN['sunum'] else 'degisti'):>9}{kat:>14}{kural:>14}")
```

```
saglam hat  ham: 2400  aritilmis: 2200  ozet hucresi: 55  sunum: 5
saglam hatta atesleyen kural: -

bozulma           ham  aritilmis  ozet    sunum  duran katman         kural
gec_gelen        2360       2120    55  degisti         sunum       dagilim
yinelenen        2460       2200    55     ayni           ham      tekillik
sema_degisti     2400       1800    45  degisti           ham          sema
birim_degisti    2400       2200    55     ayni     aritilmis       negatif
eksik_bolum      2354       2108    53  degisti           ham   bolum_donem
kayan_tip        2400       2200    55     ayni           ham           tip
```

#### Bozulma Hangi Katmanda Duruyor

Sağlam hat üç katmanda sırasıyla **2400 satır**, **2200 satır** ve **55 özet hücresi** bırakıyor;
hiçbir kural ateşlemiyor. Bozulmuş koşumların dağılımı ise **dört, bir ve bir**dir.

Ham katman dört sınıfı durduruyor ve dördü de aynı nedenle: `yinelenen`, `sema_degisti`,
`eksik_bolum` ve `kayan_tip` **satırın kendisinde** görünür. Alan eksik, tür kaymış, anahtar
yinelenmiş ya da bir bölge–dönem çifti hiç yok. Ham katman satırı olduğu gibi sakladığı için bu
dördüne bakabilir. Bir dönüşüm uygulanmış olsaydı bunların çoğu **görünmez olurdu**: `kayan_tip`
satırının kanıtı arıtılmış katmanda yoktur, çünkü tüketim hesabı metne dönmüş sayıyı sessizce
yeniden sayıya çevirir ve satır düşmez.

Arıtılmış katman tek bir sınıfı durduruyor: `birim_degisti`. Bunu yapan kural tüketimin **negatif**
olamayacağını söyler; endeks artan olduğu için sağlam hatta hiç ateşlemez. Kural ham katmanda
yazılamazdı, çünkü ham katmanda tüketim diye bir şey yoktur, yalnızca endeks vardır. Bu, katmanın
varlık nedeninin en açık kanıtıdır: **arıtılmış katman yeni bir alan üretir ve o alanın üstüne yeni
bir kural yazılabilir hâle gelir.**

Sunum katmanı da tek bir sınıfı durduruyor: `gec_gelen`. Kırk satır hiç gelmemiş, arıtılmış katman
2120'ye inmiş ama bu düşüş satır sayısı kuralının yüzde beşlik payını aşmamış. Bozulma iki katmanı
da geçip özete girdikten sonra, ancak dağılım karşılaştırmasıyla görünüyor. **O noktada bozulma
zaten iki tabloyu kirletmiştir** ve onarım yalnızca sunumu değil, altındaki iki katmanı da
kapsamak zorundadır.

Son sütunlar dersin sessiz bulgusunu taşır. Yakalanan altı sınıftan **üçü** sunulan beş değeri
**hiç değiştirmiyor**: `yinelenen`, `birim_degisti` ve `kayan_tip`. Üçü de yakalandı, ama hiçbiri
çıktıya bakılarak fark edilemezdi. `birim_degisti` en açık örnektir: ham katmanda 2400, arıtılmış
katmanda 2200 satır, özette 55 hücre ve sunumda tabanla birebir aynı beş değer. Yalnızca ara
katmandaki tüketimin işareti bozulmanın olduğunu söylüyor.

#### Katman Kaldırıldığında

Üç katman bedava değildir. Aynı veri üç ayrı biçimde saklanır ve her katman ayrı bir yazma işi,
ayrı bir tazelik sorusu, ayrı bir sahiplik getirir. Son blok katmanları teker teker kaldırır ve
durdurulan bozulma sınıfı sayısını saklanan satırın yanına koyar.

```python
KURULUM = [("uc katman", KATMAN), ("iki katman", ["ham", "sunum"]),
           ("tek katman", ["sunum"])]
print(f"{'kurulum':<12}{'saklanan satir':>16}{'durdurulan':>12}"
      f"{'sunum katmaninda':>18}  yakalanmayan")
for ad, acik in KURULUM:
    saklanan = sum(len(T[k]) if k != "sunum" else len(T["ozet"]) for k in acik)
    durdurulan, gec, kacan = 0, 0, []
    for s in BOZULMA:
        bozuk, _ = boz(KAYNAK_KAYIT, s)
        kat, _ = duran(katmanla(bozuk), TABAN, acik)
        if kat == "-":
            kacan.append(s)
        else:
            durdurulan += 1
            gec += kat == "sunum"
    print(f"{ad:<12}{saklanan:>16}{durdurulan:>12}{gec:>18}  {kacan}")
```

```
kurulum       saklanan satir  durdurulan  sunum katmaninda  yakalanmayan
uc katman               4655           6                 1  []
iki katman              2455           6                 2  []
tek katman                55           2                 2  ['yinelenen', 'sema_degisti', 'eksik_bolum', 'kayan_tip']
```

İkinci satır arıtılmış katmanın gerçek katkısını verir. Ham ve sunum katmanları saklanırken altı
sınıfın altısı da durduruluyor; tek fark `birim_degisti`nin artık dağılım kuralına kalmasıdır, yani
**bir katman geç** yakalanmasıdır. Arıtılmış katmanın satın aldığı şey budur ve bedeli **2200
satırdır**. Bir katmanın değeri her zaman yeni bir bozulma sınıfı yakalamak değildir; çoğu zaman
aynı sınıfı **daha yukarıda** yakalamaktır.

Üçüncü satır taban çizgisidir ve dersin en sert sayısını taşır. Yalnızca sunum saklandığında toplam
depolama 55 hücreye iniyor, ama durdurulan bozulma sınıfı **altıdan ikiye** düşüyor. Yakalanmayan
dört sınıf listede adıyla yazılıdır. Bunların ikisi (`sema_degisti`, `eksik_bolum`) sunulan değeri
değiştiriyor, yani dikkatli bir okuyucunun bir gün fark etme ihtimali var. Öbür ikisi
(`yinelenen`, `kayan_tip`) sunulan değeri **hiç değiştirmiyor**: tek katmanlı bir hatta bu iki
bozulma sınıfı için bakılabilecek hiçbir yer yoktur, çünkü kanıtı taşıyan tablo saklanmamıştır.

Bu, kursun kuralının katman düzeyindeki karşılığıdır. Tek katmanlı hat çalışır, sayı üretir ve
altı bozulmanın dördünde hiçbir şey söylemez. **Bozulmayı gösterebilecek tabloyu saklamayan bir
hat, o bozulma için ölçülmemiş sayılır.** Katmanların 4655'e karşı 55 satırlık bedeli, tam olarak
bu ölçülebilirliğin fiyatıdır.

#### Özet

- Üç katman üç ayrı söz verir: ham katman satırı olduğu gibi saklar, arıtılmış katman tüketim
  değerini üretir, sunum katmanı yalnızca yayımlanacak olanı taşır. Sağlam hatta sırasıyla
  **2400 satır, 2200 satır ve 55 özet hücresi** bırakırlar.
- Bir kural yalnızca okuduğu katmanın tablosu saklanıyorsa koşabilir; tüketimin işaretine bakan
  kural ham katmanda **yazılamaz**, çünkü orada tüketim yoktur.
- Altı bozulma sınıfının **dördü** ham katmanda, **biri** arıtılmış katmanda, **biri** ancak sunum
  katmanında duruyor; sonuncusu durdurulduğunda alttaki iki tablo çoktan kirlenmiştir.
- Yakalanan altı sınıftan **üçü** sunulan beş değeri hiç değiştirmiyor; `birim_degisti`nin tek
  kanıtı arıtılmış katmandaki negatif tüketimdir.
- Arıtılmış katman kaldırıldığında durdurulan sınıf sayısı düşmez, ama `birim_degisti` **bir katman
  geç** yakalanır; katmanın 2200 satırlık bedeli bu bir katmanı satın alır.
- Yalnızca sunum saklandığında depolama 55 hücreye iner ve durdurulan sınıf **altıdan ikiye** düşer;
  yakalanmayan dördün ikisi sunulan değeri de değiştirmediği için bakılabilecek hiçbir yer kalmaz.

#### Sonraki Adım

Bu dersin kuralları hattın **taşıdığı satıra** bakıyordu: alan var mı, tür doğru mu, anahtar tekil
mi, tüketim negatif mi. Hepsi verinin kendisi hakkındaydı ve hiçbiri dönüşümün kendisi hakkında bir
şey söylemedi. Bir dönüşüm doğru veriyi alıp yanlış hesaplayabilir; toplama yanlış anahtarla
gruplanabilir, katsayı yanlış tipe uygulanabilir, birleştirme satır çoğaltabilir. Bu durumların
hiçbiri girdide görünmez ve hiçbiri katman sınırındaki kurallara takılmaz. Sonraki ders kuralı
verinin üstünden **dönüşümün çıktısının üstüne** taşır: her dönüşümün kendi çıktısına konan beklenti
yazılır, kaç bozulma sınıfını yakaladığı sayılır ve hiçbir beklentinin yakalamadığı kalıntı ölçülür.

### Test Edilebilir Dönüşümler

Source: https://academia.sh/tr/kurslar/veri-hatlari/test-edilebilir-donusumler

Dönüşümün kendi çıktısına konan beklentilerin yakaladığının ve yakalamadığının sayılması: altı beklenti sağlam hatta hiç ateşlemiyor ve altı bozulma sınıfından dördünü yakalıyor, yinelenme ile tür kayması dönüşümün kendisi tarafından yutulduğu için çıktıda hiç görünmüyor. Birim değişimi çıktıya konan beklentiyle üçüncü aşamada yakalanıyor ve bunun için hiçbir taban çizgisi gerekmiyor, oysa sunum birebir aynı kalıyor. Dönem etiketini bir dönem geriye kaydıran kural hatası 2200 satırı, 55 hücreyi ve beş sunum değerini birebir aynı bıraktığı için altı beklentinin hiçbirine takılmıyor; onu yalnız sabit girdi üzerindeki birim testi yakalıyor.

Üç derste dönüşümün **yeri** yazıldı: kaynakta mı hedefte mi yapılacağı, hangi sırayla koşacağı ve
hangi katmanda duracağı. Üç sorunun da yanıtı bir düzenekti ve üçü de aynı şeyi varsaydı — kuralın
kendisi doğru, veri de düzgün geliyor. Hiçbir aşamada dönüşümün **doğru sonucu ürettiği**
sınanmadı.

Bu ders o soruyu sorar ve iki ayrı yanıt aracını yan yana koyar: elle yazılmış **sabit bir girdi**
üzerinde koşan birim testi ile koşumun **gerçek çıktısına** konan beklenti. İkisinin yakaladıkları
farklıdır ve daha önemlisi, ikisinin birden göremediği bir kalıntı vardır.

- **DN41.** Kaynak, beş aşamalı hat ve bozulma sınıfları **kurgudur**; gerçek bir depo, dönüştürme
  çerçevesi ya da veri testi kitaplığı çağrılmaz. Tohum **20260218**, çıktı koşumdan bağımsızdır.
- **DN42.** Bu derste **dönüşüm**, ilk üç aşamanın bileşkesidir: `ayikla`, `tekille` ve dönem
  farkından tüketim. Girdisi ham satır, çıktısı tüketim satırıdır.
- **DN43.** **Beklenti (expectation)** çıktının taşıması gereken bir değişmezi doğru ya da yanlış
  olarak döndüren yordamdır. Burada `True` **bozuldu** demektir.
- **DN44.** Birim testi yordamı Yazılım Kalitesi ve Test müfredatındaki Birim Testi ve Test Odaklı
  Geliştirme kursunda, kalite boyutlarının denetime çevrilmesi Veri Mühendisliğine Giriş kursunda
  ölçüldü ve **tekrarlanmaz**. Buradaki fark tek bir noktadadır: o denetimler **kaynağa**
  bakıyordu, bunlar **dönüşümün çıktısına** bakar.
- **DN45.** Taban çizgisi **beklentisiz dönüşümdür**: bozulan beklenti **0**, çünkü yazılmış
  beklenti yoktur.
- **DN46.** Bozulma sınıfları saldırı değil üretici tarafı kazalarıdır ve her biri **tek bir
  dönemi** (`2026-07`) bozar.
- **DN47.** Tüketim tavanı **200** fiziksel bir seçimdir; kaynağın üretecinden bilinen 49 değildir.
  Beklentiyi yazan, üreteci değil sayacı bilir.
- **DN48.** Çözünürlük: tüketim satırı oranının adımı **1/2200 = 0,0005**, özet **55 hücre**, sunum
  **5 değer**. **Sunum üzerinde oran basılmaz**, değerin kendisi yazılır.

#### Sınanabilir Dönüşümün Koşulu

Bir dönüşüm ancak **saf fonksiyon** olduğunda sınanabilir: aynı girdiye her koşumda aynı çıktıyı verir,
gizli durum taşımaz, saate bakmaz, dışarıdan bir şey okumaz. Bu koşul kursun kuralının doğrudan
karşılığıdır — yeniden koşturulduğunda aynı sonucu vermeyen bir dönüşümün sınanacak bir sonucu
yoktur, çünkü her koşum başka bir cevabı savunulabilir kılar.

Koşul sağlandığında iki ayrı test türü mümkün olur ve ikisi farklı sorulara bakar:

| Tür | Girdi | Kâhin (oracle): beklenen sonuç nereden | Yakaladığı |
|---|---|---|---|
| birim testi | elle yazılmış sabit girdi | elle hesaplanmış çıktı | **kuralın** hatası |
| veri testi (beklenti) | koşumun gerçek çıktısı | çıktının taşıması gereken değişmez | **verinin** bozulması |

Ayrım kâhin sütununda durur. Birim testinde doğru cevap testi yazandan gelir ve girdi küçük
tutulduğu için elle hesaplanabilir. Beklentide böyle bir cevap yoktur; iki bin küçük satırın doğru
toplamını kimse elle bilemez. Beklenti yalnız **çıktının kendisi hakkında her koşumda doğru olması
gereken** şeyi yazabilir: anahtar tekildir, tüketim eksi olamaz, hücre sayısı tamdır.

Aşağıdaki blok kaynağı, beş aşamayı ve altı bozulma sınıfını kurar. Ders boyunca kullanılan tanım
budur.

```python
# KURGUDUR. Olcum aginin sayac okumalari, bes asamali hat ve bozulma siniflari
# ders icinde tanimlanir; gercek bir depo, cerceve ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a1_ayikla(kayit):
    """Zorunlu alanlari tasimayan satir dusurulur."""
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    """Sunum: bolge basina donem ortalamasi."""
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0
```

#### Sabit Girdi Üzerinde Birim Testi

İkinci blok sekiz satırlık bir girdi yazar ve doğru çıktıyı elle hesaplar. Girdide iki abone,
üçer dönem, bir yinelenen satır ve zorunlu alanı eksik bir satır vardır; dolayısıyla üç aşama da
işini yapmak zorundadır. Yanına aynı hesabı yapan ama sonucu **bir önceki dönemin etiketiyle**
yazan bir kural konur.

```python
def donustur(kayit):
    """Ham satirdan tuketim satirina: ayikla , tekille , donem farki."""
    v, _ = a1_ayikla(kayit)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def donustur_kaydirmali(kayit):
    """AYNI hesap , tek fark: sonuc bir onceki donemin etiketiyle yaziliyor."""
    return [dict(k, donem=DONEM[DONEM.index(k["donem"]) - 1]) for k in donustur(kayit)]


def satir(ab, d, e, tip="mesken"):
    return {"abone": ab, "donem": d, "bolge": "dogu", "tip": tip, "endeks": e, "birim": "m3"}


# Sabit girdi: sekiz satir , icinde bir yinelenen ve bir eksik alanli satir var.
SABIT = [satir("T1", "2026-01", 100), satir("T1", "2026-02", 112),
         satir("T1", "2026-02", 112), satir("T1", "2026-03", 130),
         satir("T2", "2026-01", 500, "isyeri"), satir("T2", "2026-02", 540, "isyeri"),
         satir("T2", "2026-03", 545, "isyeri"), {"abone": "T3", "donem": "2026-02"}]
# Elle hesaplanmis dogru cikti: testin kahini.
BEKLENEN = [("T1", "2026-02", 12), ("T1", "2026-03", 18),
            ("T2", "2026-02", 40), ("T2", "2026-03", 5)]


def ozu(v):
    return sorted((k["abone"], k["donem"], k["m3"]) for k in v)


def yaz(ad, v):
    print(f"{ad:<11}", " ".join(f"{a}/{d[-2:]}={m}" for a, d, m in ozu(v)),
          " gecti:", ozu(v) == sorted(BEKLENEN))


print("sabit girdi:", len(SABIT), "satir , beklenen cikti:", len(BEKLENEN), "satir")
yaz("dogru", donustur(SABIT))
yaz("kaydirmali", donustur_kaydirmali(SABIT))
print("ayni girdi iki kez kosuldu , cikti ayni mi:",
      ozu(donustur(SABIT)) == ozu(donustur(SABIT)))
```

```
sabit girdi: 8 satir , beklenen cikti: 4 satir
dogru       T1/02=12 T1/03=18 T2/02=40 T2/03=5  gecti: True
kaydirmali  T1/01=12 T1/02=18 T2/01=40 T2/02=5  gecti: False
ayni girdi iki kez kosuldu , cikti ayni mi: True
```

Sekiz satır dörde iniyor: yinelenen satır `tekille` aşamasında, eksik alanlı satır `ayikla`
aşamasında, her abonenin ilk dönemi ise tüketimin tanımı gereği düşüyor. Dört sayının tamamı elle
doğrulanabilir: T1 için 112 − 100 = 12 ve 130 − 112 = 18, T2 için 540 − 500 = 40 ve 545 − 540 = 5.

Kaydırmalı kural **aynı dört sayıyı** üretiyor ve testte hemen düşüyor, çünkü etiketler bir dönem
geriye kaymış. Testin gücü buradadır: hesabın kendisi doğru, yanlış olan yalnızca sonucun hangi
döneme yazıldığıdır ve bu ayrım sabit girdide gözle görülür. Son satır kursun kuralını sabit girdi
üzerinde doğrular — aynı girdi iki kez koşturulduğunda çıktı birebir aynıdır.

#### Çıktıya Konan Beklentiler

Üçüncü blok altı beklenti tanımlar. Hepsi dönüşümün **kendi çıktısına** bakar; hiçbiri kaynağa
dönmez ve hiçbiri önceden bilinen bir doğru sonuca ihtiyaç duymaz. Beklentiler altı bozulma sınıfı
üzerinde tek tek koşturulur.

```python
import statistics

# Beklentilerin hepsi donusumun KENDI ciktisina bakar , kaynaga degil.
SATIR, HUCRE, TAVAN = 2200, 55, 200


def b_anahtar_tekil(v, oz):
    ad = [(k["abone"], k["donem"]) for k in v]
    return len(ad) != len(set(ad))


def b_negatif_yok(v, oz):
    return any(k["m3"] < 0 for k in v)


def b_tavan(v, oz):
    return any(k["m3"] > TAVAN for k in v)


def b_satir_orani(v, oz):
    return abs(len(v) - SATIR) / SATIR > 0.02


def b_hucre_tamligi(v, oz):
    return len(oz) != HUCRE


def b_oynaklik(v, oz):
    """Bir bolgenin donem hucreleri kendi ortancasindan cok uzaklasmamali."""
    for b in BOLGE:
        d = [x for (bb, _), x in oz.items() if bb == b]
        orta = statistics.median(d) if d else 0
        if not d or any(abs(x - orta) / abs(orta) > 0.30 for x in d):
            return True
    return False


BEKLENTI = [("anahtar_tekil", b_anahtar_tekil), ("negatif_yok", b_negatif_yok),
            ("tavan", b_tavan), ("satir_orani", b_satir_orani),
            ("hucre_tamligi", b_hucre_tamligi), ("oynaklik", b_oynaklik)]


def kosut(v):
    """Bozulan beklentilerin adi , ozet ve sunum."""
    oz = a4_ozet(v)[0]
    return [a for a, f in BEKLENTI if f(v, oz)], oz, a5_sun(oz)[0]


KAYNAK = kaynak_uret()
TEMIZ, _, SUNUM = kosut(donustur(KAYNAK))
print("beklenti:", len(BEKLENTI), " saglam hatta bozulan:", len(TEMIZ))
print(f"{'bozulma':<15}{'satir':>6}{'hucre':>7}{'bozulan beklenti':>34}{'sunum':>9}")
for s in BOZULMA:
    v = donustur(boz(KAYNAK, s)[0])
    bz, oz, sn = kosut(v)
    print(f"{s:<15}{len(v):>6}{len(oz):>7}{','.join(bz) or 'yok':>34}"
          f"{'ayni' if sn == SUNUM else 'degisti':>9}")
```

```
beklenti: 6  saglam hatta bozulan: 0
bozulma         satir  hucre                  bozulan beklenti    sunum
gec_gelen        2120     55              satir_orani,oynaklik  degisti
yinelenen        2200     55                               yok     ayni
sema_degisti     1800     45         satir_orani,hucre_tamligi  degisti
birim_degisti    2200     55        negatif_yok,tavan,oynaklik     ayni
eksik_bolum      2108     53         satir_orani,hucre_tamligi  degisti
kayan_tip        2200     55                               yok     ayni
```

İlk satır bedelin olmadığını gösterir: sağlam hatta altı beklentinin hiçbiri ateşlemiyor, yani iyi
satır yanlış engellenmiyor. **Altı beklenti altı sınıfın dördünü yakalıyor.**

`birim_degisti` satırı bu dersin en önemli sayısıdır. Veri Mühendisliğine Giriş kursunda aynı
bozulma yalnızca dördüncü aşamanın özetine bakan dağılım denetimiyle görünüyordu; burada
`negatif_yok` ve `tavan` beklentileri onu **dönüşümün çıktısında**, üçüncü aşamada yakalıyor.
Aradaki fark yalnız bir aşama değildir: dağılım denetimi çalışabilmek için **sağlam bir taban
özetine** ihtiyaç duyar, bu üç beklenti hiçbir tabana ihtiyaç duymaz. Yeni kurulmuş bir hatta
karşılaştırılacak bir geçmiş yoktur, değişmez ise ilk koşumda yazılabilir. Sunum sütunu ise
`birim_degisti` için hâlâ **ayni** diyor — sunulan beş değer birebir yerinde durduğu hâlde iki
dönem çöptür ve bunu söyleyen tek şey beklentidir.

Kalan üç yakalama sıradandır: `gec_gelen` ile `eksik_bolum` satır sayısını düşürüp hücre bandını
kırıyor, `sema_degisti` ise dönemin tamamını `ayikla` aşamasında kaybettiriyor ve 1800 satırla 45
hücre bırakıyor.

#### Hiçbir Beklentinin Görmediği Kalıntı

Tabloda iki satır boş: `yinelenen` ve `kayan_tip`. Dördüncü blok bunların yanına iki kural yazar —
biri hatalı, biri kasıtlı — ve aynı altı beklentiyi onlar üzerinde koşturur.

```python
KAYDIRMALI = donustur_kaydirmali(KAYNAK)
KATSAYILI = [dict(k, m3=k["m3"] * (1.05 if k["tip"] == "isyeri" else 1.0))
             for k in donustur(KAYNAK)]
for ad, v in (("kaydirmali kural", KAYDIRMALI), ("katsayili kural", KATSAYILI)):
    bz, oz, sn = kosut(v)
    print(f"{ad:<17} satir={len(v)} hucre={len(oz)} bozulan={','.join(bz) or 'yok'}")
    print("  sunum:", sn)
print("  taban:", SUNUM)
```

```
kaydirmali kural  satir=2200 hucre=55 bozulan=yok
  sunum: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
katsayili kural   satir=2200 hucre=55 bozulan=yok
  sunum: {'merkez': 1357.32, 'kuzey': 1235.57, 'dogu': 902.13, 'bati': 859.22, 'guney': 1095.81}
  taban: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
```

Kalıntı üç parçadır ve üçünün mekanizması ayrıdır.

**Dönüşümün yuttuğu iki sınıf.** `yinelenen` `tekille` aşamasında, `kayan_tip` ise tür çevriminde
düzeliyor; çıktıya bakan bir beklenti onarılmış bir bozulmayla hiç olmamış bir bozulmayı
birbirinden ayıramaz. Bu iki satırın boş kalması dönüşümün sağlamlığının kanıtıdır, ama üreticinin
hâlâ metin gönderdiğini kimse öğrenmez ve gelecek dönemin metni sayıya çevrilemeyebilir.
**Çıktıya konan beklenti, girdiye konan denetimin yerini tutmaz**; ikisi ayrı sorulara bakar.

**Etiket kaydıran kural.** Kaydırmalı kural 2200 satır, 55 hücre üretiyor, hiçbir beklentiyi
bozmuyor ve sunumu **birebir aynı** bırakıyor. Nedeni yapısaldır: hesaplanan sayılar kümesi hiç
değişmemiş, yalnızca hangi döneme yazıldıkları kaymıştır; bölge başına on bir hücrenin ortalaması
aynı sayıların ortalamasıdır. Çıktının kendisi hakkında yazılabilecek hiçbir değişmez bunu göremez,
çünkü çıktının kendisi kusursuzdur — yanlış olan **hangi gerçeğe karşılık geldiğidir**. Bu hatayı
yakalayan tek şey dersin başındaki sabit girdili testtir.

**Kasıtlı kural değişikliği.** Katsayılı kural işyeri tüketimini 1,05 ile raporluyor. Altı
beklentinin hiçbiri bozulmuyor, çünkü ortada bozulma yok; sunum ise beş değerin hepsinde yukarı
kayıyor. Beklentiler bu farkın **istenen bir değişiklik mi yoksa kaza mı** olduğunu söyleyemez.
Ayrımı yapan tek şey, kural sürümünün kayda yazılmasıdır.

#### Özet

- Bir dönüşüm ancak saf fonksiyonse sınanabilir; aynı girdiye her koşumda aynı çıktıyı vermeyen bir
  kuralın sınanacak sonucu yoktur.
- Birim testi kuralın hatasını, beklenti verinin bozulmasını yakalar; ayrım kâhinin nereden
  geldiğindedir ve ikisi birbirinin yerine geçmez.
- Sağlam hatta altı beklentinin hiçbiri ateşlemiyor ve altı bozulma sınıfının dördünü yakalıyorlar;
  `yinelenen` ile `kayan_tip` dönüşümün kendisi tarafından yutulduğu için çıktıda hiç görünmüyor.
- `birim_degisti` çıktıya konan beklentiyle üçüncü aşamada ve **hiçbir taban çizgisi olmadan**
  yakalanıyor, oysa sunulan beş değer birebir aynı kalıyor.
- Dönem etiketini bir dönem geriye kaydıran kural 2200 satır, 55 hücre ve aynı beş sunum değerini
  ürettiği için altı beklentinin hiçbirine takılmıyor; onu yalnız sabit girdili birim testi görüyor.

#### Sonraki Adım

Son bloğun katsayılı kuralı bu dersin kapatamadığı soruyu açıyor. Kural değişti ve yeni koşumlar
doğru sonucu üretecek — ama on iki dönemin on birisi **eski kuralla** hesaplanmış olarak duruyor.
Bütün dönemleri yeniden hesaplamak koşum bedeli demektir, yalnız birkaç dönemi yeniden hesaplamak
ise raporun bir kısmını yeni, kalanını eski kuralla bırakır. Sonraki ders geri doldurmayı bu iki
seçenek üzerinden ölçer: kaç dönemin yeniden hesaplandığını, karışık raporun tam uygulamadan ne
kadar saptığını ve o raporun hangi denetimlerden geçtiğini sayar.

### Geriye Dönük Doldurma

Source: https://academia.sh/tr/kurslar/veri-hatlari/geriye-donuk-doldurma

Değişen bir kuralın geçmiş dönemlere uygulanmasının sayılması: kural on iki dönemin tamamına uygulandığında 12 dönem ve 2200 satır yeniden hesaplanıyor, yalnız iki döneme uygulandığında 2 dönem ve 400 satır yeterli oluyor ama rapor tam uygulamadan 16,98 ile 29,57 arasında sapıyor. Karışık rapor altı beklentinin hiçbirini bozmuyor, kendi içinde tutarlı çıkıyor ve kuralın merkez hücrelerinde yarattığı yüzde 2,75'lik sıçrama doğal dönem oynaklığının ortancasının altında kaldığı için hiçbir denetime takılmıyor. Sapmanın kaynağı veri değil hangi dönemlerin yeniden hesaplandığıdır; satıra kural sürümü damgalandığında karışıklık tek bir karşılaştırmayla görünür oluyor.

Bir önceki dersin son bloğu işyeri tüketimini 1,05 katsayıyla raporlayan bir kural yazdı ve altı
beklentinin hiçbiri bozulmadı. Beklentiler haklıydı, çünkü ortada bir bozulma yoktu: kural kasıtlı
olarak değişmişti. Orada kapatılmayan soru şuydu — kural bugünden itibaren geçerliyse, **eski
kuralla hesaplanmış on bir dönem** ne olacak.

Bu ders o soruyu ölçer. Bütün dönemleri yeniden hesaplamak koşum bedeli demektir; yalnız birkaç
dönemi yeniden hesaplamak ise raporun bir kısmını yeni, kalanını eski kuralla bırakır. İkinci
seçenek ucuzdur, hızlıdır ve ortaya çıkardığı rapor **kendi içinde tutarlı görünür**. Bu kursun
ikinci iddiasının en net örneği budur.

- **DN49.** Kaynak ve beş aşamalı hat **kurgudur**; gerçek bir depo, dönüştürme çerçevesi ya da
  orkestratör çağrılmaz. Tohum **20260218**, rapor koşumdan bağımsızdır.
- **DN50.** **Geri doldurma (backfill)**, değişen bir kuralın ya da düzeltilen bir verinin **geçmiş
  dönemlere** uygulanıp o dönemlerin yeniden hesaplanmasıdır. Ölçülen şey hangi dönemlerin
  yeniden hesaplandığıdır.
- **DN51.** Kural değişikliği bir bozulma değil **karardır**: işyeri tüketimi 1,00 yerine 1,05
  katsayıyla raporlanacak. Yeni kural doğru kabul edilir; tartışılan şey kapsamıdır.
- **DN52.** Taban çizgisi **geri doldurmasız koşumdur**: hiçbir geçmiş dönem yeniden hesaplanmaz,
  rapor bütünüyle eski kuralla durur.
- **DN53.** Yeniden işleme ile yinelemeye dayanıklılık Veri Mühendisliğine Giriş kursunda **satır
  düzeyinde** ölçüldü ve **tekrarlanmaz**; burada ölçülen **dönem düzeyidir**.
- **DN54.** Bedel **dönem** ve **satır** olarak yazılır, ham süre yazılmaz.
- **DN55.** Tüketim dönem farkından hesaplandığı için bir dönemi yeniden hesaplamak **bir önceki
  dönemin** kaynağını da okumayı gerektirir; pencere kaynakta bir dönem geniştir.
- **DN56.** Çözünürlük: tüketim satırı **2200**, özet **55 hücre**, sunum **5 değer**. **Sunum
  üzerinde oran basılmaz**, değerin kendisi yazılır.

#### Geri Doldurmanın Kapsam Sorusu

Bir kural değiştiğinde üç seçenek vardır ve üçü de savunulabilir. **Hiç geri doldurmamak** raporu
tutarlı bırakır ama bugünden öncesini yanlış kuralla anlatır. **Tümünü geri doldurmak** raporun
tamamını yeni kurala taşır ve on iki dönemin hepsini yeniden hesaplatır. **Kısmen geri doldurmak**
en ucuzudur ve genellikle bir gerekçesi de vardır: yalnız denetlenen dönemler, yalnız açık kalan
çeyrek, yalnız veri hâlâ elde olan pencere.

Kısmi seçeneğin sorunu bedelinde değil **görünmezliğindedir**. Ortaya çıkan rapor iki kuralın
birleşimidir, ama satırların hiçbiri hangi kuralla hesaplandığını taşımaz. Aşağıdaki blok kaynağı,
beş aşamayı ve kuralı seçilen dönemlere uygulayan geri doldurma işlevini kurar.

```python
# KURGUDUR. Olcum aginin sayac okumalari ve bes asamali hat ders icinde
# tanimlanir; gercek bir depo, cerceve ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    """Zorunlu alanlari tasimayan satir dusurulur."""
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    """Sunum: bolge basina donem ortalamasi."""
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def geri_doldur(kayitlar, donemler, kural_yeni=True):
    """Kural degisti: isyeri tuketimi 1,00 yerine 1,05 katsayiyla raporlanacak.
    Yalnizca secilen donemler yeniden hesaplanir."""
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    yeni = []
    for k in v:
        kat = 1.05 if (kural_yeni and k["donem"] in donemler and k["tip"] == "isyeri") else 1.0
        yeni.append(dict(k, m3=k["m3"] * kat))
    oz, _ = a4_ozet(yeni)
    sun, _ = a5_sun(oz)
    return sun
```

#### Üç Rapor ve İki Bedel

İkinci blok aynı kaynaktan üç rapor üretir: kural hiç uygulanmamış, on iki dönemin tamamına
uygulanmış ve yalnız `2026-07` ile `2026-08` dönemlerine uygulanmış. Ardından iki geri doldurma
penceresinin bedeli sayılır.

```python
PENCERE = ["2026-07", "2026-08"]
KAYNAK = kaynak_uret()


def fark(a, b):
    return {k: round(a[k] - b[k], 2) for k in a}


ESKI = geri_doldur(KAYNAK, [], kural_yeni=False)
TAM = geri_doldur(KAYNAK, DONEM)
KARISIK = geri_doldur(KAYNAK, PENCERE)
print("eski kural     :", ESKI)
print("tam uygulama   :", TAM)
print("iki donem      :", KARISIK)
print("karisik - tam  :", fark(KARISIK, TAM))
print("karisik - eski :", fark(KARISIK, ESKI))


def bedel(donemler):
    """Yeniden hesaplanan donem , yazilan tuketim satiri , okunan kaynak donemi ve satiri."""
    i = min(DONEM.index(d) for d in donemler)
    okunan = sorted(set(donemler) | {DONEM[max(i - 1, 0)]})
    yazilan = 200 * sum(1 for d in donemler if d != DONEM[0])
    return len(donemler), yazilan, len(okunan), 200 * len(okunan)


print()
print(f"{'pencere':<13}{'donem':>7}{'yazilan':>9}{'okunan donem':>14}{'okunan satir':>14}")
for ad, dl in (("tum donemler", DONEM), ("iki donem", PENCERE)):
    print(f"{ad:<13}" + "".join(f"{x:>{w}}" for x, w in zip(bedel(dl), (7, 9, 14, 14))))
```

```
eski kural     : {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
tam uygulama   : {'merkez': 1357.32, 'kuzey': 1235.57, 'dogu': 902.13, 'bati': 859.22, 'guney': 1095.81}
iki donem      : {'merkez': 1327.75, 'kuzey': 1216.36, 'dogu': 885.15, 'bati': 837.77, 'guney': 1070.65}
karisik - tam  : {'merkez': -29.57, 'kuzey': -19.21, 'dogu': -16.98, 'bati': -21.45, 'guney': -25.16}
karisik - eski : {'merkez': 6.3, 'kuzey': 3.81, 'dogu': 3.79, 'bati': 4.68, 'guney': 5.1}

pencere        donem  yazilan  okunan donem  okunan satir
tum donemler      12     2200            12          2400
iki donem          2      400             3           600
```

Bedel tablosu kısmi seçeneğin neden seçildiğini gösterir: iki dönemlik pencere **2 dönem** ve
**400 satır** yeniden hesaplarken tam uygulama **12 dönem** ve **2200 satır** hesaplıyor. Okunan
kaynak sütunu dönemler arası bağımlılığın bedelidir: iki dönemi yeniden hesaplamak için **üç
dönemin** kaynağı okunuyor, çünkü `2026-07` tüketimi `2026-06` endeksine dayanır. Pencere kaynakta
her zaman bir dönem geniştir ve bu genişlik pencerenin başında ödenir.

Sapma satırı dersin ana sayısıdır. Karışık rapor tam uygulamadan **−16,98 ile −29,57 arasında**
sapıyor; beş bölgenin hiçbiri tam uygulamanın verdiği değerde değil. Karşılığında karışık rapor
eski kuraldan da farklı — beş değerin hepsi **3,79 ile 6,30 arasında** yukarıda. Yani ortaya çıkan
rapor **ne eski kuralın raporudur ne yeni kuralın**; üçüncü bir şeydir ve o üçüncü şeyin
karşılığında gerçek dünyada hiçbir kural yoktur.

Sapmanın kaynağı veri değildir. Üç raporun tamamı aynı 2400 kaynak satırından, aynı beş aşamayla
üretildi; hiçbir satır kaybolmadı, hiçbir satır bozulmadı. Farkı yaratan tek şey **hangi dönemlerin
yeniden hesaplandığıdır**.

#### Karışık Rapor Hangi Denetimden Geçiyor

Üçüncü blok karışık raporu bir önceki dersin altı beklentisiyle sınar, raporun kendi içinde tutarlı
olup olmadığına bakar ve kuralın hücrede yarattığı sıçramayı bölgenin **doğal dönem oynaklığıyla**
karşılaştırır. Bu son karşılaştırma en iyimser durumu temsil eder: bir gözlemci raporu dönem dönem
inceleyip sıçrama arasa ne görürdü.

```python
import statistics


def yeniden_hesapla(kayitlar, donemler):
    """Ayni kural , iki fark: satirlar donuyor ve her satira kural surumu damgalaniyor."""
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return [dict(k, kural="v2" if k["donem"] in donemler else "v1",
                 m3=k["m3"] * (1.05 if (k["donem"] in donemler and k["tip"] == "isyeri") else 1.0))
            for k in v]


def beklentiler(v):
    """Bir onceki dersin alti beklentisi , donusumun kendi ciktisi uzerinde."""
    oz = a4_ozet(v)[0]
    ad = [(k["abone"], k["donem"]) for k in v]
    d = {"anahtar_tekil": len(ad) != len(set(ad)),
         "negatif_yok": any(k["m3"] < 0 for k in v),
         "tavan": any(k["m3"] > 200 for k in v),
         "satir_orani": abs(len(v) - 2200) / 2200 > 0.02,
         "hucre_tamligi": len(oz) != 55, "oynaklik": False}
    for b in BOLGE:
        h = [x for (bb, _), x in oz.items() if bb == b]
        if any(abs(x - statistics.median(h)) / statistics.median(h) > 0.30 for x in h):
            d["oynaklik"] = True
    return [a for a, s in d.items() if s], oz


BOZULAN, OZ_KAR = beklentiler(yeniden_hesapla(KAYNAK, PENCERE))
OZ_ESKI = a4_ozet(yeniden_hesapla(KAYNAK, []))[0]
print("karisik raporda bozulan beklenti:", BOZULAN or "yok")
print("rapor kendi icinde tutarli mi:",
      all(abs(KARISIK[b] - round(sum(x for (bb, _), x in OZ_KAR.items() if bb == b) / 11, 2)) < 0.01
          for b in BOLGE))
for b in ("merkez", "dogu"):
    dogal = [abs(OZ_ESKI[(b, DONEM[i])] - OZ_ESKI[(b, DONEM[i - 1])]) / OZ_ESKI[(b, DONEM[i - 1])]
             for i in range(2, 12)]
    kural = [(OZ_KAR[(b, d)] - OZ_ESKI[(b, d)]) / OZ_ESKI[(b, d)] for d in PENCERE]
    print(f"{b:<7} dogal degisim ortanca={statistics.median(dogal):.4f} "
          f"en buyuk={max(dogal):.4f}  kuralin sicramasi={max(kural):.4f}")
```

```
karisik raporda bozulan beklenti: yok
rapor kendi icinde tutarli mi: True
merkez  dogal degisim ortanca=0.0883 en buyuk=0.1874  kuralin sicramasi=0.0275
dogu    dogal degisim ortanca=0.0909 en buyuk=0.1541  kuralin sicramasi=0.0234
```

Üç satır aynı şeyi söyler: **hiçbir denetim bunu bildirmiyor.** Altı beklentinin hiçbiri bozulmuyor,
çünkü bozulacak bir şey yok — satır sayısı 2200, hücre sayısı 55, anahtarlar tekil, hiçbir değer
eksi ya da tavanın üstünde. Rapor kendi içinde tutarlı: her bölgenin sunum değeri kendi on bir özet
hücresinin ortalamasına eşit. Kaynağa bakan denetimler de sessiz kalır, çünkü kaynak hiç
değişmemiştir.

Son iki satır en iyimser gözlemciyi de eler. Kuralın hücrede yarattığı en büyük sıçrama merkezde
**yüzde 2,75**, doğuda **yüzde 2,34**; oysa aynı bölgelerin doğal dönem değişiminin **ortancası**
yüzde 8,83 ve yüzde 9,09, en büyüğü yüzde 18,74 ve yüzde 15,41. Kuralın izi doğal oynaklığın
ortancasının bile üçte biri kadar. Dönem dönem bakan bir gözlemci sıçramayı arasa da bulamaz, çünkü
sıçrama gürültünün içindedir.

İki koşumun da kendi içinde tutarlı görünmesi bu dersin çekirdeğidir. Ne tam uygulama ne kısmi
uygulama bir hata üretir; ikisi de sonuna kadar koşar, ikisi de temiz bir rapor basar. Aralarındaki
tek fark bir liste — hangi dönemlerin yeniden hesaplandığı — ve o liste raporun hiçbir yerinde
yazmaz.

#### Kural Sürümü Damgası

Dördüncü blok eksik olanı ekler. `yeniden_hesapla` her satıra hangi kuralla hesaplandığını yazıyor;
raporda kaç sürüm bulunduğuna bakmak yeterli olur. Aynı blok kursun kuralını da sınar: geri doldurma
iki kez koşturulduğunda ne oluyor.

```python
V_TAM = yeniden_hesapla(KAYNAK, DONEM)
V_KAR = yeniden_hesapla(KAYNAK, PENCERE)
for ad, v in (("tam", V_TAM), ("karisik", V_KAR)):
    s = sorted(set(k["kural"] for k in v))
    print(f"{ad:<8} kural surumu={str(s):<14} raporda tek surum mu:", len(s) == 1)
print("kaynaktan iki kez kosuldu:",
      geri_doldur(KAYNAK, PENCERE) == geri_doldur(KAYNAK, PENCERE))
yamali = [dict(k, m3=k["m3"] * (1.05 if k["donem"] in PENCERE and k["tip"] == "isyeri" else 1.0))
          for k in V_KAR]
print("cikti uzerine ikinci kez:", fark(a5_sun(a4_ozet(yamali)[0])[0], KARISIK))
```

```
tam      kural surumu=['v2']         raporda tek surum mu: True
karisik  kural surumu=['v1', 'v2']   raporda tek surum mu: False
kaynaktan iki kez kosuldu: True
cikti uzerine ikinci kez: {'merkez': 6.62, 'kuzey': 4.0, 'dogu': 3.98, 'bati': 4.92, 'guney': 5.35}
```

Damga tek bir alandır ve karışıklığı tek bir karşılaştırmaya indirir: raporda iki sürüm varsa rapor
karışıktır. Bedeli de tek bir alandır, 2200 satırın her birine yazılan bir dize. Damganın yazılma
anı önemlidir; hesap yapıldıktan sonra hangi satırın hangi kuralla üretildiği geriye dönük
çıkarılamaz, çünkü satırda kalan tek şey sonucun kendisidir.

Son iki satır kursun kuralını iki yönden gösterir. Geri doldurma **kaynaktan** hesaplandığında iki
kez koşturmak sonucu değiştirmez; işlev **yinelemeye dayanıklıdır** ve rapor koşum sayısından
bağımsızdır. Aynı düzeltme **çıktının üzerine yama** olarak uygulandığında ikinci koşum katsayıyı
bir kez daha çarpar ve rapor 3,98 ile 6,62 arasında bir kez daha kayar. İki yol da aynı sonucu
hedefler, biri yeniden koşturulabilir, öbürü yalnızca **bir kez** doğrudur — ve hangisinin
koşulduğunu rapora bakarak ayırt etmenin yolu yoktur.

#### Özet

- Geri doldurma kapsam kararıdır: tam uygulama 12 dönem ve 2200 satır yeniden hesaplatırken iki
  dönemlik pencere 2 dönem ve 400 satırla yetiniyor, ama kaynakta **üç dönem** okunuyor çünkü
  tüketim bir önceki dönemin endeksine dayanır.
- Kısmi geri doldurmanın raporu tam uygulamadan **−16,98 ile −29,57 arasında** sapıyor ve eski
  kuraldan da 3,79 ile 6,30 arasında ayrılıyor; ortaya çıkan rapor iki kuralın hiçbirine ait değil.
- Sapmanın kaynağı veri değil **hangi dönemlerin yeniden hesaplandığıdır**; üç rapor da aynı 2400
  kaynak satırından, tek satır kaybetmeden üretiliyor.
- Karışık rapor altı beklentinin hiçbirini bozmuyor, kendi içinde tutarlı çıkıyor ve kuralın
  merkez hücrelerinde yarattığı en büyük sıçrama (yüzde 2,75) doğal dönem oynaklığının
  ortancasının (yüzde 8,83) çok altında kaldığı için dönem dönem bakan gözlemciye de görünmüyor.
- Satıra kural sürümü damgalandığında karışıklık tek bir karşılaştırmayla görünür oluyor; geri
  doldurma kaynaktan hesaplandığında yinelemeye dayanıklıdır, çıktı üzerine yama olarak
  uygulandığında ikinci koşum raporu 3,98 ile 6,62 arasında bir kez daha kaydırır.

#### Sonraki Adım

On bir derste hat kuruldu: veri altı ayrı yoldan alındı, kaybının ve yinelenmesinin sunuma ne
yaptığı sayıldı, dönüşümün nerede yapılacağı, hangi sırayla koşacağı, hangi katmanda duracağı,
nasıl sınanacağı ve geçmişe nasıl uygulanacağı yazıldı. Bu on bir dersin tamamında hattı çalıştıran
tek şey vardı: **bir insanın elle verdiği başlatma kararı**. Hangi görevin ne zaman koşacağı, bir
görev düştüğünde ardındakilerin ne yapacağı, aynı işin iki kez tetiklenip tetiklenmediği ve bu
dersin kural sürümü damgasını **kimin yazacağı** hiç sorulmadı. Sonraki konu hattı bir görev
çizgesine çevirir ve çizgenin şeklinin bir başarısızlığı nereye kadar taşıdığını ölçer.

## Orkestrasyon ve Gözlem

Bağımlılıklı bir iş akışının çizge olarak kurulmasının ve bir görev düştüğünde geri kalanın ne yaptığının sayılması: 48 düğümlü ve 47 kenarlı çizgede tüketim görevinin bir önceki dönemin ayıklamasına da bağlı olması ve ilk dönemin alma görevi düştüğünde beş görevin hiç koşamaması, zaman, olay ve veri hazırlığı tetikleyicilerinin aynı işi kaç kez ve ne zaman çalıştırdığı, yeniden denemenin yinelemeye dayanıklı olmayan hatta çoğalttığı satır ile uyarı eşiğinin kaçırdığı başarısızlık, yarım yazılan bölümün sunumu her bölgede yanlışlaması buna karşılık atomik yazımda bölümün hiç yazılmayarak dönem duyarlı bütünlük denetimine takılması, tazelik, hacim ve şema değişimi göstergelerinin bir bozulmayı kaç dönem sonra göstermesi, hat kodunun ortam ve sürüm yönetiminde aynı kodun iki ortamda ürettiği farklı sonuç.

### İş Akışı Zamanlayıcıları

Source: https://academia.sh/tr/kurslar/veri-hatlari/is-akisi-zamanlayicilari

Hattın adımları arasındaki bağımlılığın çizge olarak yazılmasının ve bir görev düştüğünde geri kalanın kaçının hiç koşamadığının ölçülmesi: on iki dönem ile dört görev türü 48 düğüm ve 47 kenar veriyor, tüketim görevi bir önceki dönemin ayıklamasına da bağlı olduğu için ilk dönemin alma görevi düştüğünde 5 görev, yedinci dönemin ayıklaması düştüğünde 4, tüketimi düştüğünde 1 görev hiç koşamıyor, dönemler arası on bir kenar yazılmadığında toplam bulaşma kapsamı 116'dan 72'ye iniyor ve 44 görev yeniden koşturulmadan geride kalıyor, elle koşumda ise düşen tek görev için 48 görevin tamamı yeniden koşturuluyor.

On bir derste hat kuruldu: kaynaktan alındı, dönüştürüldü, katmanlara ayrıldı ve geçmişe yeniden
uygulandı. Bütün bu ölçümlerde tek bir sessiz varsayım vardı — hattı biri çalıştırıyor, aşamalar
sırayla koşuyor, hepsi bitiyor. Varsayım elle koşturulan bir hat için doğrudur, çünkü elle koşturan
kişi sırayı kendi kafasında tutar ve bir adım hata verdiğinde ne yapacağına o anda karar verir.

Bu ders o sırayı kafadan çıkarıp yazıya döker. İki soru sorulur: hattın adımları arasındaki
bağımlılık eksiksiz yazıldığında ne büyüklükte bir yapı çıkıyor ve o yapının tek bir düğümü
düştüğünde geri kalanın kaçı hiç koşamıyor. İkinci sorunun yanıtı **bulaşma kapsamıdır**
(blast radius) ve bu kursun ilk sayısıdır.

- **OR1.** Kaynak, hat ve görev çizgesi **kurgudur**; gerçek bir orkestratör, iş akışı ürünü ya da
  zamanlayıcı ürünü çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **OR2.** Kursun kuralı: bir hattın sayısı bir kez doğru koşması değil, **yeniden koşturulduğunda
  aynı sonucu vermesidir**; yeniden koşturulamayan hat ölçülmemiş sayılır.
- **OR3.** Taban çizgisi **orkestrasyonsuz koşumdur**: tek elle tetikleme, yeniden deneme yok,
  atomik yazım yok, geri doldurma yok.
- **OR4.** **Görev**, bir aşamanın **belirli bir dönem için** koşmasıdır. Dört görev türü vardır:
  `al`, `ayikla`, `tuketim`, `ozet`.
- **OR5.** Yönlü çevrimsiz çizge (directed acyclic graph) ve topolojik sıralama Veri Yapıları
  kursunda ölçüldü; **algoritma burada tekrarlanmaz**. Çizge yalnızca **kurulur ve kullanılır**.
- **OR6.** **Bulaşma kapsamı**, bir görev düştüğünde hiç koşamayan görev sayısıdır. Tam sayıdır;
  üzerine oran basılmaz.
- **OR7.** Sayılan birimler: görev, engellenen görev, yeniden koşturulan görev, tüketim satırı.
  **Ham süre yazılmaz**; gecikme **dönem** ya da **görev** olarak yazılır.
- **OR8.** Çizgenin kendisi elle yazılır. Kenar listesi bir varsayımdır ve **eksik olabilir**.

#### Görev, Aşama Değil

Hattın beş aşaması bir iş akışı zamanlayıcısının (workflow scheduler) birimi değildir. Zamanlayıcı
için birim **görevdir**: bir aşamanın belirli bir dönem için koşması. `ayikla` bir aşamadır,
`ayikla@2026-07` bir görevdir. Aralarındaki fark ölçülebilir: aşama beş tanedir, görev on iki
dönem boyunca kırk sekiz tanedir.

Dört görev türü hattın beş aşamasıyla şöyle örtüşür. `al` kaynağın o dönemki yükünü hatta koyar;
K01'in aşama listesinde karşılığı yoktur, çünkü orada veri hazır kabul edilmişti. `ayikla` ilk iki
aşamayı, yani ayıklama ile tekilleştirmeyi tek görevde toplar. `tuketim` ve `ozet` üçüncü ile
dördüncü aşamadır. Beşinci aşama olan `sun` çizgeye girmez: sunum döneme değil, on iki dönemin
tamamına bakar ve dönem başına bir kez koşmaz.

```python
# KURGUDUR. Sayac okumalari, bes asamali hat ve gorev cizgesi ders icinde
# tanimlanir; gercek bir depo, orkestrator ya da zamanlayici cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


# ---- KURGU gorev cizgesi: donem basina dort gorev
GOREV = ("al", "ayikla", "tuketim", "ozet")
BAGIMLILIK = {"al": (), "ayikla": ("al",), "tuketim": ("ayikla",), "ozet": ("tuketim",)}


def cizge():
    """(gorev , donem) dugumleri. `tuketim` bir onceki donemin `ayikla`sina da baglidir."""
    dugum, kenar = [], []
    for d in DONEM:
        for g in GOREV:
            dugum.append((g, d))
            for o in BAGIMLILIK[g]:
                kenar.append(((o, d), (g, d)))
        i = DONEM.index(d)
        if i > 0:
            kenar.append((("ayikla", DONEM[i - 1]), ("tuketim", d)))
    return dugum, kenar


def sirala(dugum, kenar):
    """Yonlu cevrimsiz cizgede calistirma sirasi."""
    gelen = {u: 0 for u in dugum}
    ardil = {u: [] for u in dugum}
    for a, b in kenar:
        gelen[b] += 1
        ardil[a].append(b)
    hazir = [u for u in dugum if gelen[u] == 0]
    sira = []
    while hazir:
        u = hazir.pop(0)
        sira.append(u)
        for v in ardil[u]:
            gelen[v] -= 1
            if gelen[v] == 0:
                hazir.append(v)
    return sira, ardil


def etkilenen(dugum, kenar, dusen):
    """Bir gorev basarisiz olunca hangi gorevler hic kosamaz."""
    _, ardil = sirala(dugum, kenar)
    yigin, kapali = [dusen], set()
    while yigin:
        u = yigin.pop()
        for v in ardil[u]:
            if v not in kapali:
                kapali.add(v)
                yigin.append(v)
    return kapali
```

#### Dönemler Arası Kenar

Çizgenin şaşırtıcı yeri `tuketim` görevinin iki ataya birden bağlı olmasıdır: kendi döneminin
`ayikla`sına ve **bir önceki dönemin** `ayikla`sına. Bu, uydurulmuş bir bağ değildir; tüketim
aşamasının tanımından çıkar. Aşama, bir abonenin endeksinden bir önceki dönemin endeksini
çıkararak tüketimi bulur, yani önceki dönem elinde yoksa satırı hiç üretemez. İlk blok bunu
sayıyla gösterir.

```python
KAYNAK = kaynak_uret()
print(f"{'kosturulan donem':<22}{'girdi':>7}{'tuketim':>9}{'dusen':>7}")
for secim in (["2026-07"], ["2026-06", "2026-07"], DONEM):
    v = [k for k in KAYNAK if k["donem"] in secim]
    v1, _ = a1_ayikla(v)
    v2, _ = a2_tekille(v1)
    c, dusen = a3_tuketim(v2)
    ad = "+".join(secim) if len(secim) < 3 else "on iki donem"
    print(f"{ad:<22}{len(v):>7}{len(c):>9}{dusen:>7}")

DUGUM, KENAR = cizge()
SIRA, _ = sirala(DUGUM, KENAR)
print()
print("dugum:", len(DUGUM), " kenar:", len(KENAR), " siralanan gorev:", len(SIRA))
print("ilk uc:", SIRA[:3])
print("son uc:", SIRA[-3:])
```

```
kosturulan donem        girdi  tuketim  dusen
2026-07                   200        0    200
2026-06+2026-07           400      200    200
on iki donem             2400     2200      0

dugum: 48  kenar: 47  siralanan gorev: 48
ilk uc: [('al', '2026-01'), ('al', '2026-02'), ('al', '2026-03')]
son uc: [('ozet', '2026-10'), ('ozet', '2026-11'), ('ozet', '2026-12')]
```

İlk satır bağı kanıtlar: yalnız `2026-07`'yi hatta koyan bir koşum 200 satır girdiden **sıfır**
tüketim satırı üretir ve 200 satırın tamamını düşürür. İkinci satırda bir önceki dönem eklenince
aynı 200 satır hesaplanabilir hâle gelir. Yani dönemler arası kenar bir tercih değil, hattın
hesabının kendisidir; yazılmadığında çizge hattı değil, hattın bir yaklaşığını temsil eder.

Üçüncü satır aynı kuralın sağlam hattaki izidir. On iki dönemin tamamı elde olduğunda düşen satır
**sıfırdır**, ama tüketim yine de 2400 değil **2200** satır üretir: ilk dönemin 200 aboneliğinin
öncesi yoktur ve bu satırlar bir hata olarak değil, tanım gereği hesap dışında kalır. Zincirin
başındaki bu boşluk çizgede de görünür — `tuketim@2026-01` düğümü vardır ve koşar, yalnızca
üretebileceği satır yoktur.

Çizge kurulduğunda **48 düğüm** ve **47 kenar** çıkar. Dönem içinde üç kenar vardır
(`al` → `ayikla` → `tuketim` → `ozet`), on iki dönemde otuz altı eder; buna on bir dönemler arası
kenar eklenir. Sıralama kırk sekiz görevin tamamını yerleştirir, yani çizge çevrimsizdir. Sıralamayı
üreten yordamın kendisi Veri Yapıları kursunda ölçüldü ve burada tekrarlanmaz; buradaki soru
sıralamanın nasıl bulunduğu değil, çizgenin **hangi bilgiyi taşıdığıdır**.

#### Patlama Yarıçapı

Bir görev düştüğünde zamanlayıcının verdiği ilk karar, ardıllarını koşturmamaktır. Kaç görevin
koşamayacağı çizgeden okunur ve bu sayı düşen görevin çizgedeki yerine göre değişir. İkinci blok
üç düşme noktasını, aynı çizgeyi dönemler arası kenarları çıkarılmış hâliyle karşılaştırarak sayar.

```python
DUZ = [(a, b) for a, b in KENAR if a[1] == b[1]]
print("gercek kenar:", len(KENAR), " yalniz donem ici kenar:", len(DUZ))
print()
print(f"{'dusen gorev':<20}{'engellenen':>11}{'duz zincirde':>14}{'yeniden kosum':>15}")
for g, d in (("al", "2026-01"), ("ayikla", "2026-07"), ("tuketim", "2026-07"),
             ("ozet", "2026-07"), ("al", "2026-12")):
    e = len(etkilenen(DUGUM, KENAR, (g, d)))
    z = len(etkilenen(DUGUM, DUZ, (g, d)))
    print(f"{g + '@' + d:<20}{e:>11}{z:>14}{e + 1:>15}")
print()
t = sum(len(etkilenen(DUGUM, KENAR, u)) for u in DUGUM)
z = sum(len(etkilenen(DUGUM, DUZ, u)) for u in DUGUM)
print("toplam patlama yaricapi:", t, " duz zincirde:", z, " fark:", t - z)
```

```
gercek kenar: 47  yalniz donem ici kenar: 36

dusen gorev          engellenen  duz zincirde  yeniden kosum
al@2026-01                    5             3              6
ayikla@2026-07                4             2              5
tuketim@2026-07               1             1              2
ozet@2026-07                  0             0              1
al@2026-12                    3             3              4

toplam patlama yaricapi: 116  duz zincirde: 72  fark: 44

```

Tablo tek bir cümleyi sayıya çevirir: **zincirin şekli patlamanın büyüklüğünü belirler.** Aynı
hatta, aynı kırk sekiz görev arasında, düşen göreve göre engellenen görev sayısı **0** ile **5**
arasında değişiyor. `ozet@2026-07` düştüğünde hiçbir görev engellenmiyor, çünkü ardılı yok.
`tuketim@2026-07` düştüğünde yalnız kendi döneminin özeti duruyor. `al@2026-01` düştüğünde beş
görev koşamıyor ve bunların ikisi **başka bir dönemin** görevidir: dönemler arası kenar,
`2026-01`'in ayıklamasını `2026-02`'nin tüketimine bağlar.

Son sütun orkestrasyonun asıl kazancını verir. Düşen görev artı engellenen görevler, yeniden
koşturulması gereken kümedir: sırasıyla **6, 5, 2, 1** ve **4** görev. Bu sayı çizge olmadan
bilinemez.

Üçüncü sütun ise orkestrasyonun kendi kusurudur. Dönemler arası on bir kenar yazılmamış bir
çizge de kurulur, sıralanır ve çalışır; hiçbir şey hata vermez. Ama bulaşma kapsamını
`al@2026-01` için 5 yerine **3**, `ayikla@2026-07` için 4 yerine **2** gösterir. Bütün düğümler
üzerinde toplandığında gerçek çizge **116**, eksik çizge **72** verir: aradaki **44 görev**,
düştükleri gün yeniden koşturulmaz ve eski sonuçlarıyla yerlerinde kalır. Bir çizgenin çalışıyor
olması doğru olduğu anlamına gelmez.

#### Denetim Noktası

Çizge bir görev düştüğünde **neyin engellendiğini** söyler. Söylemediği şey **neyin bittiğidir**.
Zamanlayıcı her görev tamamlandığında bunu kaydeder; bu kayıt **denetim noktasıdır** (checkpoint)
ve yeniden başlatmanın nereden devam edeceğini belirler. İki bilgi birbirinin yerine geçmez: biri
aşağı doğru, öbürü geriye doğru bakar.

```python
print(f"{'dusen gorev':<20}{'tamamlanan':>11}{'noktali':>9}{'noktasiz':>10}{'engellenen':>12}")
for g, d in (("al", "2026-01"), ("ayikla", "2026-07"), ("tuketim", "2026-07")):
    i = SIRA.index((g, d))
    print(f"{g + '@' + d:<20}{i:>11}{len(SIRA) - i:>9}{len(SIRA):>10}"
          f"{len(etkilenen(DUGUM, KENAR, (g, d))):>12}")
```

```
dusen gorev          tamamlanan  noktali  noktasiz  engellenen
al@2026-01                    0       48        48           5
ayikla@2026-07               18       30        48           4
tuketim@2026-07              30       18        48           1
```

`ayikla@2026-07` düştüğünde sıralamada ondan önceki **18 görev** bitmiştir. Denetim noktası
tutuluyorsa yeniden başlatma **30 görev** koşar; tutulmuyorsa hiçbir görevin bittiği bilinmediği
için **48 görev** koşar. `tuketim@2026-07` için fark daha büyüktür: 18 görev yerine 48.

Üç satırın birlikte söylediği şey, iki sayının farklı sorulara yanıt verdiğidir. Engellenen görev
sayısı **hangi sonuçların artık geçersiz olduğunu** verir; denetim noktası **hangi işin
tekrarlanmayacağını** verir. `al@2026-01` satırı bunu tersinden gösterir: ilk görev düştüğünde
tamamlanan görev sıfırdır, denetim noktası hiçbir şey kazandırmaz, ama bulaşma kapsamı yine de
beştir. Bir zamanlayıcının iki ayrı kaydı olması bu yüzdendir.

#### Elle Koşumun Bedeli

Orkestrasyonsuz taban çizgisi bu tablonun karşısına konur. Elle koşturan kişi bağımlılığı yazıya
dökmediği için `ayikla@2026-07` düştüğünde hangi görevlerin bozulduğunu bilmez. Elinde iki seçenek
kalır: ya yalnız düşen görevi yeniden koşturur ve `2026-08`'in tüketimini eski sonucuyla bırakır,
ya da güvenli olanı yapıp **kırk sekiz görevin tamamını** yeniden koşturur. İkinci seçenek
doğrudur ve bedeli 9,6 kattır: çizgeyle 5 görev yeterken elle 48 görev koşar.

Bedelin yalnız görev sayısı olmadığı sonraki iki derste görülecek. Kırk sekiz görevi yeniden
koşturmak `al` görevini on iki kez yeniden çalıştırmak demektir ve hattın yazımı yinelemeye
dayanıklı değilse bu koşum satır çoğaltır. Çizgenin kazancı burada ikiye çıkar: hem daha az görev
koşar, hem de dokunulmayan dönemlerin verisi hiç yeniden yazılmaz.

Buna karşılık çizgenin kendisi bedava değildir. Kırk yedi kenarın tamamı elle yazılır, hattın kodu
değiştikçe elle güncellenir ve doğruluğunu denetleyen bir şey yoktur. Eksik kenarın nasıl
sessizce geçtiği yukarıda ölçüldü: sıralama yine tam, koşum yine yeşil, bulaşma kapsamı yine bir
sayı verir — yalnızca yanlış bir sayı.

#### Özet

- Orkestrasyonun birimi aşama değil **görevdir**: bir aşamanın belirli bir dönem için koşması. On
  iki dönem ve dört görev türü **48 düğüm** ile **47 kenar** verir, sıralama kırk sekizini de
  yerleştirir.
- Tüketim görevi bir önceki dönemin ayıklamasına bağlıdır ve bu bağ ölçülür: yalnız `2026-07`
  koşturulduğunda 200 satır girdiden **0** tüketim satırı çıkar, `2026-06` eklenince **200** çıkar.
- Bulaşma kapsamı düşen görevin çizgedeki yerine göre **0** ile **5** arasında değişir;
  `al@2026-01` için 5, `ayikla@2026-07` için 4, `tuketim@2026-07` için 1, `ozet@2026-07` için 0.
- Dönemler arası on bir kenar yazılmadığında çizge yine çalışır ama toplam bulaşma kapsamını
  **116** yerine **72** gösterir; aradaki **44 görev** yeniden koşturulmadan kalır.
- Denetim noktası ayrı bir sayı verir: `ayikla@2026-07` düştüğünde 18 görev bitmiştir, yeniden
  başlatma noktalı kurulumda **30**, noktasız kurulumda **48** görev koşar.
- Orkestrasyonsuz tabanda düşen tek görev için **48 görevin tamamı** yeniden koşturulur; çizgeyle
  aynı düşme için **5 görev** yeter. Bedel, kırk yedi kenarın elle yazılıp elle güncellenmesidir.

#### Sonraki Adım

Çizge sırayı veriyor ve düşen görevin kimi engellediğini söylüyor, ama bir şeyi hiç söylemiyor:
zinciri **kimin başlattığını**. Kırk sekiz görevin otuz altısı bir başka görevin bitmesiyle
tetiklenir; geriye on iki `al` görevi kalır ve onların atası yoktur. Bu görevleri ne çalıştırıyor
sorusu göründüğünden daha pahalıdır, çünkü yanıtı sabit bir takvimse görev veri gelmeden koşar ve
boş biter. Sonraki ders üç tetikleyici sınıfını aynı hatta karşılaştırır ve tek bir sayıyı ölçer:
aynı işin kaç kez ve **ne zaman** çalıştığı.

### Bağımlılık ve Tetikleme

Source: https://academia.sh/tr/kurslar/veri-hatlari/bagimlilik-ve-tetikleme

Zincirin başındaki görevi neyin başlattığının ve aynı işin kaç kez ve ne zaman koştuğunun ölçülmesi: kaynağın yükü üç dönemde bir adım gecikmeli başlayıp dört dönemde iki parça hâlinde gelirken sabit takvim 12 koşumun 3'ünü boş, 3'ünü yarım bitiriyor ve altı dönem eksik kalarak tüketimi 2200'den 900 satıra düşürüyor, takvime iki dönemlik emniyet penceresi eklendiğinde koşum 24'e çıkıyor ve on iki dönemin tamamı ikinci kez işleniyor ama bir dönem yine eksik kalıyor, veri hazırlığı tetikleyicisi 12 koşumla boş ve yarım koşum üretmeden tam sonucu veriyor ve bedeli yedi dönemlik toplam gecikme oluyor, hazırlık ölçütü sabit satır sayısına bağlandığında ise dört dönem hiç tetiklenmiyor.

Önceki ders hattı kırk sekiz görevlik bir çizge olarak kurdu ve bir görev düştüğünde kaçının hiç
koşamadığını saydı. Çizgenin söylemediği tek şey vardı: zinciri kim başlatıyor. Otuz altı görev
bir başka görevin bitmesiyle tetiklenir — bağımlılık tetiklemesi çizgenin kendisidir. Geriye
kalan on iki `al` görevinin atası yoktur ve onları çizge değil, dışarıdan bir karar çalıştırır.

Bu ders o kararı üç sınıfa ayırır ve aynı hatta ölçer. Ölçülen sayı basit bir sorunun yanıtıdır:
**aynı iş kaç kez ve ne zaman koştu.** Bir görevin fazladan koşması bedava değildir, hiç koşmaması
ise sessizdir; iki kusur aynı tabloda yan yana durur.

- **OR9.** Kaynak, hat ve varış planı **kurgudur**; gerçek bir zamanlayıcı, iş akışı ürünü ya da
  mesaj sistemi çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **OR10.** Bir **adım** bir dönemdir. **Ham süre yazılmaz**; gecikme **dönem** olarak sayılır.
- **OR11.** Kaynağın yükü ya kendi döneminde ya bir adım gecikmeli başlar ve bir ya da iki
  **parça** hâlinde gelir. İki bilgi de tohumdan türetilir, elle seçilmez.
- **OR12.** **Boş koşum**, hiçbir satır görmeden biten görevdir. **Yarım koşum**, dönemin
  satırlarının yalnız bir bölümünü gören görevdir.
- **OR13.** Bu derste yazım **anahtarlıdır**: aynı dönemin ikinci koşumu satır çoğaltmaz. Çoğalma
  sonraki dersin konusudur ve buradaki sayıları kirletmemesi için dışarıda tutulur.
- **OR14.** Taban çizgisi **orkestrasyonsuz koşumdur**: elle konmuş sabit takvim, yeniden deneme
  yok, hazırlık ölçütü yok.
- **OR15.** Kuyruk, konu ve teslim garantisi yordamı Önbellekleme, Kuyruklar ve Eşzamansız İşleme
  kursunda ölçüldü ve **tekrarlanmaz**. Buradaki soru yalnız tetikleyicinin görevi **ne zaman**
  başlattığıdır.
- **OR16.** Çözünürlük: dönem **12**, dönem başına satır **200**, sağlam tüketim **2200**.
  **Sunum üzerinde oran basılmaz**, değerin kendisi yazılır.

#### Üç Tetikleyici Sınıfı

**Zaman tetikleyicisi** (trigger) en ucuz olanıdır: görev takvimde yazılı bir anda koşar, kaynağa
hiç bakmaz. Bu, hattı elle çalıştırmanın yazıya geçmiş hâlidir ve bu dersin taban çizgisidir.
Sözleşmesi tek yönlüdür — zamanlayıcı söz verir, kaynak vermez.

**Olay tetikleyicisi** kararı kaynağa devreder: kaynak bir yük parçasını hatta bıraktığında bir
bildirim yollar, görev o bildirimle koşar. Kaynak ne zaman hazır olduğunu bilen taraf olduğu için
bu daha doğru görünür. Sorun bildirimin **neyi** haber verdiğidir: bir yük iki parça hâlinde
geliyorsa iki bildirim gider ve görev iki kez koşar.

**Veri hazırlığı tetikleyicisi** (readiness trigger) kararı hattın kendisine bırakır: görev,
dönemin yükünün tamamlandığını doğrulayana kadar bekler. Boş ve yarım koşum üretmez, ama beklediği
için gecikir ve **hazırlığın ölçütünü** yazmak gerekir. O ölçüt yanlış yazıldığında görev hiç
koşmaz.

```python
# KURGUDUR. Sayac okumalari, bes asamali hat ve varis plani ders icinde
# tanimlanir; gercek bir zamanlayici, orkestrator ya da mesaj sistemi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def okuma_sonucu(kayitlar):
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return sun, len(v)
```

#### Kurgu Varış Planı

Tetikleyicileri ayırt eden şey kaynağın davranışıdır. Kaynak takvime uysaydı üç sınıf da aynı
sonucu verirdi; ayrım kaynağın **bazen geç ve bazen bölünmüş** gelmesinden doğar. İkinci blok bu
davranışı tohumdan türetir: her dönemin yükü kendi adımında ya da bir adım sonra başlar ve bir ya
da iki parça hâlinde gelir.

```python
KAYNAK = kaynak_uret()
TABAN_SUN, TABAN_N = okuma_sonucu(KAYNAK)
UFUK = 13


def varis_plani(gec_pay=0.25, bol_pay=0.35):
    """KURGU: her donemin yuku bir ya da iki parca gelir , bazi donemler bir adim gec baslar."""
    r = uretec(TOHUM + 11)
    plan = {}
    for d in DONEM:
        bas = DONEM.index(d) + (1 if r() < gec_pay else 0)
        parca = 2 if r() < bol_pay else 1
        plan[d] = {"bas": bas, "parca": parca, "tam": bas + parca - 1}
    return plan


PLAN = varis_plani()


def gorunen(donem, adim):
    """Bu adima kadar hatta ulasmis satirlar."""
    p, hepsi = PLAN[donem], [k for k in KAYNAK if k["donem"] == donem]
    if adim < p["bas"]:
        return []
    gelen = min(p["parca"], adim - p["bas"] + 1)
    return hepsi[:len(hepsi) * gelen // p["parca"]]


def kosumlar(kip):
    """(adim , donem) ciftleri: gorev ne zaman ve hangi donem icin kosuyor."""
    if kip == "zaman":                                   # sabit takvim
        return [(t, DONEM[t]) for t in range(12)]
    if kip == "zaman_pencere":                           # takvim , son iki donem
        return sorted((t, DONEM[g]) for t in range(UFUK) for g in (t, t - 1) if 0 <= g < 12)
    if kip == "olay":                                    # her parca bir bildirim yolluyor
        return sorted((PLAN[d]["bas"] + j, d) for d in DONEM for j in range(PLAN[d]["parca"]))
    return [(PLAN[d]["tam"], d) for d in DONEM]          # veri hazirligi


def olc(kip):
    """Yazim anahtarlidir: ayni donemin ikinci kosumu satir cogaltmaz."""
    depo, bos, yarim, sayac, tam = {}, 0, 0, {}, {}
    for adim, d in kosumlar(kip):
        v = gorunen(d, adim)
        if not v:
            bos += 1
        elif len(v) < 200:
            yarim += 1
        sayac[d] = sayac.get(d, 0) + 1
        for k in v:
            depo[(k["abone"], k["donem"])] = k
        if d not in tam and sum(1 for x in depo if x[1] == d) == 200:
            tam[d] = adim - DONEM.index(d)
    sun, n = okuma_sonucu(list(depo.values()))
    return {"kosum": len(kosumlar(kip)), "bos": bos, "yarim": yarim,
            "iki_kez": sum(1 for d in sayac if sayac[d] > 1), "eksik": len(DONEM) - len(tam),
            "gecikme": sum(tam.values()), "tuketim": n,
            "fark": {b: round(sun[b] - TABAN_SUN[b], 2) for b in TABAN_SUN}}


print("gec baslayan donem:", [d for d in DONEM if PLAN[d]["bas"] != DONEM.index(d)])
print("iki parca gelen donem:", [d for d in DONEM if PLAN[d]["parca"] == 2])
print("yuk tamamlanma gecikmesi (donem):", sum(PLAN[d]["tam"] - DONEM.index(d) for d in DONEM))
```

```
gec baslayan donem: ['2026-02', '2026-10', '2026-12']
iki parca gelen donem: ['2026-01', '2026-02', '2026-06', '2026-09']
yuk tamamlanma gecikmesi (donem): 7
```

On iki dönemin üçü bir adım gecikmeli başlıyor, dördü iki parçaya bölünüyor ve toplam yedi dönemlik
bir gecikme birikiyor. Bu, bozuk bir kaynak değildir; sayaç okumalarının bir bölümünün geç
toplanması ve büyük yüklerin bölünerek gönderilmesi sıradan işlerdir. Ölçülecek olan şudur: aynı
kaynak karşısında dört tetikleyici kurulumu kaç görev koşturuyor ve elde ne bırakıyor.

#### Aynı İşin Kaç Kez ve Ne Zaman Koştuğu

Dört kurulum karşılaştırılır. `zaman` takvime bir kez bakar. `zaman + iki dönem` aynı takvime elle
konmuş bir emniyet penceresi ekler: her adımda hem o dönemi hem bir öncekini yeniden işler.
`olay` her yük parçasının bildirimiyle koşar. `veri hazırlığı` dönemin iki yüz satırı tamamlanana
kadar bekler.

```python
print(f"{'tetikleyici':<20}{'kosum':>6}{'bos':>5}{'yarim':>7}{'iki kez':>9}"
      f"{'eksik':>7}{'gecikme':>9}{'tuketim':>9}")
for kip, ad in (("zaman", "zaman"), ("zaman_pencere", "zaman + iki donem"),
                ("olay", "olay"), ("hazirlik", "veri hazirligi")):
    o = olc(kip)
    print(f"{ad:<20}{o['kosum']:>6}{o['bos']:>5}{o['yarim']:>7}{o['iki_kez']:>9}"
          f"{o['eksik']:>7}{o['gecikme']:>9}{o['tuketim']:>9}")
print()
print("zaman sapmasi          :", olc("zaman")["fark"])
print("zaman + iki donem      :", olc("zaman_pencere")["fark"])
print("olay ve veri hazirligi :", olc("hazirlik")["fark"])
```

```
tetikleyici          kosum  bos  yarim  iki kez  eksik  gecikme  tuketim
zaman                   12    3      3        0      6        0      900
zaman + iki donem       24    3      4       12      1        5     2000
olay                    16    0      4        4      0        7     2200
veri hazirligi          12    0      0        0      0        7     2200

zaman sapmasi          : {'merkez': -317.28, 'kuzey': -382.88, 'dogu': -175.69, 'bati': -240.59, 'guney': -251.72}
zaman + iki donem      : {'merkez': -100.63, 'kuzey': -145.19, 'dogu': -65.54, 'bati': -86.82, 'guney': -80.46}
olay ve veri hazirligi : {'merkez': 0.0, 'kuzey': 0.0, 'dogu': 0.0, 'bati': 0.0, 'guney': 0.0}
```

İlk satır taban çizgisidir ve tablodaki en yanıltıcı satırdır. Sabit takvim **gecikme sütununda
sıfır** yazar; hiçbir dönem geç işlenmemiştir, çünkü hiçbir dönem beklenmemiştir. Aynı satırda
**altı dönem eksik** kalır: on iki koşumun üçü boş biter, üçü yarım. Tüketim 2200 yerine **900**
satır çıkar ve sunum beş bölgenin hepsinde çöker (−175,69 ile −382,88 arasında). **Sıfır gecikme,
beklemeyi ölçtüğü için değil, hiç beklemediği için sıfırdır.**

İkinci satır bu kusura sahada verilen yanıttır: takvimi bırakmadan bir emniyet penceresi eklemek.
Koşum sayısı 12'den **24**'e çıkar ve **on iki dönemin tamamı ikinci kez işlenir** — yani hattın
yarısı gereksiz iştir. Karşılığında eksik dönem altıdan bire iner ve tüketim 2000'e çıkar. Ama
kalan tek dönem cezasını ödetmeye devam eder: `2026-02` hem bir adım gecikmeli başlar hem iki
parçaya bölünür, üçüncü adımda tamamlanır ve iki dönemlik pencere üçüncü adımda ona artık bakmaz.
Sunum hâlâ her bölgede yanlıştır (−65,54 ile −145,19 arasında). **Pencereyi genişletmek bir sınır
seçmektir ve seçilen her sınır bir varış planını dışarıda bırakır.**

Üçüncü satır olay tetikleyicisinin hem gücünü hem kusurunu gösterir. Eksik dönem sıfırdır, sunum
sapması boştur, sonuç tamdır. Buna karşılık **16 koşum** yapılmıştır: iki parça gelen dört dönem
iki kez işlenmiştir ve o dördün ilk koşumu **yarım** koşumdur. Bu ders yazımı anahtarlı tuttuğu
için yarım koşum zarar vermiyor; sonraki ders aynı yarım koşumun yazım anahtarsız olduğunda ne
yaptığını ölçecek.

Dördüncü satır aranan kurulumdur ve neyin satın alındığını açıkça yazar. Veri hazırlığı
tetikleyicisi **12 koşumla** sonucu tam verir: boş koşum yok, yarım koşum yok, ikinci kez işlenen
dönem yok. Ödediği bedel **gecikme sütunundaki yedidir** — yükün tamamlanmasını beklediği için
toplam yedi dönem geç bitirmiştir. Bu sayı kaynağın gecikmesiyle birebir aynıdır, yani tetikleyici
kendi başına gecikme eklemez; yalnızca kaynağın gecikmesini **görünür kılar**.

#### Hazırlık Ölçütünün Kendi Kusuru

Veri hazırlığı tetikleyicisinin bedeli yalnız gecikme değildir. Görevin ne zaman koşacağını artık
bir **ölçüt** belirler ve o ölçüt elle yazılır. Yukarıdaki kurulumda ölçüt "iki yüz satır geldi"
biçimindeydi; iki yüz sayısı kaynağın bugünkü abone sayısıdır ve bir varsayımdır.

```python
# Dokuzuncu donemden itibaren dort abone aboneligini sonlandirdi: kaynak 196 satir gonderiyor.
AYRILAN = {"A197", "A198", "A199", "A200"}
KAYNAK2 = [k for k in KAYNAK if not (k["donem"] >= "2026-09" and k["abone"] in AYRILAN)]
SAYI = {d: sum(1 for k in KAYNAK2 if k["donem"] == d) for d in DONEM}
print("donem basina satir:", sorted(set(SAYI.values())), " toplam:", len(KAYNAK2))
print(f"{'hazirlik olcutu':<28}{'kosan donem':>12}{'satir':>7}{'tuketim':>9}")
sonuc = {}
for ad, esik in (("kaynagin bildirdigi parca", 0), ("sabit 200 satir", 200)):
    kosan = [d for d in DONEM if SAYI[d] >= esik]
    kabul = [k for k in KAYNAK2 if k["donem"] in kosan]
    sun, n = okuma_sonucu(kabul)
    sonuc[ad] = sun
    print(f"{ad:<28}{len(kosan):>12}{len(kabul):>7}{n:>9}")
a, b = sonuc["kaynagin bildirdigi parca"], sonuc["sabit 200 satir"]
print("sabit esigin sapmasi:", {x: round(b[x] - a[x], 2) for x in a})
```

```
donem basina satir: [196, 200]  toplam: 2384
hazirlik olcutu              kosan donem  satir  tuketim
kaynagin bildirdigi parca             12   2384     2184
sabit 200 satir                        8   1600     1400
sabit esigin sapmasi: {'merkez': -37.59, 'kuzey': -10.75, 'dogu': -18.5, 'bati': 8.11, 'guney': -13.98}
```

Dört abone aboneliğini sonlandırdığı için kaynak dokuzuncu dönemden sonra 196 satır gönderiyor.
Bu bir bozulma değildir; kaynak doğru veriyi eksiksiz yolluyor. Sabit eşiğe bağlanmış hazırlık
ölçütü ise ikinci yüzüncü satırı beklediği için **dört dönemde hiç tetiklenmiyor**: koşan dönem
12 yerine **8**, tüketim 2184 yerine **1400**. Hiçbir görev hata vermiyor, hiçbir günlük satırı
yazılmıyor, hattın durumu bekliyor olarak kalıyor.

Sapma satırı bu kusurun sunumda nasıl göründüğünü söyler: merkez −37,59 düşerken **batı +8,11
yükseliyor**. Kayıp dönemler bölgelere eşit dağılmadığı için sapmanın işareti bile tutarlı
değildir, yani sunuma bakarak "dört dönem eksik" sonucuna varmak olanaksızdır. Doğru ölçüt sabit
bir sayı değil, kaynağın **kendi bildirdiği parça tamlığıdır**; o kullanıldığında on iki dönemin
tamamı koşar ve tüketim 2184 satırda kalır.

#### Özet

- Çizgedeki otuz altı görev bağımlılıkla tetiklenir; on iki `al` görevini dışarıdan bir karar
  başlatır ve bu karar zaman, olay ya da veri hazırlığı tetikleyicisidir.
- Taban çizgisi olan sabit takvim **gecikme sütununda sıfır** yazar ama **altı dönemi eksik**
  bırakır: 12 koşumun 3'ü boş, 3'ü yarım biter ve tüketim 2200 yerine **900** satır olur.
- Takvime iki dönemlik emniyet penceresi eklemek koşumu **24**'e çıkarır ve on iki dönemin tamamını
  ikinci kez işletir; eksik dönem bire iner ama sunum hâlâ her bölgede yanlıştır.
- Olay tetikleyicisi tam sonucu verir, bedeli **16 koşum** ve dört dönemin ikinci kez işlenmesidir;
  iki parça gelen dört dönemin ilk koşumu **yarım koşumdur**.
- Veri hazırlığı tetikleyicisi **12 koşumla** boş ve yarım koşum üretmeden tam sonucu verir; bedeli
  **yedi dönemlik toplam gecikmedir** ve bu gecikme kaynağınkiyle birebir aynıdır.
- Hazırlık ölçütü sabit satır sayısına bağlandığında dört dönem **hiç tetiklenmez**: tüketim
  2184'ten 1400'e iner, sapmanın işareti bölgeye göre değişir ve hiçbir görev hata vermez.

#### Sonraki Adım

Bu dersteki bütün koşumların ortak bir yanı vardı: hiçbiri başarısız olmadı. Görev ya koştu ya hiç
tetiklenmedi; koştuğunda gördüğü satırların tamamını yazdı. Gerçek bir hatta üçüncü bir sonuç
vardır — görev koşar, yazmaya başlar ve **ortasında düşer**. Yarım koşum bu derste zararsızdı,
çünkü yazım anahtarlıydı ve eksik kalan parça bir sonraki koşumda tamamlandı. Sonraki ders o
güvenceyi kaldırır ve iki soruyu ölçer: yarım yazılan bir bölüm sunuma ne yapıyor ve düşen görevi
yeniden denemek hattı onarıyor mu yoksa satır mı çoğaltıyor.

### Yeniden Deneme ve Uyarı

Source: https://academia.sh/tr/kurslar/veri-hatlari/yeniden-deneme-ve-uyari

Bir görevin ortasında düşmesinin sunuma ne yaptığının ve yeniden denemenin hattı onarıp onarmadığının ölçülmesi: yedinci dönemin bölümü 120 satırda yarım yazıldığında ham satır 2320'de kalıyor, satır sayısı ile dönem duyarlı bütünlük denetimlerinin ikisi de yeşil kalıyor ama sunum her bölgede yanlışlıyor (−49,09 ile −121,82 arası), atomik yazımda aynı görev hiçbir satır bırakmıyor, tüketim 2200'den 1800'e iniyor, sapma −3,25 ile +12,80 arasında kalıyor ve iki denetim de ateşliyor, üç denemeli yeniden deneme ekleyen yazımda 240 satır çoğaltıp deponun ham sayısını 2640'a çıkarıyor, anahtarlı yazımda çoğalma sıfır oluyor ve yeniden deneme başarılı olduğunda görev uyarısı susuyor.

Önceki dersteki bütün koşumların ortak bir yanı vardı: hiçbiri başarısız olmadı. Görev ya koştu ya
hiç tetiklenmedi, koştuğunda gördüğü satırların tamamını yazdı. Yarım koşum orada zararsızdı,
çünkü yazım anahtarlıydı ve eksik parça bir sonraki koşumda tamamlanıyordu. Bu ders o güvenceyi
kaldırır.

Gerçek bir hatta üçüncü bir sonuç vardır: görev koşar, yazmaya başlar ve **ortasında düşer.**
Geriye kalan şey ne boş bir bölümdür ne tam bir bölüm. Bu ders iki soruyu ölçer — yarım yazılan
bir bölüm sunuma ne yapıyor ve düşen görevi yeniden denemek hattı onarıyor mu yoksa satır mı
çoğaltıyor. İlk sorunun yanıtı kursun üçüncü iddiasının en güçlü örneğidir.

- **OR17.** Kaynak, hat ve görev koşumu **kurgudur**; gerçek bir orkestratör, depo ya da uyarı
  servisi çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **OR18.** **Atomik yazım** (atomic write), bir görevin ya bütün satırlarını yazması ya
  hiçbirini bırakmaması demektir. Atomik olmayan yazımda yarım kalan satırlar okunabilir durumda
  kalır.
- **OR19.** Görev `2026-07` bölümünü yazarken **120. satırda** düşüyor. Bu sayı bir yer seçimidir;
  ölçülen şey sayının kendisi değil, yarım ile hiçin farkıdır.
- **OR20.** Yeniden deneme, geri çekilme, devre kesici ve zarif bozulma yordamı Önbellekleme,
  Kuyruklar ve Eşzamansız İşleme ile Dayanıklılık ve Güvenilirlik kurslarında ölçüldü ve
  **tekrarlanmaz**. Buradaki soru yalnız yeniden denemenin **satır çoğaltıp çoğaltmadığıdır**.
- **OR21.** `idempotent` karşılığı **yinelemeye dayanıklıdır**. Yeniden işlemenin satır çoğaltması
  Veri Mühendisliğine Giriş kursunda ölçüldü; buradaki ölçüm aynı olguyu **görev düzeyinde**
  yapar.
- **OR22.** İki uyarı kaynağı ayrılır: **görev uyarısı** son denemeden sonra görev hâlâ düşükse
  gider, **veri uyarısı** denetimlerden gelir.
- **OR23.** Taban çizgisi **orkestrasyonsuz koşumdur**: atomik yazım yok, yeniden deneme yok,
  uyarı yok.
- **OR24.** Sayılan birimler: yazılan satır, çoğalan satır, tüketim satırı, sunum sapması, deneme.
  **Ham süre yazılmaz.** Sunum üzerinde oran basılmaz, değerin kendisi yazılır.

#### Görevin Ortasında Düşmek

Bir bölümü yazan görev tek bir işlem değildir; satırları sırayla yazar ve her satırdan sonra
düşebilir. Ne bıraktığı yazımın atomik olup olmadığına bağlıdır. Atomik yazımda yarım kalan iş
**hiç görünmez** — görev düşer, bölüm boş kalır, hat o dönemi yokmuş gibi okur. Atomik olmayan
yazımda ilk 120 satır orada durur ve hattan geçerken tam satırlardan ayırt edilemez.

Ölçüm iki denetimle yapılır. **Satır sayısı denetimi** ham satır sayısının beklenenden yüzde
beşten fazla sapıp sapmadığına bakar. **Dönem duyarlı bütünlük denetimi** her bölge ile her dönemin
çiftinin veride bulunup bulunmadığına bakar; K01 bu denetimi kusurlu kardeşinden ayırmıştı ve
burada doğru olan kullanılır.

```python
# KURGUDUR. Sayac okumalari, bes asamali hat ve gorev kosumu ders icinde
# tanimlanir; gercek bir depo, orkestrator ya da uyari servisi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


def okuma_sonucu(kayitlar):
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return sun, len(v)


def bolum_yaz(kayitlar, donem, kesme=None, atomik=False):
    """kesme: bu satir sayisindan sonra gorev duser. atomik: yarim yazim gorunmez."""
    hedef = [k for k in kayitlar if k["donem"] == donem]
    if kesme is None or kesme >= len(hedef):
        return hedef, False
    yarim = hedef[:kesme]
    return ([], True) if atomik else (yarim, True)


def d_satir_sayisi(veri, beklenen=2400, pay=0.05):
    return abs(len(veri) - beklenen) / beklenen > pay


def d_bolum_tamligi_donem(veri):
    """Her donemde her bolge var mi."""
    gor = set((k["bolge"], k["donem"]) for k in veri)
    return gor != set((b, d) for b in BOLGE for d in DONEM)
```

#### Yarım ile Hiç

İkinci blok aynı düşmeyi iki yazım biçiminde koşturur ve dört şeyi yan yana koyar: bölüme yazılan
satır, deponun ham satırı, tüketim satırı ve sunumun tabandan sapması.

```python
KAYNAK = kaynak_uret()
TABAN_SUN, TABAN_N = okuma_sonucu(KAYNAK)
OTEKI = [k for k in KAYNAK if k["donem"] != "2026-07"]
print("taban tuketim:", TABAN_N)
print("taban sunum:", TABAN_SUN)
print(f"{'kurulum':<16}{'yazilan':>8}{'ham':>6}{'tuketim':>9}{'satir':>7}{'butunluk':>10}")
for ad, atomik in (("atomik degil", False), ("atomik", True)):
    yaz, dustu = bolum_yaz(KAYNAK, "2026-07", 120, atomik)
    depo = OTEKI + yaz
    sun, n = okuma_sonucu(depo)
    print(f"{ad:<16}{len(yaz):>8}{len(depo):>6}{n:>9}"
          f"{str(d_satir_sayisi(depo)):>7}{str(d_bolum_tamligi_donem(depo)):>10}")
    print("   sapma:", {b: round(sun[b] - TABAN_SUN[b], 2) for b in TABAN_SUN})
```

```
taban tuketim: 2200
taban sunum: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
kurulum          yazilan   ham  tuketim  satir  butunluk
atomik degil         120  2320     2040  False     False
   sapma: {'merkez': -92.63, 'kuzey': -121.82, 'dogu': -49.09, 'bati': -49.27, 'guney': -61.55}
atomik                 0  2200     1800   True      True
   sapma: {'merkez': -2.89, 'kuzey': 6.12, 'dogu': -3.25, 'bati': 12.8, 'guney': 7.01}
```

Birinci satır kursun üçüncü iddiasının en güçlü örneğidir. Yarım yazılan bölüm sunumu **beş
bölgenin hepsinde** yanlışlar: sapma −49,09 ile −121,82 arasındadır ve hiçbiri küçük değildir.
Buna karşılık **her iki denetim de yeşildir.** Ham satır 2320'dir, beklenen 2400'den yüzde 3,33
sapar ve yüzde beşlik bandın içinde kalır. Dönem duyarlı bütünlük denetimi de geçer, çünkü yazılan
ilk 120 satır beş bölgenin hepsinden abone taşır — `2026-07` dönemi her bölge için veride vardır,
yalnızca eksiktir. **Sayı makul görünür, sunum yanlıştır ve hiçbir denetim bunu söylemez.**

Bandı daraltmak ilk akla gelen çözümdür ve kendi bedelini getirir. Bu yarım yazımı görmek için
bandın yüzde 3,33'ün altına inmesi gerekir. Önceki derste dört abone aboneliğini sonlandırmıştı ve
bu, dört dönemde 16 satır, yani ham satırın yüzde 0,67'si demektir; bandın o değişikliğe
ateşlememesi için yüzde 0,67'nin üstünde kalması gerekir. **Eşik ayarlanabilir ama ancak yüzde 0,67
ile 3,33 arasında; o aralığı seçmek yakalanacak hatanın da görmezden gelinecek hareketin de
büyüklüğünü önceden bilmeyi gerektirir.**

İkinci satır ilk bakışta daha kötü görünür. Atomik yazımda bölüm hiç yazılmaz; depo 2200 satıra
iner ve tüketim 2040 yerine **1800**'e düşer, yani yarım yazımdan **240 satır daha az** hesaplanır.
Kaybın büyümesinin nedeni önceki dersin çizgesidir: `2026-07` hiç yoksa `2026-08`'in tüketimi de
hesaplanamaz, çünkü bir önceki dönemin endeksi yoktur.

Ama sunuma bakıldığında sıralama tersine döner. Daha çok satır kaybeden kurulumun sapması
**−3,25 ile +12,80** arasındadır; daha az satır kaybedenin sapması −121,82'ye kadar iner. Neden
şudur: sunum bölge başına **dönem ortalamasıdır**. Bölüm hiç yazılmadığında o dönemin hücresi
ortalamaya hiç girmez ve kalan on bir dönem doğru ortalamayı verir. Yarım yazıldığında hücre
ortalamaya girer, ama içinde iki yüz abonenin yerine yüz yirmisi vardır — düşük bir sayı doğru bir
sayı gibi ortalamaya katılır.

Üstelik atomik kurulumda **iki denetim de ateşler.** Ham satır 2200'dür ve yüzde beşlik bandı aşar;
`2026-07` dönemi hiçbir bölge için veride olmadığı için dönem duyarlı bütünlük denetimi de
bozulmayı görür. Buradan çıkan cümle bu dersin omurgasıdır: **atomiklik cevabı doğru yapmaz;
başarısızlığı görünür ve sınırlı kılar.** Sonuç hâlâ eksiktir, sapma hâlâ sıfır değildir; kazanılan
şey, eksikliğin **yakalanabilir** olması ve etkisinin bir dönemin ötesine geçmemesidir.

#### Yeniden Deneme ve Uyarı

Düşen görev yeniden denenir. Üçüncü blok görevi üç kez dener — ilk iki deneme yine 120. satırda
düşer, üçüncüsü tamamlanır — ve yazımın ekleyen ya da anahtarlı olmasının ne değiştirdiğini sayar.
`gorev` sütunu görev uyarısını taşır: son denemeden sonra görev hâlâ düşükse ateşler.

```python
def gorev_kosumu(kayitlar, donem, kesmeler, atomik=False, anahtarli=False):
    """Gorev sirayla denenir; kesmeler her denemenin kac satirda dustugunu verir.
    None = deneme basarili. Yazim ya ekleyendir ya anahtarli."""
    depo, yazilan, deneme, dustu = [k for k in kayitlar if k["donem"] != donem], [], 0, False
    for kesme in kesmeler:
        deneme += 1
        parca, dustu = bolum_yaz(kayitlar, donem, kesme, atomik)
        if anahtarli:
            tablo = {(k["abone"], k["donem"]): k for k in yazilan}
            for k in parca:
                tablo[(k["abone"], k["donem"])] = k
            yazilan = list(tablo.values())
        else:
            yazilan = yazilan + parca
        if not dustu:
            break
    return depo + yazilan, deneme, len(yazilan), dustu


print(f"{'kurulum':<24}{'deneme':>7}{'yazilan':>8}{'ham':>6}{'tuketim':>9}{'kuzey':>9}"
      f"{'gorev':>7}{'satir':>7}{'butunluk':>10}")
for ad, kesmeler, atomik, anahtarli in (
        ("deneme yok , atomik degil", (120,), False, False),
        ("deneme yok , atomik", (120,), True, False),
        ("uc deneme , ekleyen", (120, 120, None), False, False),
        ("uc deneme , anahtarli", (120, 120, None), False, True)):
    depo, deneme, yaz, dustu = gorev_kosumu(KAYNAK, "2026-07", kesmeler, atomik, anahtarli)
    sun, n = okuma_sonucu(depo)
    print(f"{ad:<24}{deneme:>7}{yaz:>8}{len(depo):>6}{n:>9}"
          f"{round(sun['kuzey'] - TABAN_SUN['kuzey'], 2):>9}{str(dustu):>7}"
          f"{str(d_satir_sayisi(depo)):>7}{str(d_bolum_tamligi_donem(depo)):>10}")


def tekillestirmesiz(kayitlar):
    """Tekillestirme asamasi olmayan hat."""
    v, _ = a1_ayikla(kayitlar)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    return a5_sun(oz)[0], len(v)


depo, _, _, _ = gorev_kosumu(KAYNAK, "2026-07", (120, 120, None))
print()
print("uc deneme , ekleyen , tekillestirmesiz hat:", tekillestirmesiz(depo))
```

```
kurulum                  deneme yazilan   ham  tuketim    kuzey  gorev  satir  butunluk
deneme yok , atomik degil      1     120  2320     2040  -121.82   True  False     False
deneme yok , atomik           1       0  2200     1800     6.12   True   True      True
uc deneme , ekleyen           3     440  2640     2200      0.0  False   True     False
uc deneme , anahtarli         3     200  2400     2200      0.0  False  False     False

uc deneme , ekleyen , tekillestirmesiz hat: ({'merkez': 1470.73, 'kuzey': 1320.73, 'dogu': 1002.64, 'bati': 928.55, 'guney': 1189.73}, 2440)
```

Üçüncü satır yeniden denemenin bedelini verir. Görev üç kez denendi, iki başarısız deneme
bölüme 120'şer satır bıraktı, başarılı deneme 200 satırı daha ekledi: bölümde 200 yerine **440
satır** var, yani **240 satır çoğaldı**. Sunum yine de doğrudur ve kuzeyin sapması sıfırdır, çünkü
tekilleştirme aşaması fazlalığı yutar. Bu davranışın kendisi Veri Mühendisliğine Giriş kursunda
ölçülmüştü; buradaki yeni bilgi, çoğalmanın kaynağının bir bozulma değil **orkestrasyonun kendi
düzeneği** olmasıdır. Yeniden deneme hattı onarırken depoyu bozar.

Son satır o düzeneğin sağlamını gösterir. Anahtarlı yazımda her deneme aynı anahtarların üzerine
yazar; üç denemeden sonra bölümde 200 satır vardır, çoğalan satır **sıfırdır** ve ham satır 2400'de
kalır. Yeniden denemeyi güvenli kılan şey denemenin kendisi değil, **yazımın yinelemeye dayanıklı
olmasıdır**.

Son çıktı satırı tekilleştirmenin ne kadar yük taşıdığını söyler. Aynı ekleyen depo,
tekilleştirme aşaması olmayan bir hattan geçirildiğinde tüketim 2200 yerine 2440 satır olur ve
sunum her bölgede yükselir: kuzey 1212,55 yerine **1320,73**, merkez 1321,45 yerine **1470,73**.
Yani üçüncü satırın sıfır sapması hattın doğruluğundan değil, tek bir aşamanın varlığından gelir.

`gorev` sütunu uyarının nerede sustuğunu gösterir. İlk iki satırda son deneme başarısızdır ve görev
uyarısı gider — ama o uyarı yalnızca "görev düştü" der; birinci satırdaki −121,82'lik sapmadan söz
etmez, çünkü uyarıyı üreten şey verinin kendisi değil görevin durumudur. Son iki satırda yeniden
deneme başarılı olduğu için **görev uyarısı hiç gitmez**. Üçüncü satırda geriye tek bir işaret
kalır: satır sayısı denetimi 2640 satırı görüp ateşler. Yinelenme gürültülüdür ve onarılabilir;
yarım yazım sessizdir. **En tehlikeli kurulum, uyarı veren değil, uyarı vermeyi bırakan
kurulumdur.**

#### Özet

- Yarım yazılan bölüm sunumu **beş bölgenin hepsinde** yanlışlar (−49,09 ile −121,82 arası) ama
  ham satır 2320'de kalır: satır sayısı denetimi de dönem duyarlı bütünlük denetimi de **yeşildir**.
- Atomik yazımda bölüm hiç yazılmaz, tüketim 2040 yerine **1800**'e iner, ama sapma **−3,25 ile
  +12,80** arasında kalır ve **iki denetim de ateşler**. Atomiklik cevabı doğru yapmaz;
  başarısızlığı **görünür ve sınırlı** kılar.
- Sıralamanın tersine dönmesinin nedeni sunumun dönem ortalaması olmasıdır: yazılmayan hücre
  ortalamaya girmez, yarım yazılan hücre düşük bir sayıyla girer.
- Üç denemeli yeniden deneme ekleyen yazımda bölüme 200 yerine **440 satır** bırakır, yani **240
  satır çoğaltır**; anahtarlı yazımda çoğalan satır **sıfırdır**. Yeniden denemeyi güvenli kılan
  şey deneme değil, **yazımın yinelemeye dayanıklı olmasıdır**.
- Çoğalan satırın sunumu bozmaması tekilleştirme aşamasının eseridir: aynı depo o aşama olmadan
  tüketimi 2440 satıra, kuzeyi 1320,73'e taşır.
- Yeniden deneme başarılı olduğunda **görev uyarısı susar**; geriye yalnız veri uyarıları kalır ve
  onlar yarım yazımı hiç görmez. Uyarının sustuğu yer, hattın en az ölçülmüş yeridir.

#### Sonraki Adım

Üç derste hat çalıştırıldı: sırası çizgeden okundu, tetikleyicisi seçildi, düşen görevi yeniden
denendi ve düşmenin sunuma ne yaptığı sayıldı. Bu üç dersin ortak boşluğu şudur — her ölçüm
**sonradan** yapıldı. Yarım yazımı gösteren tablo, koşumun bittiği ve sonucun okunduğu andan sonra
kuruldu; hattın kendisi o an hiçbir şey söylemiyordu. Bir hat çalışırken neyin izlendiği, hangi
göstergenin bir bozulmayı kaç dönem sonra gösterdiği ve hangi bozulmayı **hiçbir göstergenin**
görmediği hiç sorulmadı. Sonraki ders tazelik, hacim ve şema değişimi göstergelerini K01'in altı
bozulma sınıfı üzerinde koşturur ve her birinin yakalama gecikmesini dönem olarak sayar.

### Hat Gözlemlenebilirliği

Source: https://academia.sh/tr/kurslar/veri-hatlari/hat-gozlemlenebilirligi

Çalışan bir hattın üstüne konan üç göstergenin ne gördüğünün sayılması: tazelik, hacim ve şema göstergeleri toplam 14 gözlem noktasına bakar ve hiçbiri bir hücrenin değerini okumaz, bu yüzden altı bozulma sınıfından yalnız değeri değiştiren birim değişimi üçüne de görünmez kalır. Hacim üç sınıfı aynı dönemde gösterir, şema iki sınıfı aynı dönemde, tazelik alan adı değişimini bir dönem sonra; gösterge en çok bir dönem ateşlerken bozulma iki dönem sürer ve iki sınıfta gösterge ateşlemesine karşın sunum hiç değişmez. Bant 0,05'ten 0,30'a genişledikçe hacmin gördüğü sınıf 3'ten 0'a iner ve bandın gizlediği en büyük kayıp 10 satırdan 60 satıra çıkar; tazelik toleransı 0 olduğunda gecikme 1 dönemden 0'a iner ama sağlam hatta bir yanlış alarm doğar.

Bir önceki ders düşen görevi ele aldı: yeniden deneme yinelemeye dayanıklı olmayan hatta satır
çoğaltıyor, yarım yazılan bölüm sunumu her bölgede yanlışlıyor, atomik yazım bölümü hiç yazmayarak
sapmayı −2,89 ile +12,80 arasına indiriyordu. Üç derste hat çalıştırıldı ve düştüğünde ne olacağı
yazıldı.

Düşen görev kendini haber verir. Bu dersin konusu **düşmeyen** koşumdur: bütün görevler yeşil biter,
çizge tamamlanır ve hat yanlış veri taşır. Orada tek kanıt hattın üstüne konan **göstergedir**. Ders
üç tanesini kurar — tazelik, hacim ve şema değişimi — ve tek soru sorar: Veri Mühendisliğine Giriş
kursunun altı bozulma sınıfını bunlar **kaç dönem sonra** gösterir, hangisini hiç göstermez.

- **OR41.** Kaynak, beş aşamalı hat, altı bozulma sınıfı ve üç gösterge **kurgudur** ve ders içinde
  üretilir. Hiçbir orkestratör, gözlemlenebilirlik ürünü ya da veri deposu çağrılmaz. Tohum 20260218.
- **OR42. Gözlemlenebilirlik (observability)** yordamı — ölçüt toplama, iz, günlükleme, uyarı
  tasarımı — Backend Geliştirme müfredatındaki Gözlemlenebilirlik ve Güvenilirlik, DevOps ve Platform
  Mühendisliği müfredatındaki Gözlemlenebilirlik ve İşletim ve Yapay Zekâ Mühendisliği müfredatındaki
  Yapay Zekâ Ajanları kurslarında ölçüldü; **tekrarlanmaz**. Buradaki tek ölçü veri hattına özgü üç
  göstergenin gecikmesidir.
- **OR43.** Altı bozulma sınıfı Veri Mühendisliğine Giriş kursundan **girdi** alınır. Her biri yalnız
  `2026-07` dönemini bozar; kalan on bir dönem sağlamdır.
- **OR44. Tazelik (freshness)**, sunumdaki en yeni dönemin çalışılan dönemden en çok bir dönem geride
  olması; **hacim**, dönem satır sayısının önceki üç dönemin ortancasından bant kadar sapmaması;
  **şema değişimi (schema evolution)**, alan adı ve tür çiftlerinin kayıtlı imzayla aynı kalmasıdır.
  Tolerans, bant ve imza birer **seçimdir**; son bölüm onları sayar.
- **OR45. Kayma (drift)** girdinin dağılımının zamanla değişmesidir ve Makine Öğrenmesi ve Veri
  Bilimi müfredatındaki Model Değerlendirme, Yorumlanabilirlik ve Etik kursunda ölçüldü. Üç gösterge
  kaymayı görmez; nedeni ilk bölümde sayılır.
- **OR46.** Taban çizgisi orkestrasyonsuz koşumdur: gösterge yok, görülen sınıf **0**, gecikme
  **tanımsız**. Gecikme **dönem** olarak yazılır.

#### Göstergelerin Baktığı Yüzey

Aşağıdaki blok ortak tanımı kurar ve tek bir şey basar: üç göstergenin **koşumdan bağımsız** ölçüsü,
yani her birinin baktığı nitelik sayısı. Bu sayı hat hiç çalıştırılmadan bilinir.

```python
# KURGUDUR. Olcum aginin sayac okumalari , bes asamali hat , alti bozulma sinifi
# ve uc gosterge ders icinde uretilir; hicbir orkestrator , gozlemlenebilirlik
# urunu ya da veri deposu cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000
            y["birim"] = "m3"                       # ETIKET DEGISMEDI
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad not in gor:
            gor.add(ad)
            tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


# ---- UC GOSTERGE. Her biri kaydin bir izdusumune bakar.
KAYITLI = {("abone", "str"), ("donem", "str"), ("bolge", "str"), ("tip", "str"),
           ("endeks", "int"), ("birim", "str")}
NITELIK = {"tazelik": 1, "hacim": 1, "sema": 2 * len(KAYITLI)}
print(f"kaynak {200 * len(DONEM)} satir x {len(KAYITLI)} alan ="
      f" {200 * len(DONEM) * len(KAYITLI)} hucre")
print("gosterge  baktigi sey                      nitelik")
print(f"tazelik   sunumdaki en yeni donem         {NITELIK['tazelik']:>7}")
print(f"hacim     donem basina satir sayisi       {NITELIK['hacim']:>7}")
print(f"sema      alan adi ve turu ciftleri       {NITELIK['sema']:>7}")
print(f"toplam gozlem noktasi {sum(NITELIK.values())} ,"
      f" degeri okunan hucre 0")
```

```
kaynak 2400 satir x 6 alan = 14400 hucre
gosterge  baktigi sey                      nitelik
tazelik   sunumdaki en yeni donem               1
hacim     donem basina satir sayisi             1
sema      alan adi ve turu ciftleri            12
toplam gozlem noktasi 14 , degeri okunan hucre 0
```

Kaynak 14400 hücre taşıyor, üç gösterge 14 noktaya bakıyor. Önemli olan son satırdır: **değeri
okunan hücre sıfır**. Tazelik bir dönemin var olup olmadığını, hacim satırların kaç tane olduğunu,
şema alanların adını ve türünü sorar. Hiçbiri bir sayacın **kaç metreküp** yazdığına bakmaz; kayma
görülmemesinin nedeni de budur.

Bu satır koşumdan önce bir kestirim verir: altı sınıftan yalnız **değeri** değiştiren biri varsa, o
sınıf üç göstergenin hiçbirine görünmeyecektir. Listede tanıma uyan bir sınıf vardır. `birim_degisti`
endeksi bine katlar, birim etiketini `m3` bırakır, alan adını ve türünü korur, satır sayısını
değiştirmez. Kestirim odur; koşum onu sınar.

#### Kaç Dönem Sonra

Aşağıdaki blok hattı on iki dönem boyunca dönem dönem koşturur ve her dönemden sonra üç göstergeyi
değerlendirir. Sınıf başına üç gecikme, ilk gösteren gösterge, bozulmanın **kaç dönem sürdüğü** ve
sunumun değişip değişmediği basılır.

```python
# Bu blok ilk bloktaki kaynak_uret , boz , a1..a5 ve KAYITLI tanimlarini surdurur.
KAYNAK = kaynak_uret()
GOSTERGE = ("tazelik", "hacim", "sema")
BOZUK = "2026-07"


def imza(kayitlar):
    return set((a, type(v).__name__) for k in kayitlar for a, v in k.items())


def ortanca(x):
    y = sorted(x)
    return y[len(y) // 2]


def ozetle(kayitlar):
    v, _ = a1_ayikla(kayitlar)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    return oz


def izle(kayitlar, band=0.05, tol=1):
    """Her donem hat kosturulur ve uc gosterge degerlendirilir. Tazelik tol donem
    tolerans tasir; hacim son uc donemin ortancasindan sapmaya bakar."""
    ates, gecmis = {g: [] for g in GOSTERGE}, []
    for i, d in enumerate(DONEM):
        bu = [k for k in kayitlar if k.get("donem") == d]
        oz = ozetle([k for k in kayitlar if DONEM.index(k["donem"]) <= i])
        if i - max([DONEM.index(x) for _, x in oz], default=-1) > tol:
            ates["tazelik"].append(d)
        if len(gecmis) >= 3:
            t = ortanca(gecmis[-3:])
            if abs(len(bu) - t) / t > band:
                ates["hacim"].append(d)
        gecmis.append(len(bu))
        if imza(bu) != KAYITLI:
            ates["sema"].append(d)
    return ates


def gecikme(donemler):
    """Kac donem sonra gosterdi. Hic gostermediyse tire."""
    sonra = [x for x in donemler if x >= BOZUK]
    return DONEM.index(sonra[0]) - DONEM.index(BOZUK) if sonra else "-"


TABAN = ozetle(KAYNAK)
TABAN_SUN, _ = a5_sun(TABAN)
print(f"saglam hat: ateslenen gosterge yok , sunum {TABAN_SUN}")
print("\nbozulma         tazelik  hacim  sema   ilk gosterge  etkilenen donem  sunum")
for s in BOZULMA:
    v, _ = boz(KAYNAK, s)
    a, oz = izle(v), ozetle(v)
    sun, _ = a5_sun(oz)
    g = {x: gecikme(a[x]) for x in GOSTERGE}
    goren = [(v2, x) for x, v2 in g.items() if v2 != "-"]
    etki = [d for (b, d) in set(TABAN) | set(oz) if TABAN.get((b, d)) != oz.get((b, d))]
    print(f"{s:<14} {g['tazelik']:>8} {g['hacim']:>6} {g['sema']:>5}"
          f"   {(min(goren)[1] if goren else 'yok'):<13} {len(set(etki)):>15}"
          f"  {'degisti' if sun != TABAN_SUN else 'ayni'}")

print("\nbant  gizledigi en buyuk kayip  hacmin gordugu sinif")
for band in (0.05, 0.10, 0.20, 0.25, 0.30):
    goren = [s for s in BOZULMA if izle(boz(KAYNAK, s)[0], band=band)["hacim"]]
    print(f"{band:<5} {int(band * 200):<24} {len(goren)}  {goren}")

print("\ntolerans  saglam hatta yanlis alarm  sema_degisti gecikmesi")
for tol in (0, 1, 2):
    yanlis = izle(KAYNAK, tol=tol)["tazelik"]
    bozuk = izle(boz(KAYNAK, "sema_degisti")[0], tol=tol)["tazelik"]
    print(f"{tol:<9} {len(yanlis):<26} {gecikme(bozuk)}")
```

```
saglam hat: ateslenen gosterge yok , sunum {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}

bozulma         tazelik  hacim  sema   ilk gosterge  etkilenen donem  sunum
gec_gelen             -      0     -   hacim                       2  degisti
yinelenen             -      0     -   hacim                       0  ayni
sema_degisti          1      -     0   sema                        2  degisti
birim_degisti         -      -     -   yok                         2  ayni
eksik_bolum           -      0     -   hacim                       2  degisti
kayan_tip             -      -     0   sema                        0  ayni

bant  gizledigi en buyuk kayip  hacmin gordugu sinif
0.05  10                       3  ['gec_gelen', 'yinelenen', 'eksik_bolum']
0.1   20                       3  ['gec_gelen', 'yinelenen', 'eksik_bolum']
0.2   40                       2  ['yinelenen', 'eksik_bolum']
0.25  50                       1  ['yinelenen']
0.3   60                       0  []

tolerans  saglam hatta yanlis alarm  sema_degisti gecikmesi
0         1                          0
1         0                          1
2         0                          -
```

Sağlam hatta hiçbir gösterge ateşlemiyor; yanlış alarm sıfır ve sunum ortak tanımın sunumudur.

Hacim sütunu üç sınıfı **aynı dönemde** gösteriyor: `gec_gelen` 40 satır eksiltiyor, `eksik_bolum`
46, `yinelenen` 60 ekliyor, üçü de 0,05 bandını aşıyor. Hacim ne olduğunu söylemiyor, yalnız sayının
kaydığını söylüyor; 46 satırın hangi bölgeden düştüğünü göstermez, çünkü bölge kırılımı taşımaz.
Onu K01'in dönem duyarlı bütünlük denetimi söyler.

`sema_degisti` iki gösterge tarafından **iki ayrı dönemde** gösteriliyor. Şema aynı dönemde ateşler,
imzada `endeks` yerine `endeks_degeri` vardır. Tazelik bir dönem sonra ateşler: alan adı değişen 200
satır ayıklamada düşer, o dönemin sunum hücresi hiç oluşmaz, tazelik bir dönem tolerans taşıdığı
için `2026-07`'de susar ve `2026-08`'de konuşur. Tek olay, iki gösterge, bir dönem fark.

`kayan_tip` yalnız şemaya görünür ve sunum **değişmez**: metne dönen endeks tüketim hesabında
tamsayıya geri çevrilebiliyor. Gösterge ateşler, hasar yoktur. `yinelenen` de öyledir: hacim ateşler,
tekilleştirme aşaması 60 kopyayı yutar, etkilenen dönem **0**'dır.

`birim_degisti` satırında üç tire ve **yok** yazıyor. Kestirim tuttu. Sunum da değişmiyor; K01
mekanizmasını ölçtü — ara katmanda iki dönem yaklaşık 1,8 milyar oynar ve birbirini götürür, on iki
dönemin ortalaması aynı kalır. Etkilenen dönem sütunu yine de **2** yazar: iki özet hücresi çöptür.
Bunu gören tek şey K01'in dağılım denetimidir ve o bir hat göstergesi değil, bir veri kalitesi
denetimidir. Farkı tek sözcükle söylenir: **değere bakar**.

En sert okuma sütunlar arasındadır. Gösterge en çok **bir dönem** ateşler, bozulma **iki dönem**
sürer. Nedeni çizgenin dönemler arası kenarıdır: tüketim bir önceki dönemin okumasına bağlı olduğu
için `2026-07`'de kaybolan 40 abonenin `2026-08` satırı da hesaplanamaz. Uyarı sustuktan bir dönem
sonra hat hâlâ bozuktur. Ters yön daha da kötüdür: `yinelenen` ile `kayan_tip`'te gösterge ateşler ve
hasar sıfırdır, `birim_degisti`'de gösterge susar ve iki dönem çöptür. **Ateşleme ile hasar arasında
bağ yoktur.**

#### Bant ve Tolerans Birer Seçimdir

Son iki tablo göstergenin kendi bedelini sayar. Hacmin gördüğü sınıf 0,05 ve 0,10 bandında üç,
0,20'de iki, 0,25'te bir, 0,30'da sıfırdır. İkinci sütun bandın gerçek anlamıdır: bir dönem 200 satır
taşıdığı için bant, **gizlediği en büyük kayıptır** — 0,05 on satıra kadar, 0,30 altmış satıra kadar
sessiz kalır. Kümenin çözünürlüğü bir satır, yani 0,005'tir; bandın altındaki her şey görünmezdir ve
bu bir kusur değil, tanımın kendisidir.

Bu kurgu kaynakta dar bant bedavadır, çünkü her dönem tam 200 satır taşır ve sağlam hatta yanlış
alarm sıfırdır. Gerçek bir kaynakta dönem satır sayısı oynar ve bant tam da **yanlış alarmı (false
positive)** azaltmak için genişletilir. Genişledikçe üç sınıf boy sırasıyla kaybolur: önce 40
satırlık `gec_gelen`, sonra 46 satırlık `eksik_bolum`, en son 60 satırlık `yinelenen`. Bant, yanlış
alarm ile görmezliğin takas edildiği yerdir ve para birimi satırdır.

Tolerans tablosu aynı takası tazelikte, daha çıplak biçimde gösterir. Tolerans 0'da `sema_degisti`
gecikmesi 1 dönemden **0'a** iner; bedeli sağlam hatta **bir yanlış alarmdır**, çünkü ilk dönem
öncesi olmadığı için tüketim üretmez ve toleranssız tazelik bunu boş sunum sayar. Tolerans 2'de
yanlış alarm sıfır kalır ama tazelik **hiçbir şey görmez**, sütun tiredir. Üç ayar, üç ayrı gösterge.

Şema göstergesinin böyle bir satırı yoktur. Eşiği olmadığı için ayarlanamaz: imza ya tutar ya tutmaz.
Karşılığında yalnız imzaya yazılmış olanı görür, `birim_degisti` imzayı bozmadığı için ona kördür.
**Ayarlanabilir gösterge neyi kaçıracağını seçer; ayarlanamaz gösterge neyi göreceğini tanımıyla
baştan sabitler.**

#### Özet

- Üç gösterge toplam **14 niteliğe** bakar ve hiçbiri bir hücrenin **değerini** okumaz; bu koşumdan
  bağımsız ölçü, yalnız değeri değiştiren `birim_degisti` sınıfının görünmez kalacağını önceden söyler
  ve koşum bunu doğrular.
- Hacim `gec_gelen`, `eksik_bolum` ve `yinelenen`'i **aynı dönemde** gösterir; şema `sema_degisti` ve
  `kayan_tip`'i aynı dönemde; tazelik `sema_degisti`'yi **bir dönem sonra**, çünkü bir dönem tolerans
  taşır.
- Gösterge en çok bir dönem ateşler, bozulma iki dönem sürer: tüketim bir önceki dönemin okumasına
  bağlıdır. `yinelenen` ve `kayan_tip`'te gösterge ateşlerken hasar **0** dönemdir, `birim_degisti`'de
  gösterge susarken hasar **2** dönemdir.
- Bant 0,05'ten 0,30'a genişledikçe hacmin gördüğü sınıf 3'ten 0'a iner ve gizlenen en büyük kayıp 10
  satırdan 60 satıra çıkar; bant, yanlış alarm ile görmezliğin satır cinsinden takasıdır.
- Tazelik toleransı 0'a indirildiğinde gecikme 1 dönemden 0'a iner ama sağlam hatta **bir yanlış
  alarm** doğar; tolerans 2'de yanlış alarm yoktur ve gösterge hiçbir şey görmez.

#### Sonraki Adım

Bu dersteki her sayı tek bir sessiz varsayıma yaslandı: hattı çalıştıran kod hep aynı koddur, hep
aynı ayarla ve hep aynı veri kümesi üzerinde koşar. Gösterge bandı 0,05'ti, tolerans 1'di, yüklenen
dönem sayısı on ikiydi — hiçbiri hattın kodunda yazılı değil, koşumun **çevresinde** duruyordu.
Sonraki ders aynı hat kodunu iki ortamda koşturur ve ortam ayarı değiştiğinde sunumun ne kadar
saptığını ölçer; ardından, koşumun üstüne bir sürüm damgası yazılmadığında elinizdeki raporun hangi
kodla üretildiğinin neden bilinemez olduğunu sayar.

### Ortam ve Sürüm Yönetimi

Source: https://academia.sh/tr/kurslar/veri-hatlari/ortam-ve-surum-yonetimi

Aynı hat kodunun üç ortamda ürettiği farklı sonucun ve sürüm damgası yazılmadığında kaybolan kimliğin sayılması: geliştirme, hazırlık ve üretim ortamları aynı 2400 satırdan sırasıyla 400, 1000 ve 2200 tüketim satırı üretir ve raporları birbirinden ayrılır; yüklenen bölüm sayısı sunumu 20,41 ile 62,55 arasında, kural sürümü 20,77 ile 35,87 arasında kaydırır, iki etki aynı büyüklük düzeninde olduğu için sunuma bakarak ayrılamaz ve bir bölgede birbirlerini götürüp farkı −5,72'ye indirir. On bir koşumluk arşivde kural sürümü yedinci dönemden sonra değiştiğinde gözlenen sıçramanın kodun payı 20,40 ile 34,71 arasındadır ve kod hiç değişmeseydi görülecek olağan dönem hareketinin 0,42 ile 38,30 aralığının içinde kalır; dört bölgede veri aşağı giderken rapor yukarı gider. Damgasız arşivde 11 raporun hiçbiri kimlik taşımaz ve kimlikleme 22 koşum ister. Ders kursun kapanışıdır.

Bir önceki ders üç göstergeyi hattın üstüne koydu ve her birinin ne gördüğünü saydı. Her sayı tek
bir sessiz varsayıma yaslanıyordu: kod hep aynıydı ve hep aynı ayarla koşuyordu. Oysa bant 0,05'ti,
tolerans 1'di, yüklenen dönem sayısı on ikiydi — üçü de hattın kodunda değil, koşumun
**çevresinde** duruyordu.

Bu ders o çevreyi ölçüm nesnesine çevirir. Tek bir hat kodu üç ortama dağıtılır ve iki soru sorulur:
aynı kod bu ortamlarda ne kadar farklı sonuç verir ve elinizdeki bir rapordan **hangi ortamda hangi
kodla** üretildiği okunabilir mi.

- **OR47.** Kaynak, beş aşamalı hat ve üç ortam ayarı **kurgudur** ve ders içinde üretilir. Hiçbir
  orkestratör, dağıtım aracı ya da sürüm yönetimi ürünü çağrılmaz. Tohum 20260218.
- **OR48.** Sürekli tümleştirme, dağıtım ve ortam terfisi yordamı DevOps ve Platform Mühendisliği
  müfredatındaki Sürekli Tümleştirme ve Teslimat ile Kod Olarak Altyapı kurslarında ölçüldü;
  **tekrarlanmaz**. Buradaki tek ölçü aynı kodun ortamlar arasında ürettiği **farklı sonuçtur**.
- **OR49.** Ortam üç ayar taşır ve üçü de kodun dışındadır: yüklenen **bölüm** sayısı, gösterge
  **bandı** ve raporlama kuralının **sürümü**. `s1` işyeri tüketimini 1,00, `s2` 1,05 katsayıyla
  raporlar; geri doldurma yordamı Dönüştürme konusunun son dersinde ölçüldü.
- **OR50. Sürüm damgası**, bir koşumun çıktısına yazılan dönem, kod sürümü ve ayar üçlüsüdür. Taban
  çizgisi orkestrasyonsuz koşumdur: tek ortam varsayılır, damga yazılmaz, kimlik taşıyan rapor
  **0**'dır.

#### Aynı Kod, Üç Ortam

Aşağıdaki blok tek bir `rapor` işlevi tanımlar ve onu üç ayarla koşturur. İşlev ortamdan yalnız
yüklenecek dönem kümesini ve kural katsayısını alır; bant hattı değil göstergeyi ayarladığı için
tabloya bir önceki dersin ölçüsüyle, gizlediği en büyük kayıpla girer.

```python
# KURGUDUR. Olcum aginin sayac okumalari , bes asamali hat ve uc ortam ayari ders
# icinde uretilir; hicbir orkestrator , dagitim araci ya da surum yonetimi urunu
# cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


# ---- TEK hat kodu. Ortamdan gelen: yuklenen donemler ve kural katsayisi.
# Saglam kaynakta ayikla ve tekille asamalari satir dusurmedigi icin tuketim
# dogrudan yuklenen donemlerden hesaplanir.
def rapor(kayitlar, donemler, kural):
    v, _ = a3_tuketim([k for k in kayitlar if k["donem"] in donemler])
    v = [dict(k, m3=k["m3"] * (kural if k["tip"] == "isyeri" else 1.0)) for k in v]
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return sun, len(v)


def fark(a, b):
    return {x: round(a[x] - b[x], 2) for x in sorted(a)}


KAYNAK = kaynak_uret()
AYAR = {"gelistirme": (3, 0.30, 1.00), "hazirlik": (6, 0.10, 1.00), "uretim": (12, 0.05, 1.05)}
SON = {ad: set(DONEM[len(DONEM) - b:]) for ad, (b, _, _) in AYAR.items()}
print("ortam        bolum  bant  kural  tuketim satiri  bandin gizledigi  2026-07 yuklu")
for ad, (b, e, ku) in AYAR.items():
    s, n = rapor(KAYNAK, SON[ad], ku)
    print(f"{ad:<12} {b:>5}  {e:<4}  {ku:<5}  {n:>14}  {int(e * 200):>16}"
          f"  {'evet' if '2026-07' in SON[ad] else 'hayir':>13}")
print()
for ad in AYAR:
    print(f"{ad:<12} {rapor(KAYNAK, SON[ad], AYAR[ad][2])[0]}")
```

```
ortam        bolum  bant  kural  tuketim satiri  bandin gizledigi  2026-07 yuklu
gelistirme       3  0.3   1.0               400                60          hayir
hazirlik         6  0.1   1.0              1000                20           evet
uretim          12  0.05  1.05             2200                10           evet

gelistirme   {'merkez': 1384.0, 'kuzey': 1265.0, 'dogu': 928.5, 'bati': 853.5, 'guney': 1102.5}
hazirlik     {'merkez': 1387.6, 'kuzey': 1229.0, 'dogu': 897.8, 'bati': 846.2, 'guney': 1077.8}
uretim       {'merkez': 1357.32, 'kuzey': 1235.57, 'dogu': 902.13, 'bati': 859.22, 'guney': 1095.81}
```

Üç satır aynı kodun üç çıktısıdır ve hiçbiri hatalı değildir; her biri kendi ayarına göre doğrudur.
`merkez` geliştirmede 1384,00, hazırlıkta 1387,60, üretimde 1357,32 raporlanır, oysa kaynak üç
koşumda da aynı 2400 satırdır.

Son sütun bir önceki derse bağlanır. Geliştirme ortamı yalnız son üç dönemi yükler ve `2026-07` o
pencereye girmez, oysa K01'in altı bozulma sınıfının hepsi tam o dönemi bozar. Hattı doğrulayan
koşum bozulmanın dönemini **hiç görmez**; üstelik bandı 0,30'dur ve altmış satıra kadarki kaybı
zaten gizler. Doğrulama yeşil biter, üretim bitmez.

#### Sapma Nereden Geldi

İkinci blok sapmayı iki kaynağa ayırır, sonra kural sürümünün `2026-08` koşumunda dağıtıldığı on bir
koşumluk arşive bakar.

```python
# Bu blok ilk bloktaki rapor , fark , KAYNAK , DONEM , AYAR ve SON tanimlarini surdurur.
TUM = set(DONEM)
TABAN, GEL, KUR = (rapor(KAYNAK, TUM, 1.00)[0], rapor(KAYNAK, SON["gelistirme"], 1.00)[0],
                   rapor(KAYNAK, TUM, 1.05)[0])
print("bolge   bolum etkisi  kural etkisi  gelistirme eksi uretim")
for b in sorted(TABAN):
    bl, ku = round(GEL[b] - TABAN[b], 2), round(KUR[b] - TABAN[b], 2)
    print(f"{b:<8} {bl:>12}  {ku:>12}  {round(bl - ku, 2):>22}")

# ---- ARSIV: her donem bir kosum , o doneme kadarki butun donemler yuklenir.
# Kural surumu s1 (1,00) idi ; 2026-08 kosumunda s2 (1,05) dagitildi.
KOD, DAGITIM = {"s1": 1.00, "s2": 1.05}, "2026-08"
arsiv = {d: ("s2" if d >= DAGITIM else "s1",
             rapor(KAYNAK, set(DONEM[:i + 1]), KOD["s2" if d >= DAGITIM else "s1"])[0])
         for i, d in enumerate(DONEM) if i > 0}

# Olagan donem hareketi: kod hic degismeseydi rapor ne kadar oynardi. Ilk iki
# kosum atlanir , orada ortalama iki donemden azini tasir.
olagan = [abs(v) for i in range(3, len(DONEM))
          for v in fark(rapor(KAYNAK, set(DONEM[:i + 1]), 1.00)[0],
                        rapor(KAYNAK, set(DONEM[:i]), 1.00)[0]).values()]
y07, y08 = set(DONEM[:7]), set(DONEM[:8])
gozlenen = fark(arsiv["2026-08"][1], arsiv["2026-07"][1])
veriden = fark(rapor(KAYNAK, y08, 1.00)[0], rapor(KAYNAK, y07, 1.00)[0])
koddan = fark(rapor(KAYNAK, y08, 1.05)[0], rapor(KAYNAK, y08, 1.00)[0])
print("\nbolge   gozlenen sicrama  veriden  koddan")
for b in sorted(gozlenen):
    print(f"{b:<8} {gozlenen[b]:>16}  {veriden[b]:>7}  {koddan[b]:>6}")
print(f"olagan donem hareketi (kod sabit) {round(min(olagan), 2)} ile {round(max(olagan), 2)} ;"
      f" kodun biraktigi iz {min(koddan.values())} ile {max(koddan.values())}")

damgasiz = {d: {"donem": d, "sunum": s} for d, (_, s) in arsiv.items()}
damgali = {d: dict(x, kod=arsiv[d][0], ayar=f"uretim/{len(DONEM)}/{KOD[arsiv[d][0]]:.2f}")
           for d, x in damgasiz.items()}
kimlik = [sum("kod" in x and "ayar" in x for x in a.values()) for a in (damgasiz, damgali)]
print(f"\narsiv {len(arsiv)} rapor ; kimlik tasiyan damgasizda {kimlik[0]} ,"
      f" damgalida {kimlik[1]} ; kimlikleme icin kosum damgasizda"
      f" {len(KOD) * len(arsiv)} , damgalida 0")
print(f"2026-08 damgasi: {damgali['2026-08']['kod']} / {damgali['2026-08']['ayar']}")
```

```
bolge   bolum etkisi  kural etkisi  gelistirme eksi uretim
bati            20.41         26.13                   -5.72
dogu            47.14         20.77                   26.37
guney           36.95         30.26                    6.69
kuzey           52.45         23.02                   29.43
merkez          62.55         35.87                   26.68

bolge   gozlenen sicrama  veriden  koddan
bati                13.74   -11.88   25.62
dogu                15.59    -4.81    20.4
guney                25.1    -3.76   28.86
kuzey                9.88    -11.4   21.28
merkez              52.24    17.53   34.71
olagan donem hareketi (kod sabit) 0.42 ile 38.3 ; kodun biraktigi iz 20.4 ile 34.71

arsiv 11 rapor ; kimlik tasiyan damgasizda 0 , damgalida 11 ; kimlikleme icin kosum damgasizda 22 , damgalida 0
2026-08 damgasi: s2 / uretim/12/1.05
```

İlk tablonun iki etkisi aynı büyüklük düzenindedir: bölüm sayısı sunumu 20,41 ile 62,55, kural
sürümü 20,77 ile 35,87 arasında kaydırır ve ikisi de artı yöndedir. Raporun otuz birim yükseldiğini
gören biri bunun kural sürümünden mi yüklenen dönem sayısından mı geldiğini **sunuma bakarak**
ayıramaz. `bati` satırı daha kötüsünü söyler: iki etki +20,41 ile +26,13, toplam fark **−5,72**.
Neredeyse hiçbir şeyin değişmediğini söyleyen bir sayı, birbirini götüren iki büyük etkidir. **Küçük
fark, küçük değişikliğin kanıtı değildir.**

İkinci tabloda okuyucu yalnız gözlenen sıçramayı görür, 9,88 ile 52,24 arasını. Ayrıştırma bunun ne
olduğunu söyler: beş bölgenin dördünde **veri aşağı, rapor yukarı** gitmiştir; `bati`'de tüketim
11,88 düşerken rapor 13,74 yükselmiştir. Son satır kapıyı kapatır: kodun bıraktığı iz 20,40 ile
34,71, olağan dönem hareketi ise 0,42 ile 38,30 arasındadır. Kodun izi olağanın **içindedir**;
sıçramaya bakarak bir dağıtımın olduğunu anlamanın yolu yoktur.

Geriye damga kalır. On bir raporun damgasız arşivde **hiçbiri** kimlik taşımaz, damgalıda on biri
de taşır. Damgasız arşivde bir raporun hangi kodla üretildiğini bulmak iki sürümü on bir dönem
boyunca yeniden koşturmayı, yani **22 koşumu** gerektirir — her eski sürüm hâlâ kurulabiliyorsa.
Kapsam da ölçülür: kural sürümü kodun içinde değil ortam ayarındaydı, damgaya yalnız kod parmak izi
yazılsaydı `s1` ile `s2` **aynı damgayı** taşırdı. **Damganın sınırı kodun sınırı değil, koşumu
belirleyen her şeyin sınırıdır.**

#### Özet

- Aynı hat kodu üç ortamda üç ayrı rapor üretir ve hiçbiri hatalı değildir: aynı 2400 satırdan 400,
  1000 ve 2200 tüketim satırı çıkar, `merkez` 1384,00, 1387,60 ve 1357,32 raporlanır.
- Geliştirme ortamı `2026-07`'yi hiç yüklemez ve bandı altmış satıra kadarki kaybı gizler; hattı
  doğrulayan koşum bozulmanın bulunduğu dönemi görmeden yeşil biter.
- Ortam ayarının etkisi (20,41 … 62,55) ile kural sürümünün etkisi (20,77 … 35,87) aynı büyüklük
  düzenindedir; `bati`'de ikisi birbirini götürüp farkı **−5,72**'ye indirir.
- Kodun bıraktığı iz (20,40 … 34,71) olağan dönem hareketinin (0,42 … 38,30) içinde kalır ve dört
  bölgede veri aşağı giderken rapor yukarı gider.
- Damgasız arşivde kimlik taşıyan rapor **0**, damgalıda **11**'dir; kimlikleme 22 koşum ister ve
  damga kodu değil, kural sürümünü de içeren bütün ayarı kapsar.

#### Kurs Kapanışı

Satırlar ortak birim taşımaz; her satırın **hangi ölçütle** okunduğu ders adının yanındadır.
Parantezdeki fark **orkestrasyonlu kurulum eksi orkestrasyonsuz tabandır**; işaretin yönü ölçüt
adından okunur.

| Ders | Orkestrasyonsuz taban | Orkestrasyonlu kurulum | Bedel |
|---|---|---|---|
| `veri-alma/01` — taşınan satır ve kaçan değişiklik | tam yükleme **15.560**, kaçan **0** | artımlı yükleme **2.360** (**−13.200**), kaçan **148** (**+148**) | kaçanı yalnız dağılım denetimi, aşama 4'te bildirir |
| `veri-alma/02` — alınan satır, sayfa 25 | yeniden denemesiz **2075** | yeniden denemeli **2025** (**−50**) | 112 sayfa gecikir, `2026-12` hiç alınmaz |
| `veri-alma/03` — etkin tüketici | tek bölüm **1/4** | dört bölüm **4/4** (**+3**) | abone anahtarında yük 708 ile 492 arasında eğrilir |
| `veri-alma/04` — düşen satır ve kirli hücre | tolerans 0: **40** ve **10/55** | tolerans 1: **0** ve **0** (**−40**, **−10**) | taşınan durum 400'den **600** girdiye çıkar |
| `veri-alma/05` — tüketim satırı ve sapma | en çok bir kez **2162**, −9,18 … −28,36 | en az bir kez **2200** (**+38**), sapma **boş** | 19 yinelenme oluşur, kayıp iki eşiğin de altındadır |
| `veri-alma/06` — koşumlar arası fark | geri yazmayan hat **0,00** | ters ETL **47,00 / 10,90 / 14,36** | 55 hücrenin **55'i** kirlenir, dört denetim yeşildir |
| `donusturme/01` — yeniden hesaplanabilen kural | ETL **0**, hedefe 55 satır | ELT **3** (**+3**), hedefe 2400 satır (**+2345**) | hedefte okunan hücre 110'dan **33110**'a çıkar |
| `donusturme/02` — ihlal edilen bağımlılık | elle sıra **1**, eksik girdili **7** model | çözümleyici **0** ve **0** (**−1**, **−7**) | ihlalli koşum hata vermez, sunum 17,46 … 28,37 sapar |
| `donusturme/03` — durdurulan bozulma sınıfı | yalnız sunum **2/6**, 55 hücre | üç katman **6/6** (**+4**), 2400 + 2200 + 55 | yakalanan altının **üçü** sunulan değeri değiştirmez |
| `donusturme/04` — yakalanan bozulma sınıfı | beklentisiz **0/6** | altı beklenti **4/6** (**+4**) | `yinelenen` ile `kayan_tip` dönüşümde yutulur |
| `donusturme/05` — yeniden hesaplanan dönem | tam uygulama **12** dönem, 2200 satır | kısmi uygulama **2** dönem, 400 satır (**−10**) | rapor **−16,98 … −29,57** sapar, altı beklenti yeşildir |
| `orkestrasyon/01` — düşen görev başına yeniden koşan görev | elle **48** | çizgeyle **5** (**−43**) | kırk yedi kenar elle yazılır |
| `orkestrasyon/02` — eksik dönem ve tüketim satırı | sabit takvim **6** eksik, **900** | veri hazırlığı tetikleyicisi **0** ve **2200** (**+1300**) | yedi dönemlik toplam gecikme |
| `orkestrasyon/03` — sapma ve ateşleyen denetim | yarım yazım **−49,09 … −121,82**, iki denetim yeşil | atomik yazım **−3,25 … +12,80**, ikisi de ateşler | tüketim 2040 yerine **1800**'e iner |
| `orkestrasyon/04` — görülen bozulma sınıfı | göstergesiz **0/6**, gecikme tanımsız | üç gösterge **5/6** (**+5**), gecikme 0–1 dönem | `birim_degisti` görünmez ve iki dönem çöp bırakır |
| `orkestrasyon/05` — kimlik taşıyan rapor | damgasız arşiv **0/11** | damgalı arşiv **11/11** (**+11**) | kimlikleme damgasızda **22 koşum** ister |

Tablo tek bir kuralın on altı kez yazılmasıdır. **Bir hattın sayısı bir kez doğru koşması değil,
yeniden koşturulduğunda aynı sonucu vermesidir; yeniden koşturulamayan hat ölçülmemiş sayılır.**
Ölçüt adları kuralın kendisidir: `orkestrasyon/01`'de yeniden koşan görev 48'den 5'e iner,
`donusturme/01`'de yeniden hesaplanabilen kural 0'dan 3'e çıkar, `veri-alma/06`'da hat kendi
çıktısını okuduğu için koşumlar arası fark 0,00 olmaktan çıkıp 47,00'ye ulaşır.

**İkinci iddia aynı tablodan okunur: orkestrasyon da bozar.** `veri-alma/02`'de yeniden deneme
kotayı yer, alınan satırı 2075'ten 2025'e düşürür ve bir dönemi yok eder. `veri-alma/06`'da ters ETL
koşumlar arası farkı 47,00'ye çıkarıp 55 hücrenin 55'ini kirletir, `orkestrasyon/03`'te üç denemeli
yeniden deneme 240 satır çoğaltır, `donusturme/05`'te kısmi geri doldurma raporu −16,98 ile −29,57
arasında saptırır, `orkestrasyon/02`'de emniyet penceresi koşumu 24'e çıkarır. `orkestrasyon/04`'te
bant genişledikçe hacmin gördüğü sınıf sıfıra iner, bu derste ortam ayarı aynı kodu 62,55'e kadar
saptırır.

**Üçüncü iddia daha sessizdir: sunulan sayı değişmez ya da makul görünür.** `veri-alma/04`'te dokuz
kurulumun hiçbirinde sunulan beş değer değişmez, on hücre kirlidir. `veri-alma/05`'te on dokuz
yinelenme sunum farkını boş bırakır, kayıp ise hiçbir denetime takılmaz. `orkestrasyon/03`'te yarım
yazılan bölümde ham satır 2320'de kalır ve iki denetim yeşildir, `donusturme/04`'te dönem etiketini
kaydıran kural 2200 satırı ve beş değeri aynı bırakır, `donusturme/03`'te durdurulan altı sınıfın
üçü sunulan değeri değiştirmez, `orkestrasyon/04`'te `birim_degisti` üç göstergeye birden görünmez
ve bu derste kodun bıraktığı iz olağan hareketin içine düşer. Dokuz derste hat yanlış bir sayı sunar
ve hiçbir şey bunu söylemez.

On altı derste hat **tek bir makinede** koşuyormuş gibi ele alındı: görevler sıraya girdi, düştü,
yeniden denendi ve kırk sekiz düğüm tek listeye dizildi. Veri tek bir makineye sığmadığında **işin
nasıl bölüneceği**, bölümler dengesiz olduğunda hangi bölümün bitişi geciktireceği ve **ağ üzerinden
veri taşımanın** bedeli hiç sorulmadı. Sonraki kurs görevin sırasını değil, bir görevin **kaç parçaya
bölündüğünü** ve o parçaların birbirine ne kadar veri gönderdiğini ölçer.

