İçeriğe geç
academia.sh

Ders 04 / 18

Günlük Toplama

Günlük akışı aracı, tampon ve dizinleme katmanlarından geçirilir ve her katmanda kaybolan kayıt sayılır: tampon dolması, kırpma ve dizinleme gecikmesi. Yük iki katına çıktığında hangi katmanın önce kırıldığı ölçülür ve kaybın hangi soruyu yanıtsız bıraktığı, soru başına maliyetle birlikte yazılır.

İçindekiler

Bir önceki dersin bütün yanıtları sayıydı: kaç milisaniye, kaç istek, kaç hata. Ama hiçbir sayı neden sorusunu yanıtlamaz — faturalamanın p95’inin 1042 milisaniye olduğu bilinir, hangi kilitte beklendiği bilinmez. O bilgi ölçütte değil, sürecin kendi yazdığı metinde durur.

Günlük satırı ölçütten farklı bir şeydir: önceden tanımlı bir seri değil, olay anında yazılmış bir metindir ve kardinalitesi sınırsızdır. Bedeli de buradan gelir. Bir ölçüt hattı saniyede birkaç bin sayı taşır; günlük hattı saniyede on binlerce satır. Bu ders o hattı üç katmana ayırır — günlük aracısı, tampon ve dizinleyici — ve her katmanda kaç kaydın kaybolduğunu sayar.

Akış bölgesel ölçüm ağının akışıdır ve kurgudur; gerçek bir günlük deposu koşturulmaz, sayılan şey node ile saniye saniye koşan bir modeldir.

OG19. Satır hızları, karışım ve katman sınırları kurgudur ve önceden yazılıdır; benzetimde rastgelelik yoktur, aynı girdi her koşumda aynı sayıyı verir. OG20. Günlük aracısı saniyede 1400 satır okur ve 30.000 satırlık yerel tampon tutar; kuyruk 300.000 satır, dizinleyici saniyede 1000 satırdır. OG21. Kırpma 512 baytta yapılır. Hata satırı 620 bayttır ve son 108 baytında hata kodu ile sürecin ürettiği işlem kimliği durur. OG22. Tampon dolduğunda düşen satırların türü o saniyenin bileşimindedir; kuyruk tür ayrımı yapmaz. OG23. Bir satır yazıldıktan 60 saniyeden geç dizinlenirse “geç görünen” sayılır ve olay anında sorulan soru için yok kabul edilir. OG24. Bir soru, gerektirdiği satır türündeki kayıp kabul payının altındaysa yanıtlanır: oran soruları yüzde 10, sayım soruları yüzde 1. S11 ölçer değeri günlüğe yazılmadığı için, S12 servisler zincirinde ortak bir kimlik olmadığı için hiçbir yükte yanıtlanmaz.

Üç Katman ve Sınırları

// akis.mjs — gunluk akisi araci, tampon ve dizinleme katmanlarindan gecirilir; her katmanda kayip sayilir.
export const TABAN = 232, KARISIM = { istek: 0.86, hata: 0.02, "is-adimi": 0.12 };
export const BAYT = { istek: 180, hata: 620, "is-adimi": 240 };
export const IS_BAS = 600, IS_BIT = 1200, IS_EK = 900;   // gecelik toplu is penceresi, tamami is-adimi
export const SINIR = { araciOkuma: 1400, araciTampon: 30000, kuyruk: 300000, dizin: 1000,
  kirpma: 512, gorunurluk: 60 };
export const PENCERE = 3600;

export const kostur = (yuk, s = SINIR) => {
  let tampon = 0, kuyruk = 0;
  const d = { uretilen: 0, araciDusen: 0, kuyrukDusen: 0, dizinlenen: 0, gecGorunen: 0,
    enBuyukGecikme: 0, tur: {}, dusenTur: {} };
  for (const t of Object.keys(BAYT)) { d.tur[t] = 0; d.dusenTur[t] = 0; }
  for (let sn = 0; sn < PENCERE; sn++) {
    const girdi = {};                                    // o saniyenin satir bilesimi
    for (const t of Object.keys(BAYT)) girdi[t] = yuk * TABAN * KARISIM[t];
    if (sn >= IS_BAS && sn < IS_BIT) girdi["is-adimi"] += yuk * IS_EK;
    const toplam = Object.values(girdi).reduce((a, b) => a + b, 0);
    for (const t of Object.keys(BAYT)) d.tur[t] += girdi[t];
    d.uretilen += toplam;

    tampon += toplam;                                    // katman 1: araci
    const okunan = Math.min(tampon, s.araciOkuma);
    tampon -= okunan;
    if (tampon > s.araciTampon) {
      const dus = tampon - s.araciTampon;                // dusen satirlar o saniyenin bilesimindedir
      for (const t of Object.keys(BAYT)) d.dusenTur[t] += dus * girdi[t] / toplam;
      d.araciDusen += dus; tampon = s.araciTampon;
    }
    kuyruk += okunan;                                    // katman 2: tampon kuyrugu
    if (kuyruk > s.kuyruk) {                             // kuyruk tur ayrimi yapmaz, kayip her ture yayilir
      const dus = kuyruk - s.kuyruk;
      for (const t of Object.keys(BAYT)) d.dusenTur[t] += dus * girdi[t] / toplam;
      d.kuyrukDusen += dus; kuyruk = s.kuyruk;
    }
    const dizin = Math.min(kuyruk, s.dizin);             // katman 3: dizinleyici
    kuyruk -= dizin; d.dizinlenen += dizin;
    const gecikme = tampon / s.araciOkuma + kuyruk / s.dizin;
    d.enBuyukGecikme = Math.max(d.enBuyukGecikme, gecikme);
    if (gecikme > s.gorunurluk) d.gecGorunen += dizin;
  }
  return d;
};

Üç katmanın üç ayrı kırılma biçimi vardır ve ders için ayrımı yapan şey budur. Aracı ve kuyruk satır düşürür — kayıt yok olur. Dizinleyici satır düşürmez, geciktirir — kayıt sonunda gelir ama sorunun sorulduğu anda orada değildir. Kırpma ne düşürür ne geciktirir; satırı olduğu yerde bırakıp sonunu keser.

İki Yükte Katman Katman Kayıp

// katman.mjs — iki yukte katman katman kayip ve kaybin yanitsiz biraktigi soru.
import { TABAN, KARISIM, BAYT, IS_BAS, IS_BIT, IS_EK, SINIR, PENCERE, kostur } from "./akis.mjs";

const sy = (x) => Math.round(x).toLocaleString("tr-TR");
console.log(`gunluk akisi (kurgu, saniye saniye): ${PENCERE} sn, taban ${TABAN} satir/sn` +
  ` (${Object.entries(KARISIM).map(([t, p]) => `${t} %${p * 100}`).join(", ")})`);
console.log(`gecelik toplu is ${IS_BAS}-${IS_BIT} sn arasi +${IS_EK} satir/sn, tamami is-adimi`);
console.log(`sinirlar: araci okuma ${SINIR.araciOkuma}/sn tampon ${sy(SINIR.araciTampon)};` +
  ` kuyruk ${sy(SINIR.kuyruk)}; dizinleyici ${SINIR.dizin}/sn; kirpma ${SINIR.kirpma} bayt;` +
  ` gorunurluk esigi ${SINIR.gorunurluk} sn\n`);

console.log("yuk   uretilen   araci dusen  kuyruk dusen  kirpilan  en buyuk gecikme  gec gorunen  kayip %");
const sonuc = {};
for (const yuk of [1, 2]) {
  const d = kostur(yuk);
  const kirpilan = Object.entries(d.tur).reduce((t, [tur, n]) => t + (BAYT[tur] > SINIR.kirpma ? n : 0), 0);
  sonuc[yuk] = { ...d, kirpilan };
  console.log(`${`${yuk}x`.padEnd(6)}${sy(d.uretilen).padStart(9)}${sy(d.araciDusen).padStart(14)}` +
    `${sy(d.kuyrukDusen).padStart(14)}${sy(kirpilan).padStart(10)}` +
    `${`${d.enBuyukGecikme.toFixed(0)} sn`.padStart(18)}${sy(d.gecGorunen).padStart(13)}` +
    `${`%${(100 * d.araciDusen / d.uretilen).toFixed(1)}`.padStart(9)}`);
}

