Ders 12 / 16
İş Akışı Zamanlayıcıları
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.
İçindekiler
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.
# 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
ayiklasına ve bir önceki dönemin ayiklası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.
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.
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.
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 sekiz 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-07koşturulduğunda 200 satır girdiden 0 tüketim satırı çıkar,2026-06eklenince 200 çıkar. - Bulaşma kapsamı düşen görevin çizgedeki yerine göre 0 ile 5 arasında değişir;
al@2026-01için 5,ayikla@2026-07için 4,tuketim@2026-07için 1,ozet@2026-07iç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-07düş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ığı.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.