İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat