İçeriğe geç
academia.sh

Ders 13 / 16

Bağımlılık 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.

İçindekiler

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

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

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.

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.

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

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat