Ders 07 / 19
Gösterge, Hedef ve Anlaşma
Telemetrinin karara çevrilmesi: gösterge, hedef ve anlaşmanın ayrı cümleler olduğunun gösterilmesi, ödünç sisteminin otuz günlük istek kaydından istek, dakika ve üye tabanlı üç gösterge tanımının hesaplanması ve aynı dönemin on iki farklı sayıyla anlatılabildiğinin sayılması.
İçindekiler
Önceki konu üç sinyali tek bir olayda birleştirdi: bir isteğin hangi süreçten geçtiği, nerede beklediği, nerede kaybolduğu artık tek bir bağıntı kimliğiyle okunabiliyor. Eksik olan tek şey, o kaydın iyi mi kötü mü olduğunu söyleyen cümledir. Telemetri gözlem üretir, karar üretmez. Ödünç servisinin bir isteği 380 milisaniyede yanıtladığını bilmek, o isteğin başarılı sayılıp sayılmayacağını söylemez.
Bu ders karar katmanının ilk taşını koyar. Birbirine karıştırılan üç kavram ayrı ayrı tanımlanır, sonra aynı telemetriden üç ayrı gösterge tanımı çıkarılır ve aynı otuz günün kaç farklı sayıyla anlatılabildiği sayılır.
Üç Kavram, Üç Ayrı Cümle
Gösterge (service level indicator) ölçülen bir orandır. Telemetriden hesaplanır ve tek başına hiçbir taahhüt taşımaz: “ödünç sisteminin isteklerinin yüzde kaçı iyi” bir göstergedir.
Hedef (service level objective) o göstergenin üzerine konan iç taahhüttür ve üç parçadan oluşur: bir tanım, bir pencere, bir sayı. “Otuz günlük pencerede istek tabanlı gösterge yüzde 99,5’in altına düşmez” bir hedeftir. Karşılığında kimseye para ödenmez, ama hedef aşıldığında ekibin ne yapacağı önceden yazılıdır.
Anlaşma (service level agreement) dışarıya verilen taahhüttür ve bir yaptırım taşır: ihlal edilirse ücret iadesi, üyelik uzatması ya da başka bir bedel doğar. Anlaşma her zaman hedeften gevşektir, çünkü hedef aşıldıktan sonra düzeltmek için zaman kalması gerekir.
Üçünü ayıran soru şudur: gösterge “ne ölçülüyor”, hedef “ne zaman müdahale edilir”, anlaşma “ihlal edilirse ne ödenir” sorusunu yanıtlar. Üçü de aynı telemetriden beslenir. Bu yüzden gösterge tanımı değiştiğinde diğer ikisi de anlamını değiştirir, ve bu dersin ölçtüğü şey tam olarak budur.
Otuz Günlük Kayıt
Ölçü, bölünmüş ödünç sisteminin istek kaydından çıkar. Aşağıdaki üreteç beş servisin otuz günlük isteklerini tohumlu tam sayı aritmetiğiyle yazar; dört bozulma penceresi ve bir planlı bakım penceresi bilinen takvimlerle yerleştirilir.
// slo/telemetri.mjs — bolunmus odunc sisteminin 30 gunluk istek kaydi. Tohumlu tam sayi // aritmetigiyle uretilir. Kayit bicimi: [dakika, servis, durum, sure_ms, uye, tur]. export const DK = 30 * 1440, UYE = 4000, TOHUM = 20260731; export const SERVIS = ["katalog", "uyelik", "odunc", "bildirim", "ucret"]; const TABAN = [40, 25, 60, 30, 45]; // SD1 (varsayim): dakikadaki istek sayisi gun ritmine baglidir (08-22 arasi 40, disinda 10). // SD2 (varsayim): dort bozulma [gun, saat, sure dk, servis (-1 hepsi), kip, siddet (binde)]. export const OLAY = [ [4, 8, 90, 2, "hata", 60], [11, 14, 240, 0, "yavas", 1000], [19, 2, 30, -1, "hata", 600], [26, 20, 120, 3, "hata", 250], ]; // SD3 (varsayim): planli bakim penceresi 15. gunun 03:00-04:00 dilimi. export const BAKIM = [15 * 1440 + 180, 60]; export function kayitlar() { let s = TOHUM; const rast = (n) => Math.floor(((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32) * n); const k = []; for (let t = 0; t < DK; t += 1) { const saat = Math.floor(t / 60) % 24; const bakim = t >= BAKIM[0] && t < BAKIM[0] + BAKIM[1]; for (let i = 0, n = saat >= 8 && saat < 22 ? 40 : 10; i < n; i += 1) { const sv = rast(5); let durum = 200, sure = TABAN[sv] + rast(60); if (rast(1000) < 8) durum = 404; if (rast(1000) < 1) durum = 500; if (rast(1000) === 0) sure += 400 + rast(600); for (const [g, sa, sr, sd, kip, si] of OLAY) { const bas = g * 1440 + sa * 60; if (t < bas || t >= bas + sr || (sd >= 0 && sd !== sv)) continue; if (kip === "hata" && rast(1000) < si) durum = 500; if (kip === "yavas") sure += 300 + rast(si); } if (bakim) durum = 503; k.push([t, SERVIS[sv], durum, sure, rast(UYE), rast(50) === 0 ? "tarama" : "insan"]); } } return k; } if (import.meta.filename === process.argv[1]) { const K = kayitlar(); const say = (f) => K.filter(f).length; console.log(`tohum ${TOHUM}, ${DK} dakika, ${K.length} istek, ${UYE} uye`); console.log(`durum 5xx ${say((r) => r[2] >= 500)}, durum 4xx ${say((r) => r[2] >= 400 && r[2] < 500)}, ` + `sure > 400 ms ${say((r) => r[3] > 400)}, tarama ${say((r) => r[5] === "tarama")}`); }
tohum 20260731, 43200 dakika, 1188000 istek, 4000 uye durum 5xx 2214, durum 4xx 9457, sure > 400 ms 3087, tarama 23868
Aynı Telemetriden Üç Gösterge
Kötü isteğin tanımı üç gösterge için de ortaktır: durum kodu 400 ve üstü olan ya da 400 milisaniyeyi aşan istek kötüdür. Değişen şey neyin bölünen, neyin bölen olduğudur.
- İstek tabanlı: iyi istek sayısı bölü toplam istek sayısı.
- Dakika tabanlı: bir dakika, kötü oranı yüzde 5’in altındaysa iyidir; gösterge iyi dakika sayısı bölü dolu dakika sayısı.
- Üye tabanlı: bir üye-günü, o üyenin o günkü isteklerinin en çok yüzde 1’i kötüyse iyidir; gösterge günlük iyi üye oranının otuz günlük ortalamasıdır.
Buna dört süzgeç eklenir. Süzgeç, göstergenin neyi saymadığını söyler: tarama istekleri, istemci hatası olan istekler ve planlı bakım penceresi sırayla dışarıda bırakılır.
// slo/gosterge.mjs — ayni telemetriden uc gosterge tanimi ve dort suzgec. Kotu istek: // durum 400 ve ustu ya da sure 400 ms ustu. Uc tanimin sonucu %99,5 hedefiyle karsilastirilir. import { kayitlar, DK, UYE, BAKIM } from "./telemetri.mjs"; const K = kayitlar(), HEDEF = 0.995, y = (x) => `%${(x * 100).toFixed(3)}`; const kotu = (r) => r[2] >= 400 || r[3] > 400; const istekTabanli = (ks) => 1 - ks.filter(kotu).length / ks.length; function dakikaTabanli(ks) { // bir dakika: kotu orani %5 altindaysa iyi const top = new Int32Array(DK), kot = new Int32Array(DK); for (const r of ks) { top[r[0]] += 1; if (kotu(r)) kot[r[0]] += 1; } let iyi = 0, dolu = 0; for (let t = 0; t < DK; t += 1) if (top[t] > 0) { dolu += 1; if (kot[t] <= top[t] * 0.05) iyi += 1; } return iyi / dolu; } function uyeTabanli(ks) { // bir uye-gunu: kotu orani %1 altindaysa iyi let toplam = 0, istek = 0, gunSay = 0; for (let g = 0; g < 30; g += 1) { const top = new Int32Array(UYE), kot = new Int32Array(UYE); for (const r of ks) { if (Math.floor(r[0] / 1440) !== g) continue; top[r[4]] += 1; if (kotu(r)) kot[r[4]] += 1; } let etkin = 0, iyi = 0, n = 0; for (let u = 0; u < UYE; u += 1) if (top[u] > 0) { etkin += 1; n += top[u]; if (kot[u] <= top[u] * 0.01) iyi += 1; } toplam += iyi / etkin; istek += n / etkin; gunSay += 1; } return [toplam / gunSay, istek / gunSay]; } const SUZGEC = [ ["ham", () => true], ["tarama haric", (r) => r[5] !== "tarama"], ["+ istemci hatasi haric", (r) => r[5] !== "tarama" && (r[2] < 400 || r[2] >= 500)], ["+ bakim haric", (r) => r[5] !== "tarama" && (r[2] < 400 || r[2] >= 500) && (r[0] < BAKIM[0] || r[0] >= BAKIM[0] + BAKIM[1])], ]; console.log(`${"suzgec".padEnd(24)}${"istek".padStart(9)}${"elenen".padStart(8)}` + `${"istek tabanli".padStart(15)}${"dakika tabanli".padStart(16)}${"uye tabanli".padStart(13)}` + `${"hedefi gecen".padStart(14)}`); let uyeIstek = 0; for (const [ad, f] of SUZGEC) { const ks = K.filter(f), [ut, n] = uyeTabanli(ks); const u = [istekTabanli(ks), dakikaTabanli(ks), ut]; uyeIstek = n; console.log(`${ad.padEnd(24)}${String(ks.length).padStart(9)}` + `${String(K.length - ks.length).padStart(8)}` + `${u.map((v, i) => y(v).padStart([15, 16, 13][i])).join("")}` + `${`${u.filter((v) => v >= HEDEF).length}/3`.padStart(14)}`); } console.log(`hedef ${y(HEDEF)}; ayni 30 gun, ayni ${K.length} kayit, 12 sayi`); console.log(`uye basina gunde ortalama ${uyeIstek.toFixed(1)} istek: ` + `%1'lik uye esigi ${Math.floor(uyeIstek * 0.01)} kotu istege izin verir`);
suzgec istek elenen istek tabanli dakika tabanli uye tabanli hedefi gecen ham 1188000 0 %98.760 %94.655 %88.763 0/3 tarama haric 1164132 23868 %98.761 %93.074 %88.980 0/3 + istemci hatasi haric 1154873 33127 %99.553 %98.123 %96.089 1/3 + bakim haric 1154280 33720 %99.604 %98.259 %96.540 1/3 hedef %99.500; ayni 30 gun, ayni 1188000 kayit, 12 sayi uye basina gunde ortalama 9.6 istek: %1'lik uye esigi 0 kotu istege izin verir
Tanım Neyi Sayıyor
Tek bir kayıt değişmeden aynı otuz gün on iki sayı üretti ve bunlar yüzde 88,763 ile yüzde 99,604 arasına yayıldı — 10,8 puanlık bir aralık. Yüzde 99,5 hedefi on iki sayının yalnız ikisini geçiyor. “Hedefi tutturduk” cümlesi, tanım yazılmadan doğrulanamaz.
Elenen kayıt sayısı ile göstergeye etkisi arasında ilişki yok. Tarama istekleri 23.868 kayıt eledi ve istek tabanlı göstergeyi yüzde 98,760’tan yüzde 98,761’e taşıdı; istemci hatası 9.259 kayıt eledi ve aynı göstergeyi 0,79 puan sıçrattı. Belirleyici olan elenen kaydın sayısı değil, kötü olup olmadığıdır. Bakım penceresinin 593 kaydı 0,05 puan getirdi; küçük bir sayı, ama hedefe uzaklık 0,05 puana indiğinde bu kayıtların hariç tutulup tutulmadığı sonucu belirler.
Tarama isteklerini elemek dakika tabanlı göstergeyi düşürdü (yüzde 94,655’ten yüzde 93,074’e). Elenen istekler kötü olmadığı için bu ters görünür; nedeni bölendedir. Kırk isteklik bir dakikada iki kötü istek tam yüzde 5 eder ve dakika iyi sayılır. Bir tarama isteği elenince bölen 39 olur, aynı iki istek yüzde 5,1 eder ve dakika kötüye döner. İyi istekleri elemek oran tabanlı bir göstergeyi bozar; süzgeç, kötü istekleri elediğinde göstergeyi yükseltir, iyi istekleri elediğinde düşürür.
Üye tabanlı gösterge üçünün en sertidir: istek tabanlı yüzde 99,553 derken o yüzde 96,089 diyor. Sertliğin kaynağı eşik değil bölendir. Üye başına günde ortalama 9,6 istek düşüyor ve yüzde 1’lik eşik bu bölende sıfır kötü isteğe izin veriyor; yani tanım pratikte “üye o gün hiç kötü istek görmesin” hâline geliyor. Üç tanım aynı olayın üç ayrı yüzünü ölçer: istek tabanlı olayın hacmini, dakika tabanlı süresini, üye tabanlı yayılımını.
Hedef ile Anlaşma Arasındaki Boşluk
Hedef, tanımıyla birlikte yazılmadıkça ölçülemez. “Yüzde 99,5” tek başına yukarıdaki on iki sayıdan hangisine bakıldığını söylemez, ve bakılan sayıya göre aynı ay hem tutturulmuş hem kaçırılmış olur. Yazılı hedefin üç parçası — tanım, pencere, sayı — üçü birden bulunmak zorundadır.
Anlaşma bir adım daha ister: karşı taraf da aynı sayıyı hesaplayabilmelidir. Üye tabanlı bir gösterge üzerine anlaşma yazılamaz, çünkü üye kendi üye-günlerini görmez. Bu yüzden anlaşma en kaba ve en doğrulanabilir tanımı kullanır: istek tabanlı, tarama ve istemci hatası hariç, bakım penceresi hariç. Ödünç sisteminde anlaşma yüzde 99,0’a, hedef yüzde 99,5’e konursa aradaki 0,5 puan müdahale alanıdır; hedef aşıldığında yaptırım henüz doğmamıştır ve düzeltmek için ay içinde yer kalır.
Bu ayrımın kodda görünen karşılığı SUZGEC dizisidir. Anlaşma metnindeki her hariç tutma cümlesi
o dizide bir satırdır: dört süzgeç seviyesi, üç hariç tutma kuralı. Metinde yazan ile dizide duran
ayrışırsa taahhüt edilen sayı ile ölçülen sayı farklı olur — bakım penceresinin 593 kaydı metinde
hariç tutulmamışsa, ölçüm doğru çalışırken anlaşma ihlal edilmiş görünür.
Özet
- Gösterge ölçülen orandır, hedef o oran üzerine konan iç taahhüttür, anlaşma yaptırımı olan dış taahhüttür; anlaşma her zaman hedeften gevşektir.
- Aynı otuz günlük 1.188.000 kayıttan üç tanım ve dört süzgeçle on iki sayı çıktı: yüzde 88,763 ile yüzde 99,604 arası, ve yüzde 99,5 hedefini bunların yalnız ikisi geçiyor.
- Hariç tutmanın etkisi elenen kayıt sayısıyla değil, elenen kayıtların kötü olup olmadığıyla ölçülür: 23.868 tarama kaydı göstergeyi 0,001 puan, 9.259 istemci hatası 0,79 puan değiştirdi.
- İyi istekleri elemek dakika tabanlı göstergeyi düşürür, çünkü böleni küçültür: yüzde 94,655’ten yüzde 93,074’e.
- Üye tabanlı gösterge, üye başına günde 9,6 istek düşerken yüzde 1’lik eşiği sıfır toleransa çevirir; üç tanım aynı olayın hacmini, süresini ve yayılımını ölçer.
- Anlaşmanın hariç tutma listesi koda dönüşür; süzgeç dizisi ile anlaşma metni ayrışırsa ölçülen sayı ile taahhüt edilen sayı da ayrışır.
Sonraki Adım
Buradaki üç tanım da tek bir “kötü istek” ölçütü üzerine kuruldu: durum kodu ya da süre. Oysa ödünç sisteminde bir isteğin kötüye gitmesinin tek yolu bu değil. Katalog servisi hiç hata döndürmeden yavaşlayabilir, bildirim servisi hatasız çalışırken kuyruğu doldurabilir, ücret servisine hiç istek gelmemesi tek başına bir arıza belirtisi olabilir. Bunların hepsi aynı telemetride yazılıdır, ama tek bir orana bakan gösterge bir kısmını göremez. Sonraki ders aynı kayıtlardan dört ayrı sinyal hesaplar ve her sinyalin tek başına hangi olay sınıfını kaçırdığını sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.