console.log("\nkaybin turu ve yanitsiz biraktigi soru");
console.log("kayip                     etkilenen satir                          yanitsiz kalan soru");
const iki = sonuc[2], SATIR = [
  ["araci tamponu doldu (2x)", `is-adimi ${sy(iki.dusenTur["is-adimi"])} satir` +
    ` (%${(100 * iki.dusenTur["is-adimi"] / iki.tur["is-adimi"]).toFixed(1)})`, "S5 S6"],
  ["kirpma (her iki yukte)", `hata satirlarinin son ${BAYT.hata - SINIR.kirpma} bayti (%100)`, "S10 S12"],
  ["dizinleme gecikmesi (2x)", `${sy(iki.gecGorunen)} satir ${SINIR.gorunurluk} sn'den gec gorundu`,
    "olay anindaki her soru"]];
for (const [a, b, c] of SATIR) console.log(`${a.padEnd(26)}${b.padEnd(40)}${c}`);

// soru kumesine karsi: [kimlik, satir turu, alan kirpilan kuyrukta mi, kabul edilen kayip payi]
const SORU_GUNLUK = [["S1", "istek", false, 0.10], ["S2", "istek", false, 0.10],
  ["S3", "istek", false, 0.10], ["S4", "istek", false, 0.10], ["S5", "is-adimi", false, 0.01],
  ["S6", "is-adimi", false, 0.01], ["S7", "istek", false, 0.10], ["S8", "istek", false, 0.10],
  ["S9", "is-adimi", false, 0.01], ["S10", "hata", true, 0.10]];
const mb = (b) => (b / 1024 ** 2).toFixed(0);
console.log("\nsoru kumesine karsi (S11 olcer degeri gunluge yazilmaz, S12 zincir capinda kimlik yok)");
console.log("yuk  saklanan MB  kaybolan MB  istek kayip  is-adimi kayip  yanitlanan  MB/soru  yanitsiz");
for (const yuk of [1, 2]) {
  const d = sonuc[yuk], kayip = (k) => d.dusenTur[k] / d.tur[k];
  const uret = Object.keys(BAYT).reduce((t, k) => t + d.tur[k] * BAYT[k], 0);
  const sakla = Object.keys(BAYT).reduce((t, k) =>
    t + (d.tur[k] - d.dusenTur[k]) * Math.min(BAYT[k], SINIR.kirpma), 0);
  const y = SORU_GUNLUK.filter(([, tur, kuyrukta, tol]) =>
    kayip(tur) <= tol && !(kuyrukta && BAYT[tur] > SINIR.kirpma));
  console.log(`${`${yuk}x`.padEnd(5)}${mb(sakla).padStart(11)}${mb(uret - sakla).padStart(13)}` +
    `${`%${(100 * kayip("istek")).toFixed(1)}`.padStart(13)}${`%${(100 * kayip("is-adimi")).toFixed(1)}`.padStart(16)}` +
    `${`${y.length}/12`.padStart(12)}${mb(sakla / y.length).padStart(9)}  ` +
    [...SORU_GUNLUK.filter((s) => !y.includes(s)).map(([k]) => k), "S11", "S12"].join(" "));
}
gunluk akisi (kurgu, saniye saniye): 3600 sn, taban 232 satir/sn (istek %86, hata %2, is-adimi %12)
gecelik toplu is 600-1200 sn arasi +900 satir/sn, tamami is-adimi
sinirlar: araci okuma 1400/sn tampon 30.000; kuyruk 300.000; dizinleyici 1000/sn; kirpma 512 bayt; gorunurluk esigi 60 sn

yuk   uretilen   araci dusen  kuyruk dusen  kirpilan  en buyuk gecikme  gec gorunen  kayip %
1x    1.375.200             0             0    16.704             79 sn      170.000     %0.0
2x    2.750.400       488.400             0    33.408            261 sn      895.000    %17.8

