Ders 12 / 16
Enstrümantasyon Tasarımı
Bir ölçütün toplanmaya değip değmediğinin iki yanlı tartılması: üretilen kayıt, saklanan bayt ve eklenen kod yolu cinsinden bedelin hesaplanması, örnekleme ile toplulaştırmanın kazandırdığı hacmin ve kaybettirdiği bilginin aynı nüfus üzerinde ölçülmesi, yakın bedelli iki biçimin yüzdelik hatasının karşılaştırılması ve toplama biçiminin mümkün kıldığı tanıya göre seçilmesi.
İçindekiler
Önceki ders on beş ölçütü beş kategoriye yerleştirdi ve listenin eksiksizliğini kapsamayla sınadı. Sınama tek yanlıydı: her ölçüt yalnız mümkün kıldığı tanıyla değerlendirildi, hiçbirinin bedeli sayılmadı. Oysa her ölçüt üretilir, taşınır, saklanır ve bir kod yoluna eklenir; eklendiği kod yolu tepe uçtaki 513,89 isteğin her birinde çalışır.
Bu ders kararı iki yanlı hâle getirir. Bir ölçüt toplanmaya değer mi sorusunun yanıtı, bedeli ile mümkün kıldığı tanının karşılaştırılmasından çıkar. İkisi de sayıya çevrilir.
Ölçütün Üç Maliyet Kalemi
Enstrümantasyon (instrumentation), bir ölçütü üretmek için koda eklenen çağrılardır. Bedeli üç kalemdir. Üretilen kayıt: birim zamanda kaç ölçüt kaydı doğuyor. Saklanan bayt: o kayıtlar ne kadar süre ne kadar yer tutuyor. Eklenen kod yolu: her istekte kaç enstrümantasyon çağrısı çalışıyor.
Üçüncü kalem ilk ikisinden farklıdır ve doğrudan sayılamaz — bir sayaç artırmakla bir olay kaydı oluşturup kuyruğa koymak aynı iş değildir. Bu derste kod yolu, istek başına üretilen kayıt sayısıyla temsil edilir: bir istekte bir kayıt üreten biçim, saniyede bir kayıt üreten biçimden daha çok kod yolu çalıştırır. Aşağıdaki hesabın ilk sütunu bu yüzden hem kayıt hem kod yolu kalemidir.
Aylık hacim GB-ay ile yazılır: bir ay boyunca tutulan bir gigabayt. Para birimi kullanılmaz, çünkü bir fiyat sağlayıcıya ve tarihe bağlıdır; kaynak birimi bağlı değildir. Bu kural kursun kalanında da geçerlidir.
// izleme/maliyet.mjs — bes toplama biciminin bedeli: uretilen kayit, saklanan bayt ve // sistemin kendi yazma hizina orani. Girdiler K01'in hesaplari ve bu dersin varsayimlaridir. const TEPE_UC = 513.89; // K01: tepe uctaki istek/s const TEPE_YAZMA = 97.22; // K01: tepe yazma istek/s (sistemin kendi kalici kaydi) const OLAY_BAYT = 180; // IZ2 (varsayim): bir ham olcut kaydi (ad, deger, zaman, uc etiket) const KOVA_BAYT = 40; // IZ3 (varsayim): bir toplulastirilmis kova kaydi const KOVA = 20; // IZ4 (varsayim): histogram kova sayisi const SAKLAMA = 30; // IZ5 (varsayim): ham kaydin saklanma suresi (gun) const b = (x, n = 2) => x.toFixed(n); const BICIM = [ ["ham olay", TEPE_UC, OLAY_BAYT], ["1/10 orneklem", TEPE_UC / 10, OLAY_BAYT], ["1/100 orneklem", TEPE_UC / 100, OLAY_BAYT], ["saniyelik histogram", KOVA, KOVA_BAYT], ["saniyelik sayac+toplam", 2, KOVA_BAYT], ]; console.log(`${"toplama bicimi".padEnd(23)}${"kayit/s".padStart(10)}${"bayt/s".padStart(10)}` + `${"gunluk GB".padStart(11)}${"GB-ay (30 gun)".padStart(16)}${"sistem yazmasina oran".padStart(23)}`); for (const [ad, hiz, bayt] of BICIM) { const gunlukGB = (hiz * bayt * 86_400) / 1e9; console.log(`${ad.padEnd(23)}${b(hiz).padStart(10)}${b(hiz * bayt, 0).padStart(10)}` + `${b(gunlukGB, 3).padStart(11)}${b(gunlukGB * SAKLAMA, 2).padStart(16)}` + `${(b(hiz / TEPE_YAZMA) + " kat").padStart(23)}`); } const ham = TEPE_UC * OLAY_BAYT * 86_400 / 1e9; const hist = KOVA * KOVA_BAYT * 86_400 / 1e9; console.log(`\nham olay / saniyelik histogram = ${b(ham / hist, 0)} kat hacim`); console.log(`ornekleme istek hiziyla buyur, toplulastirma buyumez: istek hizi 2 katina ciksa ` + `ham ${b(2 * ham, 3)} GB/gun, histogram ${b(hist, 3)} GB/gun`); console.log(`10 olcut ayni bicimde toplanirsa hacim 10 katina cikar: ham ${b(10 * ham, 2)} GB/gun`);
toplama bicimi kayit/s bayt/s gunluk GB GB-ay (30 gun) sistem yazmasina oran ham olay 513.89 92500 7.992 239.76 5.29 kat 1/10 orneklem 51.39 9250 0.799 23.98 0.53 kat 1/100 orneklem 5.14 925 0.080 2.40 0.05 kat saniyelik histogram 20.00 800 0.069 2.07 0.21 kat saniyelik sayac+toplam 2.00 80 0.007 0.21 0.02 kat ham olay / saniyelik histogram = 116 kat hacim ornekleme istek hiziyla buyur, toplulastirma buyumez: istek hizi 2 katina ciksa ham 15.984 GB/gun, histogram 0.069 GB/gun 10 olcut ayni bicimde toplanirsa hacim 10 katina cikar: ham 79.92 GB/gun
Son sütun ölçünün en sert hâlidir. Her isteğe bir ölçüt kaydı düşürmek, tek bir ölçüt için sistemin kendi kalıcı yazmasının 5,29 katı kayıt üretir: gönderi takip servisi saniyede 97,22 durum olayı yazarken izleme 513,89 kayıt yazar. Önceki dersin listesinde on beş ölçüt vardı; hepsi bu biçimde toplansa izleme yükü sistemin yükünün yanında küçük bir kalem olmaktan çıkar. Son satır bunu doğrudan söylüyor: on ölçüt ham olayla günde 79,92 GB üretir.
İki biçim iki ayrı büyümeye bağlıdır ve fark tasarım kararını belirler. Örnekleme istek hızıyla büyür, çünkü örneklenen şey isteklerdir; istek hızı iki katına çıktığında ham hacim 7,992’den 15,984 GB’a çıkar. Toplulaştırma istek hızıyla büyümez, çünkü üretilen kayıt sayısı kova sayısıyla ve toplama aralığıyla belirlenir; aynı ikiye katlamada histogram 0,069 GB’da kalır. Ölçeklenen bir sistemde bu, izleme bedelinin ölçekle birlikte artıp artmayacağı sorusudur.
Kazanç Kayıpla Ölçülür
Bedeli düşüren her biçim bir şey kaybettirir ve kaybın adı konmadan karar verilemez. Kaybı ölçmek için tek bir nüfus üzerinde beş biçim birden uygulanır. Aşağıdaki düzenek bir modeldir: gecikmeler tam sayı aritmetiğiyle, görünür bir tohumla üretilir, bu yüzden koşum makineden bağımsız olarak tekrarlanır. Model gerçek bir dağılımı öngörmez; gösterdiği şey biçimlerin aynı veriye ne yaptığıdır.
Ölçülen büyüklük kuyruk gecikmesi (tail latency), yani dağılımın en yavaş ucudur — bekleyen işlerin dizisi anlamındaki kuyrukla ilgisi yoktur, buradaki kuyruk dağılımın kuyruğudur. Yüzdelikler Sistem Tasarımına Giriş kursunda tanımlandı ve burada yeniden tanımlanmaz.
// izleme/kayip.mjs — orneklemenin ve toplulastirmanin bilgi kaybi MODELI. Gecikmeler tam sayi // aritmetigiyle uretilir (tohum gorunur), boylece kosum makineden bagimsiz tekrarlanir. const N = 200_000; // model nufusu: 400 saniye x saniyede 500 istek const SANIYE = 500; // saniye basina istek (K01'in 513,89 tepe hizina yakin) const TOHUM = 20260730; let s = TOHUM; const rast = (n) => Math.floor((((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32)) * n); // Gecikmeler 0,1 ms biriminde tam sayidir: govde, orta kuyruk, uzak kuyruk, en uzak kuyruk. const KATMAN = [[900, 30, 30], [90, 60, 140], [9, 200, 700], [1, 900, 3100]]; const veri = []; for (const [pay, taban, genislik] of KATMAN) for (let i = 0; i < (N * pay) / 1000; i += 1) veri.push(taban + rast(genislik)); for (let i = veri.length - 1; i > 0; i -= 1) { // tohumlu karistirma const j = rast(i + 1); [veri[i], veri[j]] = [veri[j], veri[i]]; } const yuzdelik = (dizi, p) => { const d = [...dizi].sort((a, b) => a - b); return d[Math.min(d.length - 1, Math.floor((p / 100) * d.length))]; }; const ms = (x) => (x / 10).toFixed(1); const enBuyuk = (d) => d.reduce((a, c) => (c > a ? c : a), 0); const KENAR = [30, 40, 50, 60, 80, 100, 130, 170, 220, 290, 380, 500, 650, 850, 1100, 1500, 2000, 2600, 3400, Infinity]; const kova = new Array(KENAR.length).fill(0); for (const v of veri) kova[KENAR.findIndex((k) => v < k)] += 1; const kovaYuzdelik = (p) => { let toplam = 0; const hedef = (p / 100) * veri.length; for (let i = 0; i < kova.length; i += 1) { toplam += kova[i]; if (toplam >= hedef) return KENAR[i]; } return Infinity; }; const kovaEnBuyuk = KENAR[kova.findLastIndex((k) => k > 0)]; const ortalamalar = []; for (let i = 0; i < veri.length; i += SANIYE) ortalamalar.push(veri.slice(i, i + SANIYE).reduce((a, c) => a + c, 0) / SANIYE); const her = (k) => veri.filter((_, i) => i % k === 0); const tam = { p50: yuzdelik(veri, 50), p99: yuzdelik(veri, 99), p999: yuzdelik(veri, 99.9), enB: enBuyuk(veri) }; console.log(`tohum ${TOHUM}, ${N} istek, ${veri.length / SANIYE} saniye, ${KENAR.length} kova`); console.log(`gercek deger: p50 ${ms(tam.p50)} ms, p99 ${ms(tam.p99)} ms, ` + `p99,9 ${ms(tam.p999)} ms, en buyuk ${ms(tam.enB)} ms`); console.log(`\n${"toplama bicimi".padEnd(23)}${"saklanan kayit".padStart(15)}${"p50".padStart(8)}` + `${"p99".padStart(8)}${"p99,9".padStart(9)}${"en buyuk".padStart(10)}${"p99 hatasi".padStart(12)}`); const yaz = (ad, kayit, p50, p99, p999, enB) => console.log(`${ad.padEnd(23)}${kayit.toLocaleString("tr-TR").padStart(15)}${ms(p50).padStart(8)}` + `${ms(p99).padStart(8)}${ms(p999).padStart(9)}${ms(enB).padStart(10)}` + `${(((p99 - tam.p99) / tam.p99) * 100).toFixed(1).padStart(11)}%`); yaz("ham olay", veri.length, tam.p50, tam.p99, tam.p999, tam.enB); for (const k of [10, 100]) { const o = her(k); yaz(`1/${k} orneklem`, o.length, yuzdelik(o, 50), yuzdelik(o, 99), yuzdelik(o, 99.9), enBuyuk(o)); } yaz("saniyelik histogram", (veri.length / SANIYE) * KENAR.length, kovaYuzdelik(50), kovaYuzdelik(99), kovaYuzdelik(99.9), kovaEnBuyuk); yaz("saniyelik sayac+toplam", (veri.length / SANIYE) * 2, yuzdelik(ortalamalar, 50), yuzdelik(ortalamalar, 99), yuzdelik(ortalamalar, 99.9), enBuyuk(ortalamalar)); const genelOrt = veri.reduce((a, c) => a + c, 0) / veri.length; const ortOrt = ortalamalar.reduce((a, c) => a + c, 0) / ortalamalar.length; console.log(`\nortalama iki bicimde de tam korunuyor: ham ${ms(genelOrt)} ms, ` + `saniyelik ortalamalarin ortalamasi ${ms(ortOrt)} ms`); const yavas = veri.filter((v) => v >= 200).length; console.log(`200 ms ustu istek: ham veride ${yavas}, 1/100 orneklemde ` + `${her(100).filter((v) => v >= 200).length} (beklenen ${(yavas / 100).toFixed(1)})`);
tohum 20260730, 200000 istek, 400 saniye, 20 kova gercek deger: p50 4.6 ms, p99 20.0 ms, p99,9 90.3 ms, en buyuk 399.5 ms toplama bicimi saklanan kayit p50 p99 p99,9 en buyuk p99 hatasi ham olay 200.000 4.6 20.0 90.3 399.5 0.0% 1/10 orneklem 20.000 4.6 20.4 91.3 397.6 2.0% 1/100 orneklem 2.000 4.6 38.6 351.8 397.6 93.0% saniyelik histogram 8.000 5.0 22.0 110.0 Infinity 10.0% saniyelik sayac+toplam 800 5.8 7.9 8.8 8.8 -60.5% ortalama iki bicimde de tam korunuyor: ham 5.9 ms, saniyelik ortalamalarin ortalamasi 5.9 ms 200 ms ustu istek: ham veride 2000, 1/100 orneklemde 22 (beklenen 20.0)
Aynı Bedel, Farklı Kayıp
İki tablo yan yana okunduğunda dersin ana sonucu çıkıyor. 1/100 örneklem 2,40 GB-ay tutuyor, saniyelik histogram 2,07 GB-ay — bedelleri birbirine yakın. Kayıpları yakın değil: örneklemin p99 hatası yüzde 93,0, histogramınki yüzde 10,0. Örneklemde p99,9 tümüyle anlamsızlaşıyor, çünkü 2.000 kayıtta o yüzdelik yalnız iki kayda dayanıyor; gerçek 90,3 ms yerine 351,8 ms okunuyor. Aynı bedelle çok daha az bilgi kaybeden bir biçim varken kaybeden biçimi seçmek bir karar değil, bir gözden kaçırmadır.
Örneklemenin ne kaybettiği son satırda açıkça duruyor: ham veride 200 ms üstü 2.000 istek var, 1/100 örneklemde 22 tanesi görülüyor. Sayı beklenen 20,0’a yakın, yani örnekleme yanlı değil — kaybolan şey kesinlik. Bir yüzdelik ne kadar uçtaysa onu taşıyan kayıt sayısı o kadar azdır ve örnekleme önce en uçtaki bilgiyi siler. 1/10 örneklem hâlâ 20.000 kayıt tutuyor ve p99 hatası yüzde 2,0; kaybın örnekleme oranıyla doğrusal büyümediği buradan görünür.
Toplulaştırmanın kaybı başka biçimdedir. Histogram bütün istekleri sayar, hiçbirini atmaz, ama
değeri kova sınırına yuvarlar: p99 20,0 yerine 22,0 okunuyor ve hata kova genişliğinden gelir. En
büyük değer sütununda ise Infinity yazıyor — en üstteki kovanın üst sınırı olmadığı için histogram
en büyük gecikmeyi hiç sınırlayamıyor. Bunu geri getirmenin yolu saniye başına bir sayı daha
saklamaktır: en büyük değer. Bir sayının bedeli 40 bayttır.
Saniyelik sayaç ve toplam ise dağılımı tümüyle siliyor. Ortalama her iki biçimde de birebir korunuyor (5,9 ms), ama saniyelik ortalamaların p99’u 7,9 ms ve gördüğü en büyük değer 8,8 ms; gerçek en büyük 399,5 ms. Beş yüz isteğin ortalaması alındığında bir yavaş istek dört yüz doksan dokuz hızlı istek tarafından yutulur. Ortalama gecikme bildiren bir ölçüt, kuyruk gecikmesini ölçmez; bu biçimin ucuzluğu tam olarak o bilgiyi atmasından gelir.
Karar Kuralı
Üç sonuç, ölçüt başına verilecek kararın kuralını verir. Birincisi: bir toplama biçimi bedeliyle değil, bedel başına koruduğu bilgiyle seçilir. İkincisi: biçim, ölçütün mümkün kılacağı tanıya göre seçilir. Histogram “ne kadar yavaş” sorusunu ucuza yanıtlar ama “hangi istek yavaştı” sorusunu yanıtlayamaz, çünkü kayıt kimliği yoktur; örneklem tersini yapar — az sayıda ama tam kayıt tutar, dolayısıyla etiketleriyle birlikte bir isteğin izini verir. Önceki dersin on tanısından ölçüt bileşimiyle ayrılanlar tam kayıt ister, eşik aşımını görmek isteyenler histogramla yetinir.
Üçüncüsü ölçekleme kuralıdır: ham olay ve örneklem istek hızıyla büyür, toplulaştırma büyümez. Bugün 513,89 istek/s’de 2,40 GB-ay tutan bir örneklem, trafik iki katına çıktığında 4,80 GB-ay tutar; aynı koşulda histogram 2,07 GB-ay’da kalır. Bir izleme tasarımı sistemin ölçekleme planından bağımsız yapılamaz.
Özet
- Bir ölçütün bedeli üç kalemdir: üretilen kayıt, saklanan bayt ve eklenen kod yolu; üçüncüsü istek başına üretilen kayıt sayısıyla temsil edilir. Hacim GB-ay ile yazılır, para birimiyle değil.
- Her isteğe bir kayıt düşüren tek bir ölçüt, sistemin kendi kalıcı yazmasının 5,29 katı kayıt üretir (513,89’a karşı 97,22 kayıt/s); on ölçüt aynı biçimde günde 79,92 GB eder.
- Örnekleme istek hızıyla büyür, toplulaştırma büyümez: istek hızı ikiye katlandığında ham hacim 7,992’den 15,984 GB/güne çıkar, histogram 0,069 GB/günde kalır.
- 1/100 örneklem ile saniyelik histogram yakın bedeldedir (2,40 ve 2,07 GB-ay) ama p99 hataları yüzde 93,0 ve yüzde 10,0’dır; aynı bedelde daha az kaybeden biçim seçilir.
- Örnekleme önce en uçtaki bilgiyi siler (2.000 yavaş istekten 22’si görünür); toplulaştırma değeri kova sınırına yuvarlar ve üst kova sınırsız olduğu için en büyük değeri hiç sınırlayamaz.
- Ortalama saklamak dağılımı siler: saniyelik ortalamaların gördüğü en büyük değer 8,8 ms iken gerçek en büyük 399,5 ms’dir; ortalama her iki biçimde de 5,9 ms olarak korunur.
Sonraki Adım
Bu iki ders neyin toplanacağını ve hangi biçimde toplanacağını kararlaştırdı. Toplanan sayı henüz kimseye bir şey söylemiyor: bir yerde çizilecek ya da birini uyandıracak. İkisi aynı şey değildir ve aynı eşikle çalışmazlar. Bir çizim, ona zaten bakmakta olan birinin sorusunu yanıtlar; bir uyarı, bakmayan birini bakmaya çağırır ve yanlış çağırdığında bir maliyeti vardır. Sonraki ders ikisini ayırır, uyarı eşiğinin nereden geldiğini Sistem Tasarımına Giriş kursunun aylık 43,2 dakikalık kesinti bütçesine bağlar ve eşik seçimini iki sayıyla sınar: kaç yanlış uyarı üretiyor ve kaç gerçek olayı kaçırıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.