---
title: 'İş Akışı Zamanlayıcıları'
source: 'https://academia.sh/tr/kurslar/veri-hatlari/is-akisi-zamanlayicilari'
course: 'Veri Hatları ve Orkestrasyon'
language: tr
updated: '2026-08-17T18:08:50+00:00'
license: 'CC BY-SA 4.0'
---

# İş 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.

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