kaybin turu ve yanitsiz biraktigi soru
kayip                     etkilenen satir                          yanitsiz kalan soru
araci tamponu doldu (2x)  is-adimi 400.315 satir (%31.3)          S5 S6
kirpma (her iki yukte)    hata satirlarinin son 108 bayti (%100)  S10 S12
dizinleme gecikmesi (2x)  895.000 satir 60 sn'den gec gorundu     olay anindaki her soru

soru kumesine karsi (S11 olcer degeri gunluge yazilmaz, S12 zincir capinda kimlik yok)
yuk  saklanan MB  kaybolan MB  istek kayip  is-adimi kayip  yanitlanan  MB/soru  yanitsiz
1x           278            2         %0.0            %0.0        9/12       31  S10 S11 S12
2x           449          111         %6.0           %31.3        6/12       75  S5 S6 S9 S10 S11 S12

Tek kat yükte hiçbir satır düşmez. Aracı saniyede 1400 satır okuyabilir, tepe hız 1132’dir; kuyruk üç yüz bin satırlık kapasitesinin yakınına bile gelmez. Ama hat yine de tam çalışmaz: dizinleyici saniyede 1000 satır işler ve toplu iş penceresinde girdi bunun üstüne çıkar. Birikme kuyrukta kalır, kayıp olmaz — yalnız gecikir. En büyük gecikme 79 saniyedir ve altmış saniyelik görünürlük eşiğinin üstünde 170.000 satır dizinlenir. O satırlar kaybolmamıştır; yalnız sorulduğu anda orada değildirler.

Kırpmanın Sessizliği

Kırpma her iki yükte de aynı şeyi yapar ve tabloda en küçük görünen sayıdır: tek kat yükte iki megabayt. Ama etkilediği satır oranı yüzde yüzdür. Hata satırı 620 bayttır, kırpma sınırı 512; her hata satırının son 108 baytı kesilir ve orada iki alan durur — hata kodu ile sürecin ürettiği işlem kimliği.

Kaybın biçimi ayrıca önemlidir. Düşen satır kendini belli eder: sayaçlar tutmaz, satır aralığı atlar. Kırpılan satır belli etmez. Depoda 16.704 hata satırı vardır, hepsi okunabilir, hepsinin zaman damgası ve servis adı yerindedir. Yalnız hata kodu alanı boştur ve boş olmasının nedeni hata kodunun yazılmamış olması değil, satırın kesilmiş olmasıdır. Bu iki durum sorgu tarafında ayırt edilemez; ikisi de aynı boş hücreyi verir.

Bedeli S10’dur: hangi hata kodunun arttığı sorusu günlükten yanıtlanamaz. Bir önceki derste aynı soru ölçütten yanıtlanmıştı — sonuc etiketi beş değerliydi ve hata oranı hesaplanabiliyordu. Ama ölçütteki sonuc etiketi beş sınıfa ayırır; günlükteki hata kodu yüzlerce ayrı değer taşır ve incelemeyi asıl daraltan odur.

Yük İki Katına Çıkınca

İkinci satır soruyu yanıtlar: önce aracı kırılır. Tepe hız 2264 satır/saniyeye çıkar, aracının okuma hızı 1400’dür ve aradaki 864 satır/saniye tamponda birikir; otuz bin satırlık tampon yirmi dört saniyede dolar ve kalan beş yüz yetmiş altı saniye boyunca fazlalık düşer — 488.400 satır, üretilenin yüzde 17,8’i.

Kuyruk hiç kırılmaz, çünkü aracı zaten fazlalığı geçirmemektedir. Bu, katmanlı bir hattın karakteristik davranışıdır: en dar katman ötekileri korur ve koruduğu için ötekilerin sınırları hiç sınanmaz. Kuyruk kapasitesini artırmak bu hatta tek bir satır kazandırmaz.

Düşen satırların dağılımı da rastlantı değildir. Tampon taştığı an toplu iş penceresidir ve o pencerede girdinin yüzde yetmiş dokuzu iş adımı satırıdır; düşen 488.400 satırın 400.315’i iş adımıdır — bütün iş adımı satırlarının yüzde 31,3’ü. Gecelik işin kaç okuma işlediği ve neden geç bittiği artık günlükten sorulamaz.

