Ders 09 / 26
Hız Sınırları ve Yeniden Deneme
Aynı 160 kurgu talebin iki yük düzeyinde üç politikayla koşturulması: düzeneksiz istemci her iki yükte de 0,8562 tamamlıyor ve hiç hız sınırına takılmıyor. Hafif yükte yeniden deneme tamamlamayı 1,0000'a çıkarıyor, bedeli 25 ek çağrı ve 1209 belirteç. Ağır yükte aynı düzenek işi bozuyor: sabit aralıklı deneme 0,8000'e düşüyor ve 151 hız sınırı reddi üretiyor, geri çekilmeli deneme 0,8750 ile düzeneksizin 0,0188 üstünde kalıyor ama iki koşum farkı da 0,0188. Aynı sınırı paylaşan etkileşimli akış 0,9500'den 0,8667'ye iniyor: yeniden deneme kendi işini kurtarırken başkasının işini bozuyor.
İçindekiler
Önceki sekiz derste arayüzün ne sunduğu ölçüldü: konuşma durumunun taşınması, yanıt akışı, araç çağrısı, yapılandırılmış çıktı kipi, çok kipli girdi, gömme arayüzü, uyumlu arayüz katmanı ve toplu çağrı. Hepsinin ortak bir sessiz varsayımı vardı: çağrı başarılı oldu. Sayılan her belirteç, ödenen ve karşılığı alınan bir belirteçti.
Bir üretim hattında bu varsayım tutmaz. Çağrı reddedilir, yarıda kesilir, hiç yanıt vermez. Bunların karşılığı bilinen bir yordamdır: yeniden dene. Yeniden deneme M16’nın dayanıklılık kursunda ve M19’un çalışma zamanı kursunda ölçüldü; yordamı burada tekrarlanmaz. Bu derste eklenen tek şey, çağrının pahalı ve belirsiz olmasından gelir: her deneme girişi bir kez daha öder, her deneme aynı hız sınırına bir çağrı daha yükler, ve yarıda kesilen bir çıktı hiçbir işe yaramadan belirteç harcamıştır. Sorunun kendisi budur — yeniden deneme, düzelttiği şeyin kaynağını besler.
- UK1. Kod bir benzeticidir; gerçek bir uç nokta çağrılmaz. Talepler, kalıplar ve ağ davranışı kurgudur, tohum 20260218.
- UK2. Zaman adım ile sayılır, ham süre yazılmaz. Bir adım istemcinin bir gönderim turudur.
- UK3. Hız sınırı kayan pencerede okunur ve reddedilen çağrılar da sayılır: son üç adımda yapılan toplam çağrı yirmi dördü aşarsa çağrı reddedilir. Reddi saymayan bir uç noktada aşağıda ölçülen fark küçülür; bu davranış bir varsayımdır ve sonucu belirler.
- UK4. Reddedilen çağrı belirteç harcamaz. Kabul edilen her çağrı girişini tam öder.
- UK5. İki içerik başarısızlığı vardır: zaman aşımı (çıktı başlamadan kesilir, giriş ödenir) ve yarım çıktı (giriş ile bir belirteç çıkış ödenir, yanıt okunamaz).
- UK6. İstemci bir adımda en çok on iki çağrı gönderir; bir istek en çok dört kez denenir.
- UK7. İki iş yükü aynı sınırı paylaşır: 160 kayıtlık toplu sınıflandırma ve iki adımda bir gelen etkileşimli durum sorgusu. Etkileşimli sorgunun gecikme bütçesi bir adımdır.
- UK8. Yük, patlama sırasında adım başına gelen istek sayısıdır: hafif üç, ağır altı.
Çağrının Üç Başarısızlığı ve Bedeli
Benzetici bu konuda bir şey daha taşır: ağ davranışı. Aynı istem, pencerenin doluluğuna göre reddedilebilir; kabul edilse bile yanıt yarıda kesilebilir. Üç sınıfın ayrı ayrı sayılması gerekir, çünkü bedelleri farklıdır.
# BENZETICI -- KURGUDUR. Gercek bir uc nokta cagrilmaz; ag davranisi da ders # icinde yazilir. Talepler ve kaliplar kurgudur, tohum 20260218. TOHUM, M32 = 20260218, 0xFFFFFFFF KOTA, KADIM = 24, 3 # kayan pencerede sayilan cagri / pencere adimi ESZAMAN, BUTCE, EN_COK = 12, 140, 4 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 def ayrik(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 def belirtecle(m): # KURGU belirtecleyici, parca en cok bes damga b = [] for s in m.lower().replace(",", " , ").split(): while len(s) > 5: b.append(s[:5]) s = s[5:] b.append(s) return b def izno(m): h = 2166136261 for c in m: h = ((h ^ ord(c)) * 16777619) & M32 return h KONU = ["fatura", "ariza", "sayac", "tarife", "kesinti"] KALIP = [("bu ayki fatura tutari gecen aya gore cok yuksek geldi", "fatura"), ("faturami odedim ama sistemde borc gorunuyor", "fatura"), ("fatura ayrintisini nereden gorebilirim", "fatura"), ("evde su akmiyor bir ariza var galiba", "ariza"), ("baglanti bozuk oldu ve hic calismiyor", "ariza"), ("ariza kaydi actim ama ekip gelmedi", "ariza"), ("sayac okumasi yanlis girilmis endeks tutmuyor", "sayac"), ("sayac degisimi icin randevu istiyorum", "sayac"), ("sayac uzerindeki endeks ile fatura ayni degil", "sayac"), ("hangi tarife basamagindayim ogrenmek istiyorum", "tarife"), ("tarife degisikligi icin ne yapmam gerekiyor", "tarife"), ("birim ucret abone tipine gore degisiyor mu", "tarife"), ("mahallede kesinti var ne zaman gelecek", "kesinti"), ("planli kesinti duyurusu nereden takip edilir", "kesinti"), ("aksamdan beri kesinti yasiyoruz", "kesinti")] EK = ["ayrica gecen ay tarife degisikligi de yapilmisti", "kayit numarasini ekliyorum ekip hala gelmedi", "odeme gorunmuyor ama dekont elimde duruyor", "duyurulari takip ediyorum yine de bilgi alamadim"] TALEP = [] for i in range(160): # 160 KURGU destek talebi r = uretec(TOHUM + i) k = KONU[ayrik(r(), [0.24, 0.22, 0.18, 0.20, 0.16])] m = [x for x in KALIP if x[1] == k][int(r() * 3)][0] TALEP.append({"no": 4001 + i, "konu": k, "metin": m + (" " + EK[int(r() * 4)] if r() < 0.34 else "")}) KAVRAM = KONU + ["odeme", "sikayet", "bilgi", "genel"] IZ = {"fatur": "fatura", "tutar": "fatura", "ayrin": "fatura", "borc": "odeme", "ariza": "ariza", "bozuk": "ariza", "calis": "ariza", "akmiy": "ariza", "sayac": "sayac", "endek": "sayac", "okuma": "sayac", "tarif": "tarife", "birim": "tarife", "abone": "tarife", "basam": "tarife", "kesin": "kesinti", "duyur": "kesinti", "mahal": "kesinti", "dekon": "odeme", "sikay": "sikayet"} YONERGE = ("Talebi su etiketlerden birine ata: fatura , ariza , sayac , tarife , " "kesinti . Yanit olarak tek etiket yaz . ") SUNUCU = {"gecmis": []} def istemi(t): return YONERGE + f"Talep {t['no']} : {t['metin']}" def etiketle(bel): # en olasi secim; adayi daraltan sey etiket kumesidir aday = [k for k in KAVRAM if k[:5] in bel] p = {k: 0.15 for k in (aday if len(aday) >= 3 else KAVRAM)} for t in bel: if IZ.get(t) in p: p[IZ[t]] += 1.0 return max(sorted(p), key=lambda a: p[a]) def cagir(istem, adim, deneme=0, tohum=TOHUM): """KURGU uc nokta. Hiz siniri kayan pencerede REDDEDILENLER DAHIL sayar.""" g = SUNUCU["gecmis"] while len(g) <= adim: g.append(0) g[adim] += 1 if sum(g[max(0, adim - KADIM + 1):adim + 1]) > KOTA: return {"durum": "hiz_siniri", "giris": 0, "cikis": 0, "yanit": ""} bel = belirtecle(istem) r = uretec((tohum ^ izno(istem)) + 7919 * deneme) if r() < 0.03 + 0.0006 * len(bel): # cikti baslamadan kesildi return {"durum": "zaman_asimi", "giris": len(bel), "cikis": 0, "yanit": ""} k = etiketle(bel) if r() < 0.09: # yarida kesilen cikti return {"durum": "yarim_cikti", "giris": len(bel), "cikis": 1, "yanit": k[:2]} return {"durum": "tamam", "giris": len(bel), "cikis": len(belirtecle(k)), "yanit": k} SUNUCU["gecmis"] = [] print("tek cagri :", cagir(istemi(TALEP[0]), 0)) SUNUCU["gecmis"] = [KOTA] # pencere doldurulmus durumda print("dolu pencere :", cagir(istemi(TALEP[1]), 0)) SUNUCU["gecmis"] = [] sinif = {} for t in TALEP: SUNUCU["gecmis"] = [] y = cagir(istemi(t), 0) a, b = sinif.get(y["durum"], (0, 0)) sinif[y["durum"]] = (a + 1, b + y["giris"] + y["cikis"]) print("\nsinif adet belirtec sonuc") for d in ("tamam", "zaman_asimi", "yarim_cikti"): a, b = sinif[d] print(f"{d:<12} {a:>4} {b:>8} {'kullanilir' if d == 'tamam' else 'atilir'}")
tek cagri : {'durum': 'tamam', 'giris': 44, 'cikis': 2, 'yanit': 'kesinti'}
dolu pencere : {'durum': 'hiz_siniri', 'giris': 0, 'cikis': 0, 'yanit': ''}
sinif adet belirtec sonuc
tamam 137 6523 kullanilir
zaman_asimi 9 436 atilir
yarim_cikti 14 655 atilir
Son tabloda pencere her çağrıdan önce boşaltıldı, yani hız sınırı devre dışı ve geriye yalnız içerik başarısızlıkları kaldı. Yirmi üç istek düşüyor ve bu isteklerin 1091 belirteci ödenmiş, karşılığı alınmamıştır. Toplamın 7614 belirteç olduğu düşünülürse boşa giden pay 0,1433. Bu, hiçbir yeniden deneme yapılmadan, tek denemeli bir istemcide ödenen bedeldir.
İki başarısızlık sınıfının bedeli eşit değildir. Zaman aşımı dokuz istekte 436 belirteç yakıyor; yarım çıktı on dört istekte 655. Reddedilen çağrı ise sıfır belirteç harcıyor — ödeme açısından en ucuz başarısızlık odur. Bu sıralama, yeniden deneme politikasının niçin körlemesine kurulamayacağını söylüyor: reddedilmiş bir çağrıyı tekrar göndermek yalnız bir çağrıya mal olur, yarıda kesilmiş bir çağrıyı tekrar göndermek girişin tamamını ikinci kez ödetir.
Yük Altında Üç Politika
Şimdi aynı 160 talep bir zaman ekseninde koşturulur. İlk yüz adımda adım başına bir istek gelir; otuzuncu adımda bir patlama başlar ve altmış istek ya üçerli ya altışarlı olarak yığılır. Aynı hız sınırını, saha uygulamasından iki adımda bir gelen etkileşimli durum sorgusu da kullanır. Üç politika karşılaştırılır: yeniden deneme yok, sabit aralıklı (her başarısızlıktan bir adım sonra) ve geri çekilmeli (bir, iki, dört adım sonra).
def kosum(politika, yuk, tohum=TOHUM): """politika: yok | sabit | geri . yuk: patlama sirasinda adim basina istek.""" SUNUCU["gecmis"] = [] varis = [[] for _ in range(BUTCE)] for i in range(len(TALEP)): varis[i if i < 100 else 30 + (i - 100) // yuk].append(i) kuyruk, etkin = [], [] s = dict.fromkeys(("cagri", "hiz", "icerik", "bel", "bos", "tamam", "dogru", "etamam", "etoplam"), 0) for adim in range(BUTCE): if adim % 2 == 0 and adim < 120: # etkilesimli akis etkin.append({"a": adim, "d": 0}) s["etoplam"] += 1 for i in varis[adim]: kuyruk.append({"i": i, "hazir": adim, "deneme": 0}) for x in list(etkin): if x["a"] + 1 < adim: # gecikme butcesi doldu etkin.remove(x) continue y = cagir(f"Saha uygulamasi sorgusu {x['a']} : acik kayit sayisi", adim, x["d"], tohum) s["bel"] += y["giris"] + y["cikis"] if y["durum"] == "tamam": s["etamam"] += 1 etkin.remove(x) else: s["bos"] += y["giris"] + y["cikis"] x["d"] += 1 for x in [x for x in kuyruk if x["hazir"] <= adim][:ESZAMAN]: kuyruk.remove(x) t = TALEP[x["i"]] y = cagir(istemi(t), adim, x["deneme"], tohum) s["cagri"] += 1 s["bel"] += y["giris"] + y["cikis"] if y["durum"] == "tamam": s["tamam"] += 1 s["dogru"] += y["yanit"] == t["konu"] continue s["bos"] += y["giris"] + y["cikis"] s["hiz" if y["durum"] == "hiz_siniri" else "icerik"] += 1 x["deneme"] += 1 if politika == "yok" or x["deneme"] >= EN_COK: continue x["hazir"] = adim + (1 if politika == "sabit" else 2 ** (x["deneme"] - 1)) kuyruk.append(x) n = len(TALEP) return (s["tamam"] / n, s["etamam"] / s["etoplam"], s["cagri"], s["hiz"], s["icerik"], s["bel"], s["bos"], s["dogru"] / n) print("yuk politika tamamlanan etkilesimli cagri hiz reddi icerik hatasi" " belirtec bosa giden isabet") for yuk, ad in ((3, "hafif"), (6, "agir ")): for p in ("yok", "sabit", "geri"): a = kosum(p, yuk) print(f"{ad} {p:<8} {a[0]:>10.4f} {a[1]:>11.4f} {a[2]:>5} {a[3]:>9}" f" {a[4]:>13} {a[5]:>8} {a[6]:>10} {a[7]:>6.4f}") print("\nikinci kosum (tohum 20260219), tamamlanan / etkilesimli:") for yuk, ad in ((3, "hafif"), (6, "agir ")): b = [kosum(p, yuk, TOHUM + 1) for p in ("yok", "sabit", "geri")] print(f" {ad} " + " ".join(f"{p}: {x[0]:.4f} / {x[1]:.4f}" for p, x in zip(("yok", "sabit", "geri"), b)))
yuk politika tamamlanan etkilesimli cagri hiz reddi icerik hatasi belirtec bosa giden isabet hafif yok 0.8562 0.9500 160 0 23 8425 1218 0.8438 hafif sabit 1.0000 0.9500 185 0 25 9634 1311 0.9875 hafif geri 1.0000 0.9500 185 0 25 9634 1311 0.9875 agir yok 0.8562 0.9500 160 0 23 8425 1218 0.8438 agir sabit 0.8000 0.8667 295 151 16 7658 873 0.7937 agir geri 0.8750 0.8833 289 129 20 8389 1038 0.8625 ikinci kosum (tohum 20260219), tamamlanan / etkilesimli: hafif yok: 0.8750 / 1.0000 sabit: 1.0000 / 1.0000 geri: 1.0000 / 1.0000 agir yok: 0.8750 / 1.0000 sabit: 0.6813 / 0.8667 geri: 0.7188 / 0.8833
Tablonun okunması gereken ilk satırı, düzeneksiz istemcinin iki yükte de aynı olmasıdır: 0,8562 tamamlanma, sıfır hız sınırı reddi. Patlamanın adım başına üçten altıya çıkması düzeneksiz istemci için hiçbir şeyi değiştirmiyor, çünkü gelen yük hâlâ sınırın altında kalıyor. Sınır, iş yükü tarafından değil, düzenek tarafından aşılacak.
Hafif yükte yeniden deneme tam olarak beklendiği gibi çalışıyor: tamamlanma 0,8562’den 1,0000’a çıkıyor ve iki politika birbirinin aynısı — çünkü hiç red olmadığında geri çekilmenin geciktirecek bir şeyi yoktur. Bedel açık: 25 ek çağrı, 1209 ek belirteç, ve boşa giden belirteç 1218’den 1311’e. Yani kurtarılan yirmi üç isteğin karşılığında ödenen belirtecin bir kısmı yine boşa gitti, çünkü kimi yeniden deneme de düştü.
Yeniden Denemenin Kimin İşini Bozduğu
Ağır yük satırları kursun ikinci iddiasının en açık örneğidir. Sabit aralıklı yeniden deneme tamamlanmayı 0,8562’den 0,8000’e düşürüyor. Düzenek eklendi, sayı geriledi. Nedeni sütunlarda duruyor: çağrı 160’tan 295’e çıkıyor ve bu çağrıların 151’i hız sınırından reddediliyor. Reddedilen çağrılar da pencerede sayıldığı için her red bir sonraki reddi kolaylaştırıyor; düzeltmek için gönderilen çağrı, düzeltilecek sorunu büyütüyor. İçerik hatası 23’ten 16’ya düşerken toplam tamamlanma da düşüyor: istekler artık içerik yüzünden değil, kendi denemelerinin yarattığı tıkanıklık yüzünden kaybediliyor.
Geri çekilmeli deneme aynı yükte 0,8750 veriyor, yani düzeneksize göre 0,0188 üstünde. Bu sayının yanına ikinci koşum konmalıdır: düzeneksiz istemci tohum 20260219’da 0,8750 veriyor ve iki koşum farkı da tam 0,0188. Kazanç, koşum farkını aşmıyor, dolayısıyla ölçülmemiş sayılır. Buna karşılık sabit aralıklının kaybı 0,0562, yani koşum farkının üç katı — kayıp ölçülmüştür. İkinci koşumun ağır yük satırları bunu doğruluyor: orada iki politika da düzeneksizin altına düşüyor (0,6813 ve 0,7188).
Son sütun, çoğu ölçümde hiç bakılmayan yerdedir. Etkileşimli akış aynı hız sınırını paylaşıyor ve kendi yeniden denemesi yok; gecikme bütçesi bir adım. Düzeneksiz istemcide bu akış 0,9500 tamamlıyor. Toplu işe yeniden deneme eklendiğinde 0,8667’ye düşüyor. Etkileşimli akış hiçbir şey değiştirmedi, isteği artmadı, istemi büyümedi; yalnız komşusu dayanıklı hâle geldi. Bu, dayanıklılık düzeneğinin en sinsi bedelidir: kendi ölçütünü kurtarırken paylaşılan bir kaynağı tüketir ve zararı başka bir ölçütte görünür. Bir yeniden deneme politikası yalnız kendi başarısızlık payıyla değerlendirilirse bu satır hiç görülmez.
Özet
- Bir çağrının üç başarısızlığı ayrı bedeller taşır: hız sınırı reddi belirteç harcamaz, zaman aşımı girişi öder, yarım çıktı giriş ile kısmi çıkışı öder; tek denemeli istemcide boşa giden belirteç payı 0,1433’tür.
- Düzeneksiz istemci hafif ve ağır yükte aynı sonucu veriyor (0,8562) ve hiç hız sınırına takılmıyor; sınırı iş yükü değil, eklenen düzenek aşıyor.
- Hafif yükte yeniden deneme tamamlanmayı 1,0000’a çıkarıyor ve sabit aralıklı ile geri çekilmeli arasında fark yok; bedel 25 ek çağrı ve 1209 belirteçtir.
- Ağır yükte sabit aralıklı deneme tamamlanmayı 0,8000’e düşürüyor ve 151 hız sınırı reddi üretiyor: düzeltmek için gönderilen çağrı, düzeltilecek sorunu büyütüyor.
- Geri çekilmeli denemenin ağır yükteki kazancı 0,0188, iki koşum farkı da 0,0188; kazanç ölçülmemiş sayılır, buna karşılık sabit aralıklının 0,0562’lik kaybı ölçülmüştür.
- Aynı sınırı paylaşan etkileşimli akış 0,9500’den 0,8667’ye iniyor; dayanıklılık düzeneği kendi ölçütünü kurtarırken zararı başka bir ölçütte bırakıyor.
Sonraki Adım
Bu derste her çağrı sıfırdan gönderildi. Oysa tabloya giren 160 istemin hepsi aynı yönergeyle başlıyor ve o yönerge her seferinde yeniden belirteçleniyor, yeniden ödeniyor. Yeniden deneme bu tekrarı bir kat daha büyütüyor: ağır yükte 295 çağrının hepsi aynı ortak metni taşıyor. Tekrarın kendisi bir bedeldir ve bu bedel, yanıtın veya ortak bağlamın saklanmasıyla düşürülebilir. Ama saklanan bir yanıtın bir yaşı vardır: dayandığı belge değişmişse yanıt hâlâ hızlıdır, hâlâ ucuzdur ve artık yanlıştır. Sonraki ders iki sayıyı yan yana koyar: önbellek isabeti yükseldikçe belirteç ne kadar düşüyor ve bayat yanıt payı ne kadar büyüyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.