Ders 10 / 19
Uyarı Tasarımı
Uyarı kuralının kaynağının sonucu belirlemesi: on iki neden tabanlı kural ile dört belirti tabanlı kuralın aynı yedi günün üzerinden geçirilmesi, ürettikleri uyarı sayısının, aynı olayda kaç kuralın birden çaldığının ve hiç uyarı üretmeyen gerçek olayların sayılması.
İçindekiler
Önceki dersin kalan bütçe tablosunda karar 12. günde verilmeliydi. O gün kimse tabloya bakmıyorsa sayının bir değeri yok; birinin çağrılması gerekir. Çağrının hangi kuraldan doğacağı ise açık bir soru. Kural bildirim kuyruğunun derinliğine mi bakar, ödünç servisinin bellek kullanımına mı, yoksa doğrudan üyenin gördüğü hata oranına mı?
Bu ders eşik seçmez. Eşik ve pencere taraması Başarım Karşıt Kalıpları ve İzleme kursunda yapıldı; buradaki soru başka: kuralın kaynağı. Aynı yedi günün üzerinden iki ayrı kural kümesi geçirilir ve üç sayı karşılaştırılır — üretilen uyarı, aynı olayda birden çalan kural, hiç uyarı üretmeyen olay.
Kuralın Kaynağı
Neden tabanlı kural bir bileşenin iç ölçütünü okur: bellek kullanımı, disk doluluğu, önbellek isabet oranı, bağlantı havuzu doluluğu, çöp toplama süresi. Bu ölçütler süreçlerin içinden gelir ve sayıları servis sayısıyla birlikte artar.
Belirti tabanlı kural üyenin gördüğü şeyi okur: hata oranı, gecikme, trafik sapması, bildirim teslim oranı. Bu ölçütler sistemin dışından bakınca da aynıdır ve sayıları servis sayısından bağımsızdır.
Ayrım keyfi değil: bir uyarı bir kişiyi çağırır, ve çağrılan kişinin yapacak bir işi olmalıdır. Ödünç servisinin belleğinin yüzde 91 olması tek başına bir iş tanımlamaz; ödünç isteklerinin yüzde 4’ünün başarısız dönmesi tanımlar. Bu, gösterge tablosu ile uyarının farklı sorular yanıtladığı ayrımının kural yazımına düşen karşılığıdır.
On Altı Kural ve Altı Olay
Aşağıdaki üreteç yedi günü dakikalık örneklerle yazar. On iki bileşen ölçütü ve dört kullanıcı ölçütü aynı dakikalarda üretilir; her ölçütün tabanı, gürültüsü ve eşiği kendi satırındadır. Altı olay bu ölçütlerin bir kısmını hedef değere doğru taşır.
// uyari/olcut.mjs — 7 gunluk dakikalik olcut ornegi. On iki bilesen olcutu (neden) ile dort // kullanici olcutu (belirti) ayni dakikalarda uretilir. Kural bicimi: [ad, taban, gurultu, // yon, esik]; yon "ust" ise esigin ustu, "alt" ise alti kotudur. export const DK = 7 * 1440, TOHUM = 20260731; export const NEDEN = [ ["katalog.onbellek_isabet", 92, 2, "alt", 70], ["katalog.islemci", 38, 6, "ust", 85], ["odunc.bellek", 58, 3, "ust", 90], ["odunc.cop_toplama_ms", 25, 6, "ust", 120], ["odunc.yeniden_baslatma", 0, 0, "ust", 0], ["uyelik.havuz_doluluk", 44, 6, "ust", 90], ["uyelik.bagimlilik_ms", 40, 8, "ust", 300], ["bildirim.kuyruk", 12, 8, "ust", 400], ["bildirim.etkin_isci", 8, 1, "alt", 3], ["ucret.disk", 70, 1, "ust", 95], ["ucret.dosya_tanitici", 900, 60, "ust", 1800], ["genel.yeniden_deneme", 3, 2, "ust", 30], ]; export const BELIRTI = [ ["hata_orani", 0.2, 0.15, "ust", 1], ["p95_gecikme_ms", 180, 25, "ust", 400], ["trafik_sapmasi", 0, 6, "ust", 45], ["teslim_orani", 99.5, 0.3, "alt", 97], ]; // SD1 (varsayim): alti olay [gun, saat, sure dk, ad, [[olcut, olay icindeki deger], ...]] export const OLAY = [ [1, 10, 90, "onbellek cokusu", [["katalog.onbellek_isabet", 35], ["katalog.islemci", 91], ["p95_gecikme_ms", 520]]], [2, 8, 240, "bellek sizintisi", [["odunc.bellek", 96], ["odunc.cop_toplama_ms", 190], ["odunc.yeniden_baslatma", 2]]], [3, 0, 300, "disk dolmasi", [["ucret.disk", 98], ["ucret.dosya_tanitici", 2100]]], [4, 14, 120, "bagimlilik yavaslamasi", [["uyelik.bagimlilik_ms", 680], ["uyelik.havuz_doluluk", 96], ["genel.yeniden_deneme", 48], ["p95_gecikme_ms", 690], ["hata_orani", 1.8]]], [5, 9, 180, "sessiz teslim kaybi", [["teslim_orani", 3]]], [6, 11, 60, "yayin sonrasi hata artisi", [["hata_orani", 4.6]]], ]; export const pencere = (i) => [OLAY[i][0] * 1440 + OLAY[i][1] * 60, OLAY[i][2]]; // SD2 (varsayim): bilesen olcutu tek surecten okundugu icin 1500 dakikada bir 3-7 dakikalik // sivri yapar; kullanici olcutu binlerce istekten ortalandigi icin 6000 dakikada bir. export function seri() { let s = TOHUM; const rast = (n) => Math.floor(((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32) * n); const cikti = {}; for (const [ad, taban, gur, yon, esik] of [...NEDEN, ...BELIRTI]) { const bilesen = NEDEN.some((k) => k[0] === ad); const v = new Float64Array(DK); let sivri = 0; for (let t = 0; t < DK; t += 1) { let d = taban + (gur ? (rast(2001) - 1000) / 1000 * gur : 0); if (sivri > 0) { sivri -= 1; d = yon === "ust" ? esik * 1.15 + 1 : esik * 0.85; } else if (rast(bilesen ? 1500 : 6000) === 0) sivri = 3 + rast(5); for (let i = 0; i < OLAY.length; i += 1) { const [bas, sr] = pencere(i), hedef = OLAY[i][4].find((h) => h[0] === ad); if (!hedef || t < bas || t >= bas + sr) continue; const ilerleme = Math.min(1, (t - bas + 1) / Math.max(1, sr * 0.2)); d = taban + (hedef[1] - taban) * ilerleme; } v[t] = d; } cikti[ad] = v; } return cikti; } if (import.meta.filename === process.argv[1]) { const S = seri(); console.log(`tohum ${TOHUM}, ${DK} dakika, ${NEDEN.length} bilesen olcutu, ` + `${BELIRTI.length} kullanici olcutu, ${OLAY.length} olay`); const [b, sr] = pencere(0); console.log(`ornek — onbellek cokusu penceresi (${b}. dakika, ${sr} dk): ` + `onbellek isabet ${S["katalog.onbellek_isabet"][b + 30].toFixed(1)}, ` + `islemci ${S["katalog.islemci"][b + 30].toFixed(1)}, ` + `p95 ${S["p95_gecikme_ms"][b + 30].toFixed(0)} ms, ` + `hata orani ${S["hata_orani"][b + 30].toFixed(2)}`); }
tohum 20260731, 10080 dakika, 12 bilesen olcutu, 4 kullanici olcutu, 6 olay ornek — onbellek cokusu penceresi (2040. dakika, 90 dk): onbellek isabet 35.0, islemci 91.0, p95 520 ms, hata orani 0.07
İki Kümeyi Aynı Haftadan Geçirmek
Bir kural, koşulu üst üste üç dakika sağlandığında bir uyarı üretir ve koşul düşene kadar tekrarlamaz. İki küme aynı ölçütlerin üzerinde bağımsız çalıştırılır.
// uyari/kural.mjs — ayni yedi gunun uzerinden iki kural kumesi gecirilir: on iki neden tabanli // kural ve dort belirti tabanli kural. Bir kural, kosulu ust uste 3 dakika saglandiginda bir // uyari uretir ve kosul dusene kadar yeniden uretmez. import { seri, NEDEN, BELIRTI, OLAY, DK, pencere } from "./olcut.mjs"; const S = seri(); const asar = (k, v) => (k[3] === "ust" ? v > k[4] : v < k[4]); function uyarilar(kume) { const m = new Map(); for (const k of kume) { const v = S[k[0]], dk = []; let ard = 0, etkin = false; for (let t = 0; t < DK; t += 1) { if (asar(k, v[t])) { ard += 1; if (ard >= 3 && !etkin) { etkin = true; dk.push(t - 2); } } else { ard = 0; etkin = false; } } m.set(k[0], dk); } return m; } const KUME = [["neden tabanli", NEDEN, uyarilar(NEDEN)], ["belirti tabanli", BELIRTI, uyarilar(BELIRTI)]]; console.log(`${"olay".padEnd(26)}${"sure".padStart(7)}` + `${KUME.map(([ad]) => `${ad} kural`.padStart(24) + "ilk uyari".padStart(11)).join("")}`); const yakalanan = KUME.map(() => new Set()); OLAY.forEach(([, , sr, ad], i) => { const [bas] = pencere(i); const s = KUME.map(([, , m], j) => { const c = [...m.values()].map((dk) => dk.filter((t) => t >= bas && t < bas + sr)); const kural = c.filter((x) => x.length > 0).length; const ilk = Math.min(...c.flat().map((t) => t - bas), Infinity); if (kural > 0) yakalanan[j].add(i); return [kural, ilk]; }); console.log(`${ad.padEnd(26)}${(sr + " dk").padStart(7)}` + `${s.map(([kural, ilk]) => String(kural).padStart(24) + (ilk === Infinity ? "-" : `${ilk}. dk`).padStart(11)).join("")}`); }); const ici = (t) => OLAY.some((o, i) => t >= pencere(i)[0] && t < pencere(i)[0] + o[2]); console.log(`\n${"kume".padEnd(16)}${"kural".padStart(7)}${"uyari".padStart(7)}` + `${"olay disi".padStart(11)}${"yakalanan".padStart(11)}${"olay basina".padStart(13)} kacan olay`); KUME.forEach(([ad, kume, m], j) => { const hepsi = [...m.values()].flat(); const disi = hepsi.filter((t) => !ici(t)).length; const ici2 = hepsi.length - disi; console.log(`${ad.padEnd(16)}${String(kume.length).padStart(7)}${String(hepsi.length).padStart(7)}` + `${String(disi).padStart(11)}${`${yakalanan[j].size}/${OLAY.length}`.padStart(11)}` + `${(ici2 / yakalanan[j].size).toFixed(1).padStart(13)} ` + `${OLAY.filter((_, i) => !yakalanan[j].has(i)).map((o) => o[3]).join(", ") || "-"}`); }); const birlikte = new Set([...yakalanan[0], ...yakalanan[1]]); console.log(`\nikisi birlikte: ${NEDEN.length + BELIRTI.length} kural, ` + `${birlikte.size}/${OLAY.length} olay`); const cok = OLAY.map((o, i) => { const [bas] = pencere(i); return [o[3], [...KUME[0][2].values()].filter((dk) => dk.some((t) => t >= bas && t < bas + o[2])).length]; }).sort((a, b) => b[1] - a[1])[0]; console.log(`en gurultulu olay: ${cok[0]} — tek olayda ${cok[1]} neden tabanli uyari birden`); const enCok = [...KUME[0][2]].sort((a, b) => b[1].length - a[1].length)[0]; console.log(`en cok uyaran tek kural: ${enCok[0]} — 7 gunde ${enCok[1].length} uyari`);
olay sure neden tabanli kural ilk uyari belirti tabanli kural ilk uyari onbellek cokusu 90 dk 2 6. dk 1 11. dk bellek sizintisi 240 dk 5 0. dk 0 - disk dolmasi 300 dk 3 45. dk 0 - bagimlilik yavaslamasi 120 dk 3 9. dk 2 10. dk sessiz teslim kaybi 180 dk 0 - 1 0. dk yayin sonrasi hata artisi 60 dk 0 - 1 2. dk kume kural uyari olay disi yakalanan olay basina kacan olay neden tabanli 12 81 67 4/6 3.5 sessiz teslim kaybi, yayin sonrasi hata artisi belirti tabanli 4 9 4 4/6 1.3 bellek sizintisi, disk dolmasi ikisi birlikte: 16 kural, 6/6 olay en gurultulu olay: bellek sizintisi — tek olayda 5 neden tabanli uyari birden en cok uyaran tek kural: bildirim.kuyruk — 7 gunde 10 uyari
Üç Sayı
Üretilen uyarı. On iki neden tabanlı kural yedi günde 81 uyarı üretti, dört belirti tabanlı kural 9 uyarı. Kural sayısı üç kat, uyarı sayısı dokuz kat. Bunun 67’si hiçbir olaya denk gelmiyor: neden tabanlı uyarıların yüzde 83’ü boşa çıkıyor. Belirti tabanlı kümede aynı oran 9’da 4. Farkın kaynağı SD2’de yazılı: bileşen ölçütü tek bir süreçten okunur ve kısa sıvrılar yapar, kullanıcı ölçütü binlerce isteğin ortalamasıdır ve düzgündür. Tek bir kural — bildirim kuyruğu — yedi günde 10 uyarı üretti.
Aynı olayda birden çalan kural. Neden tabanlı kümede olay başına 3,5 uyarı düşüyor, belirti tabanlı kümede 1,3. Bellek sızıntısı sırasında beş kural birden çaldı: üçü olayın kendisiydi (bellek, çöp toplama, yeniden başlatma), ikisi o dört saat içinde denk gelen ilgisiz sıvrılardı. Çağrılan kişi beş uyarıyı açtığında hangisinin kök neden olduğunu ayırt edemez; uyarıların sayısı bilgi taşımaz, çünkü hepsi aynı olaydan doğmuştur.
Hiç uyarı üretmeyen olay. İki küme de altıda dört olay yakaladı, ama farklı ikisini kaçırdı. Neden tabanlı kurallar sessiz teslim kaybını ve yayın sonrası hata artışını göremedi: ikisinde de bileşen ölçütleri normaldi, bozuk olan işin kendisiydi. Belirti tabanlı kurallar bellek sızıntısını ve disk dolmasını göremedi: ikisinde de üye hiçbir şey fark etmemişti, çünkü kaynak henüz tükenmemişti.
Uyandıran Kural, Teşhis Eden Ölçüt
Zamanlama sütunu iki kümenin rollerini ayırıyor. Önbellek çöküşünde neden tabanlı kural 6. dakikada, belirti tabanlı kural 11. dakikada çaldı; bağımlılık yavaşlamasında fark bir dakikaya iniyor. Neden tabanlı ölçütler biraz daha erken, ama bu beş dakikanın bedeli 67 boş uyarıdır.
Buradan çıkan düzen şudur: on iki bileşen ölçütü silinmez, uyarı kuralı olmaktan çıkarılır. Uyandıran kurallar belirtiden yazılır — dört kural, üyenin gördüğü dört ölçüt. Bileşen ölçütleri gösterge tablosunda kalır ve çağrılan kişi uyarıyı aldıktan sonra oraya bakar. Uyarı “bir şey yapılmalı” der, gösterge tablosu “nereye bakılmalı” der. İki kümenin birleşimi altı olayın altısını kapsıyor, ama bu 16 uyarı kuralıyla değil, 4 uyarı kuralı ve 12 gösterge tablosu paneliyle olur.
Belirti tabanlı kümenin kaçırdığı iki olay bu düzenin gerçek boşluğudur ve eşik değiştirerek kapanmaz. Disk yüzde 98 doluyken üye hâlâ bir şey görmez; belirti ancak disk dolduğunda ortaya çıkar ve o an geri dönüşü pahalıdır. Tükenen kaynaklar için kural eşik üzerine değil tükenme süresi üzerine yazılır: “bu doluş hızıyla disk dört saat içinde dolar” cümlesi bir belirtidir, çünkü bir kişinin yapacağı işi tanımlar. Aynı cümle bellek sızıntısı için de yazılabilir; ikisi de neden tabanlı bir ölçütten okunur ama belirti tabanlı bir soruya yanıt verir.
Özet
- Uyarı kuralının kaynağı iki türdür: bileşenin iç ölçütü (neden) ya da üyenin gördüğü ölçüt (belirti); ayrımın ölçütü, çağrılan kişinin yapacak bir işi olup olmadığıdır.
- On iki neden tabanlı kural yedi günde 81 uyarı üretti ve 67’si hiçbir olaya denk gelmedi; dört belirti tabanlı kural 9 uyarı üretti ve 4’ü boşa çıktı.
- Neden tabanlı kümede olay başına 3,5 uyarı düşüyor: bellek sızıntısında beş kural birden çaldı, üçü olayın kendisi, ikisi ilgisiz sıvrıydı.
- İki küme de altıda dört olay yakaladı ama farklı ikisini kaçırdı: neden tabanlı kurallar işin kendisinin bozulduğu olayları, belirti tabanlı kurallar üyeye henüz ulaşmamış tükenmeleri.
- Neden tabanlı kurallar önbellek çöküşünde beş dakika erken çaldı; bu beş dakikanın bedeli 67 boş uyarıdır.
- Çözüm bileşen ölçütlerini silmek değil, uyarı kuralı olmaktan çıkarmaktır: dört uyarı kuralı ve on iki gösterge tablosu paneli altı olayın altısını kapsar.
Sonraki Adım
Bu derste kurulan her kural bir insanı çağırıyor ve insan dakikalar içinde yanıt veriyor. Bazı kararlar ise insana hiç sorulmaz. Ödünç sisteminin süreçlerinden birinin yaşayıp yaşamadığına ve trafik alıp almayacağına makine karar verir, üstelik saniyeler içinde ve bir günde binlerce kez. Bu karar iki ayrı soruya bakar — süreç ölmüş mü, ve süreç şu an iş alabilir mi — ve iki soru tek bir uca bağlandığında verilen yanıt yalnız yanlış olmaz, kendini besleyen bir döngü kurar. Sonraki ders iki ucu kodda ayırır ve ayırmamanın bedelini yeniden başlatma sayısıyla düşen istek sayısıyla ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.