Ama kayıp orada durmaz. Tampon taşarken hangi satırın düşeceğini seçen bir kural yoktur; istek satırlarının da yüzde altısı düşer. Toplu işle hiçbir ilgisi olmayan istek satırları, yalnız aynı tampondan geçtikleri için eksilir. Bir katmanın taşması, taşmaya sebep olmayan soruları da bozar — sayım soruları için yüzde altı zaten fazlasıyla yeter, oran soruları için sonuç yanlış değil ama eğridir ve eğri olduğu görünmez.

Son tablo bu kursun kuralını en açık biçimde yazar. Yük iki katına çıktığında saklanan veri 278 megabayttan 449 megabayta çıkar — yüzde altmış bir artış. Yanıtlanan soru 9’dan 6’ya iner. Soru başına maliyet 31 megabayttan 75 megabayta, iki buçuk katına çıkar. Daha çok veri toplandı, daha az soru yanıtlandı; ikisi aynı olayın iki yüzüdür.

Hangi Sınır Değişirse Hangi Soru Geri Gelir

Kırılan katman bilindiğine göre soru şudur: tek bir sınırı büyütmek kaç soruyu geri getirir?

// onlem.mjs — 2x yukte tek tek sinir degisiklikleri: hangi soru geri geliyor, kac megabayta.
import { BAYT, SINIR, kostur } from "./akis.mjs";

const SORU_GUNLUK = [["S1", "istek", false, 0.10], ["S2", "istek", false, 0.10],
  ["S3", "istek", false, 0.10], ["S4", "istek", false, 0.10], ["S5", "is-adimi", false, 0.01],
  ["S6", "is-adimi", false, 0.01], ["S7", "istek", false, 0.10], ["S8", "istek", false, 0.10],
  ["S9", "is-adimi", false, 0.01], ["S10", "hata", true, 0.10]];
const mb = (b) => (b / 1024 ** 2).toFixed(0), sy = (x) => Math.round(x).toLocaleString("tr-TR");
const DEGISIKLIK = [["degisiklik yok", {}],
  ["tampon 30 bin -> 300 bin", { araciTampon: 300000 }],
  ["araci okuma 1400 -> 2400", { araciOkuma: 2400 }],
  ["dizinleyici 1000 -> 2400", { dizin: 2400 }],
  ["kirpma 512 -> 1024 bayt", { kirpma: 1024 }],
  ["araci 2400 + dizinleyici 2400", { araciOkuma: 2400, dizin: 2400 }]];

console.log("2x yukte tek tek sinir degisikligi");
console.log("degisiklik                     araci dusen  kuyruk dusen  gecikme  saklanan MB  yanitlanan  MB/soru");
for (const [ad, yeni] of DEGISIKLIK) {
  const s = { ...SINIR, ...yeni }, d = kostur(2, s), kayip = (k) => d.dusenTur[k] / d.tur[k];
  const sakla = Object.keys(BAYT).reduce((t, k) =>
    t + (d.tur[k] - d.dusenTur[k]) * Math.min(BAYT[k], s.kirpma), 0);
  const y = SORU_GUNLUK.filter(([, tur, kuyrukta, tol]) =>
    kayip(tur) <= tol && !(kuyrukta && BAYT[tur] > s.kirpma));
  console.log(`${ad.padEnd(31)}${sy(d.araciDusen).padStart(11)}${sy(d.kuyrukDusen).padStart(14)}` +
    `${`${d.enBuyukGecikme.toFixed(0)} sn`.padStart(9)}${mb(sakla).padStart(13)}` +
    `${`${y.length}/12`.padStart(12)}${mb(sakla / y.length).padStart(9)}`);
}
2x yukte tek tek sinir degisikligi
degisiklik                     araci dusen  kuyruk dusen  gecikme  saklanan MB  yanitlanan  MB/soru
degisiklik yok                     488.400             0   261 sn          449        6/12       75
tampon 30 bin -> 300 bin           218.400        69.000   454 sn          495        6/12       83
araci okuma 1400 -> 2400                 0       459.400   299 sn          455        6/12       76
dizinleyici 1000 -> 2400           488.400             0    21 sn          449        6/12       75
kirpma 512 -> 1024 bayt            488.400             0   261 sn          452        7/12       65
araci 2400 + dizinleyici 2400            0             0     0 sn          556        9/12       62

Beş tek adımdan dördü hiçbir soruyu geri getirmez. Tamponu on katına çıkarmak aracıdaki kaybı yarıya indirir ama kaybı kuyruğa taşır ve gecikmeyi 261 saniyeden 454 saniyeye çıkarır: tampon kaybı önlemez, kaybı geciktirir ve gecikme de bir kayıptır. Aracının okuma hızını artırmak aracıdaki kaybı sıfırlar, karşılığında kuyrukta 459.400 satır düşer — toplam kayıp neredeyse aynıdır, yalnız katman değişmiştir. Dizinleyiciyi hızlandırmak tek başına hiçbir satır kurtarmaz; yalnız gecikmeyi 261 saniyeden 21 saniyeye indirir, ki bu da gerçek bir kazançtır ama başka bir kalemde.

Bir soruyu geri getiren tek ucuz adım kırpma sınırıdır: 512’den 1024 bayta çıkarmak üç megabayt ek yer tutar ve S10’u geri getirir — soru başına maliyet 75 megabayttan 65’e iner. Öteki soruları geri getiren tek yol iki sınırı birlikte değiştirmektir: kayıp sıfırlanır, gecikme sıfıra iner, yanıtlanan soru 9/12’ye çıkar ve soru başına maliyet 62 megabayta düşer — altı satırın en düşüğü. Hattın darboğazı tek bir katmanda değil, iki katmanın oranındadır.

Özet

  • Üç katman üç ayrı biçimde bozulur: aracı ve kuyruk satır düşürür, dizinleyici geciktirir, kırpma satırı bırakıp sonunu keser.
  • Tek kat yükte hiçbir satır düşmez ama dizinleme gecikmesi 79 saniyeye çıkar ve 170.000 satır 60 saniyelik görünürlük eşiğinden geç görünür — kaybolmadan yanıtsız kalmış veri.
  • Kırpma yükten bağımsızdır ve hata satırlarının yüzde yüzünde son 108 baytı siler; boş alan ile kesilmiş alan sorguda ayırt edilemez, S10 yanıtsız kalır.
  • Yük iki katına çıkınca önce aracı kırılır: 488.400 satır düşer (%17,8), kuyruk hiç taşmaz — en dar katman ötekilerin sınırlarını hiç sınatmaz.
  • Taşma seçici değildir: düşen satırların 400.315’i iş adımı (%31,3) ama istek satırlarının da %6’sı düşer; toplu işle ilgisi olmayan sorular da bozulur.
  • Saklanan veri 278’den 449 MB’a çıkarken yanıtlanan soru 9/12’den 6/12’ye iner; soru başına maliyet 31 MB’dan 75 MB’a çıkar. Tek sınır büyütmenin dördü hiçbir soruyu geri getirmez; tampon kaybı kuyruğa taşır, aracı hızı kaybı katman değiştirir, çözüm iki sınırın oranındadır.

Sonraki Adım

Buraya kadar günlük satırı bir bütün olarak sayıldı: kaç satır üretildi, kaç satır düştü, kaç bayt saklandı. Ama depoya ulaşan satır hâlâ metindir ve metin üzerinde yanıtlanabilecek soru sayısı çok azdır — bir dizeyi arayabilir, kaç satırda geçtiğini sayabilirsin; hangi servisin hata oranının hangi ortamda arttığını soramazsın. Bir sonraki ders gerçek günlük satırlarını ayrıştırır ve aranabilir alanlara çevirir: yapılandırılmamış metinle kaç soru yanıtlanıyor, alanlar üretilince kaç soru yanıtlanabilir hâle geliyor ve ayrıştırma kuralının sessizce kaçırdığı satırın bedeli ne.

İ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