İçeriğe geç
academia.sh

Ders 05 / 18

Günlük Ayrıştırma ve Yapılandırma

Gerçek günlük satırları ayrıştırılıp aranabilir alanlara çevrilir ve iki depo biçimi soru kümesine karşı sayılır: yapılandırılmamış metin kaç soruyu yanıtlıyor, alanlar üretilince kaç soru açılıyor. Biçim değiştiğinde ayrıştırma kuralının sessizce kaçırdığı satır sayılır ve yanlış çıkan yanıtın bedeli yazılır.

İçindekiler

Bir önceki ders satırı bir bütün olarak saydı: kaç satır üretildi, kaç satır düştü, kaç bayt saklandı. Depoya ulaşan satır ise hâlâ metindir. Metin üzerinde bir dizeyi arayabilir ve kaç satırda geçtiğini sayabilirsin; hangi bölgenin okumasının hangi ortamda doğrulamadan geçmediğini soramazsın, çünkü bunun için satırın parçalara ayrılmış olması gerekir.

Bu ders o adımı ölçer. Gerçek satırlar bir ayrıştırma kuralından geçirilir, aranabilir alanlara çevrilir ve iki depo biçimi — ham metin ile alanlar — aynı soru kümesine karşı sayılır. Üçüncü ölçü kuralın kaçırdığı satırdır: bir servis biçim değiştirdiğinde alanlar sessizce boş kalır ve sorgu hata vermeden yanlış yanıt döndürür.

Satırlar bölgesel ölçüm ağının gecelik doğrulama akışındandır ve kurgudur; her satırın doğru karşılığı ayrıca tutulur ve ölçüm ona karşı yapılır.

OG25. Doğrulama akışının 900 satırı kendi üretecimizle üretilir, tohum 20260903; her satırın yapılandırılmış karşılığı ayrıca saklanır ve doğruluk ona göre denetlenir. OG26. Doğrulayıcı servisi, satırların dörtte birlik noktasından sonra yeni bir sürüme geçer ve alan adlarını değiştirir: bolge yerine alan, sonuc yerine durum, sure=…ms yerine sure_ms=…. OG27. Ayrıştırma kuralı eski biçime göre yazılmıştır ve sürüm geçişinden sonra değiştirilmez. OG28. 07 bölgesinin düşen okumalarının çoğunu doğrulayıcı yazar; 13 bölgesinin düşenlerini öteki servisler yazar. OG29. Yapılandırılmamış metin yalnız önceden bilinen tek bir dizeyi sayabilir; gruplama ve sayısal işlem alan gerektirir. OG30. Bir alan, gruplama ya da sayısal soruda kullanılabilmesi için satırların en az yüzde 80’inde dolu olmalıdır.

Gerçek Günlük Satırları

// satir.mjs — gercek gunluk satirlari uretilir; her satirin dogru karsiligi ayrica tutulur.
export const uretec = (t) => () => (t = (t * 1103515245 + 12345) % 2147483648) / 2147483648;
export const BOLGE = ["01", "03", "05", "07", "09", "11", "13", "15", "17", "19", "21"];
export const DUSME = { "07": [0.80, 0.10], "13": [0.12, 0.26] };
export const SERVIS = ["okuma-toplayici", "dogrulayici", "faturalama", "is-emri"];
const iki = (n) => String(n).padStart(2, "0");

// dogrulayici yeni surume gectikten sonra alan adlarini degistirir: bolge->alan, sonuc->durum
export const uret = (adet = 900, tohum = 20260903) => {
  const rast = uretec(tohum), satir = [], kayit = [];
  for (let i = 0; i < adet; i++) {
    const servis = SERVIS[Math.floor(rast() * SERVIS.length)];
    const yeniBicim = servis === "dogrulayici" && i > adet * 0.25;   // surum gecisi
    const b = BOLGE[Math.floor(rast() * BOLGE.length)];
    // dusme orani bolgeye gore: [dogrulayicida, oteki servislerde]
    const [dd, od] = DUSME[b] ?? [0.10, 0.07];
    const dustu = rast() < (servis === "dogrulayici" ? dd : od);
    const sure = 60 + Math.floor(rast() * 320), ornek = `u${iki(Math.floor(rast() * 29))}`;
    const zaman = `2026-08-02T02:${iki(Math.floor(i / 15))}:${iki(i % 60)}Z`;
    kayit.push({ servis, bolge: b, sonuc: dustu ? "dustu" : "gecti", sure, ornek, yeniBicim });
    satir.push(yeniBicim
      ? `${zaman} ortam=uretim servis=${servis} ornek=${ornek} duzey=info olay=okuma` +
        ` alan=${b} durum=${dustu ? "dustu" : "gecti"} sure_ms=${sure}`
      : `${zaman} uretim ${servis}[${ornek}] INFO okuma islendi bolge=${b}` +
        ` sonuc=${dustu ? "dustu" : "gecti"} sure=${sure}ms`);
  }
  return { satir, kayit };
};

İki biçim de aynı bilgiyi taşır; fark yalnız adlarındadır. Bir günlükleme sözleşmesi değişikliğinin en sık görülen sonucu budur ve hiçbir servis için hata değildir — her iki satır da geçerlidir.

Ayrıştırma ve Kaçırılan Satır

// ayristir.mjs — yapilandirilmamis metin ile aranabilir alanlarin soru kumesine karsi karsilastirmasi.
import { uret, SERVIS } from "./satir.mjs";

const { satir, kayit } = uret();
console.log(`uretilen gunluk satiri: ${satir.length}`);
console.log("ornek satirlar");
for (const i of [0, 1, 227, 232]) console.log(`  ${satir[i]}`);

// eski bicime gore yazilmis ayristirma kurali
const KURAL = /^(\S+) (\w+) ([\w-]+)\[(\w+)\] (\w+) (.*?) bolge=(\d+) sonuc=(\w+) sure=(\d+)ms$/;
const alan = satir.map((s) => {
  const m = KURAL.exec(s);
  return m ? { zaman: m[1], ortam: m[2], servis: m[3], ornek: m[4], bolge: m[7], sonuc: m[8], sure: +m[9] }
    : { zaman: s.slice(0, 20).trim(), ortam: "", servis: "", ornek: "", bolge: "", sonuc: "", sure: null };
});
const eslesen = alan.filter((a) => a.bolge).length;
console.log(`\nayristirma kurali ${eslesen}/${satir.length} satiri esledi;` +
  ` ${satir.length - eslesen} satirda alanlar sessizce bos (%${(100 * (satir.length - eslesen) / satir.length).toFixed(1)})`);

// S7: hangi bolgenin okumasi dogrulanmadi
const say = (kaynak, bAl, sAl) => {
  const t = {};
  for (const k of kaynak) { const b = bAl(k); if (!b) continue; t[b] ??= [0, 0]; t[b][0]++; if (sAl(k) === "dustu") t[b][1]++; }
  return t;
};
const enKotu = (t) => Object.entries(t).sort((a, b) => b[1][1] - a[1][1])[0];
const gercek = say(kayit, (k) => k.bolge, (k) => k.sonuc), cikan = say(alan, (a) => a.bolge, (a) => a.sonuc);
const [gb, gv] = enKotu(gercek), [cb, cv] = enKotu(cikan);
console.log("\nS7 'hangi bolgenin okumasi dogrulanmadi'");
console.log(`  gercek        bolge ${gb}  ${gv[1]} dusen / ${gv[0]} okuma`);
console.log(`  ayristirmadan bolge ${cb}  ${cv[1]} dusen / ${cv[0]} okuma` +
  `   sapma %${(100 * Math.abs(cv[1] - gv[1]) / gv[1]).toFixed(1)}   yanit ayni mi: ${gb === cb ? "evet" : "hayir"}`);
console.log(`  kural bolge ${gb} icin ${gercek[gb][1] - (cikan[gb]?.[1] ?? 0)} dusen okumayi hic gormedi`);

// yapilandirilmamis metin yalniz bilinen tek bir dizeyi sayabilir
const DIZE = "okuma islendi", hamSay = satir.filter((x) => x.includes(DIZE)).length;
console.log(`\nS5 'gecelik is kac okuma isledi'`);
console.log(`  gercek ${kayit.length}   ham metinde "${DIZE}" gecen satir ${hamSay}` +
  `   sapma %${(100 * (kayit.length - hamSay) / kayit.length).toFixed(1)}`);

// S1: servis ortalamasi alanlardan dogru cikiyor mu
const ort = (l, s, f) => { const g = l.filter((x) => x.servis === s).map(f); return g.reduce((a, b) => a + b, 0) / g.length; };
const enYavas = (l, f) => SERVIS.map((s) => [s, ort(l, s, f)]).sort((a, b) => b[1] - a[1])[0];
const [gs, gd] = enYavas(kayit, (k) => k.sure), [as_, ad] = enYavas(alan.filter((a) => a.servis), (a) => a.sure);
console.log(`S1 'hangi servis yavasladi'   gercek ${gs} ${gd.toFixed(0)} ms` +
  `   ayristirmadan ${as_} ${ad.toFixed(0)} ms   yanit ayni mi: ${gs === as_ ? "evet" : "hayir"}`);

// bu akisin kapsamindaki alti soru; otekiler baska satir turlerinde durur
const SORU = [["S1", ["servis", "sure"], "sayisal", gs === as_], ["S2", ["sure"], "sayisal", true],
  ["S3", ["ortam", "sonuc"], "grupla", true], ["S5", ["dize"], "say", hamSay === kayit.length],
  ["S7", ["bolge", "sonuc"], "grupla", gb === cb], ["S8", ["ornek"], "grupla", true]];
const kapsama = (a) => alan.filter((x) => x[a] !== "" && x[a] !== null).length / alan.length;
const bayt = satir.reduce((t, x) => t + x.length, 0);
const alanBayt = alan.reduce((t, a) => t + Object.values(a).join("").length + Object.keys(a).length * 2, 0);
console.log("\ndepo bicimi        bayt   yanitlanabilen   dogru yanit   bayt/dogru soru   yanlis yanitlanan");
const yaz = (ad2, b, y) => {
  const d = y.filter(([, , , dogru]) => dogru);
  console.log(`${ad2.padEnd(18)}${b.toLocaleString("tr-TR").padStart(6)}${`${y.length}/6`.padStart(15)}` +
    `${`${d.length}/6`.padStart(14)}${(d.length ? Math.round(b / d.length).toLocaleString("tr-TR") : "-").padStart(18)}   ` +
    (y.filter((q) => !d.includes(q)).map(([k]) => k).join(" ") || "-"));
};
yaz("ham metin", bayt, SORU.filter(([, , i]) => i === "say"));
yaz("alanlar + dizin", bayt + alanBayt, SORU.filter(([, ger, , ]) =>
  ger.every((g) => g === "dize" || kapsama(g) >= 0.80)));
uretilen gunluk satiri: 900
ornek satirlar
  2026-08-02T02:00:00Z uretim dogrulayici[u02] INFO okuma islendi bolge=11 sonuc=gecti sure=210ms
  2026-08-02T02:00:01Z uretim okuma-toplayici[u21] INFO okuma islendi bolge=01 sonuc=gecti sure=217ms
  2026-08-02T02:15:47Z ortam=uretim servis=dogrulayici ornek=u21 duzey=info olay=okuma alan=05 durum=gecti sure_ms=250
  2026-08-02T02:15:52Z ortam=uretim servis=dogrulayici ornek=u08 duzey=info olay=okuma alan=11 durum=gecti sure_ms=83

ayristirma kurali 736/900 satiri esledi; 164 satirda alanlar sessizce bos (%18.2)

S7 'hangi bolgenin okumasi dogrulanmadi'
  gercek        bolge 07  29 dusen / 97 okuma
  ayristirmadan bolge 13  18 dusen / 51 okuma   sapma %37.9   yanit ayni mi: hayir
  kural bolge 07 icin 21 dusen okumayi hic gormedi

S5 'gecelik is kac okuma isledi'
  gercek 900   ham metinde "okuma islendi" gecen satir 736   sapma %18.2
S1 'hangi servis yavasladi'   gercek dogrulayici 225 ms   ayristirmadan dogrulayici 238 ms   yanit ayni mi: evet

depo bicimi        bayt   yanitlanabilen   dogru yanit   bayt/dogru soru   yanlis yanitlanan
ham metin         88.590            1/6           0/6                 -   S5
alanlar + dizin   140.932            6/6           4/6            35.233   S5 S7

Kural 900 satırın 736’sını eşler. Kalan 164 satır reddedilmez; ayrıştırma başarısız olduğunda satır atılmaz, yalnız alanları boş kalır. Depoda 900 kayıt vardır, hepsi aranabilir, hepsinin zaman damgası yerindedir. Yalnız 164’ünde bölge, sonuç, servis ve süre alanları boştur ve bu, bir sorgunun sonucunda hiçbir işaret bırakmaz.

Sessizce Değişen Yanıt

S7 satırı bu dersin en pahalı sayısını taşır. Gerçekte en çok okuma düşen bölge 07’dir: doksan yedi okumanın yirmi dokuzu doğrulamadan geçmemiştir. Ayrıştırılmış alanlardan çıkan yanıt ise 13’tür. Sapma yalnız yüzde 37,9 değildir — yanıt farklıdır.

Nedeni birinci bölümdeki tasarımdadır. 07 bölgesinin düşen okumalarının çoğunu doğrulayıcı yazar; doğrulayıcı yeni biçime geçmiştir; yeni biçim kurala uymaz. Kural, 07 bölgesi için yirmi bir düşen okumayı hiç görmemiştir. 13 bölgesinin düşenlerini ise öteki servisler yazar, onlar eski biçimdedir ve hepsi sayılır. Sonuç, saha ekibinin yanlış bölgeye gitmesidir: iş emirleri 13 bölgesi için açılır, 07 bölgesinin sayaçları okunmamış kalır ve fatura kesilmez.

Bu, ölçüt tarafında görülmeyen bir kayıp türüdür. Bir ölçüt eksikse seri kesilir ve kesinti görülür. Bir alan boşsa sorgu yine çalışır; yalnız daha az satır üzerinde çalışır ve bunu söylemez. Kaybın işareti kaybın kendisiyle birlikte kaybolur.

S5 satırı aynı olguyu daha basit biçimde gösterir. Gecelik işin kaç okuma işlediği sorusu ham metinde okuma islendi dizesini saymakla yanıtlanabilir — bu, yapılandırılmamış metnin gerçekten yanıtlayabildiği tek soru türüdür. Ama yeni biçim o dizeyi yazmaz; sayım 900 yerine 736 verir, yüzde 18,2 eksik. Grep ile alınan sayı da, ayrıştırmadan alınan sayı da aynı nedenle aynı miktarda yanlıştır.

S1 ise doğru yanıt verir: en yavaş servis her iki hesapta da doğrulayıcıdır. Ama değeri kayar — gerçek ortalama 225, ayrıştırmadan çıkan 238 milisaniye. Aynı eksik veri bir soruda yanıtı değiştirir, ötekinde yalnız sayıyı kaydırır. Hangisinin olacağı verinin dağılımına bağlıdır ve sorgu yazarken bilinmez.

Metin, Alan ve Bedel

Son tablo iki depo biçimini yan yana koyar. Ham metin 88.590 bayttır ve altı sorudan birini yanıtlayabilir — o da yanlış. Alanlar üretildiğinde bayt 140.932’ye çıkar, yüzde elli dokuz artış, ve altı sorunun altısı yanıtlanabilir hâle gelir. Artan şey satırlar değil, alan adları, ayrılmış değerler ve arama dizinidir.

Ama tablonun asıl sütunu üçüncüsüdür: yanıtlanabilen altı sorunun yalnız dördü doğru yanıtlanır. “Yanıtlanabilir” ile “doğru” arasındaki bu iki soruluk boşluk, bu dersin ölçtüğü şeydir. Doğru yanıt başına maliyet 35.233 bayttır; altı sorunun altısı doğru olsaydı 23.489 bayt olurdu. Aradaki fark, kuralın bakımsız kalmasının fiyatıdır ve bunu tetikleyecek bir uyarı yoktur — eşleşmeyen satır oranı bir ölçüt olarak toplanmadıkça yüzde 18,2 hiçbir yerde görünmez.

Kuralı Onarmanın Beş Yolu

Beş ayrıştırma seçeneği aynı satır kümesine uygulanır; eşleşen satır, doğru yanıt ve bayt birlikte ölçülür.

// secenek.mjs — ayristirma seceneklerinin esledigi satir, dogru yanit ve bayt bedeli.
import { uret } from "./satir.mjs";

const { satir, kayit } = uret();
const ESKI = /^(\S+) (\w+) ([\w-]+)\[(\w+)\] \w+ .*? bolge=(\d+) sonuc=(\w+) sure=(\d+)ms$/;
const YENI = /^(\S+) ortam=(\w+) servis=([\w-]+) ornek=(\w+) .*? alan=(\d+) durum=(\w+) sure_ms=(\d+)$/;
const kv = (s) => Object.fromEntries([...s.matchAll(/(\w+)=(\S+)/g)].map((m) => [m[1], m[2]]));
const ESLEM = { alan: "bolge", durum: "sonuc", sure_ms: "sure" };
const duzelt = (o) => Object.fromEntries(Object.entries(o).map(([k, v]) => [ESLEM[k] ?? k, v]));
const bicimden = (m) => ({ ortam: m[2], servis: m[3], ornek: m[4], bolge: m[5], sonuc: m[6], sure: +m[7] });
const bos = { ortam: "", servis: "", ornek: "", bolge: "", sonuc: "", sure: null };

const SECENEK = [
  ["tek kural (eski bicim)", (s) => { const m = ESKI.exec(s); return m ? bicimden(m) : bos; }],
  ["iki kural (eski + yeni)", (s) => { const m = ESKI.exec(s) || YENI.exec(s); return m ? bicimden(m) : bos; }],
  ["anahtar-deger, eslemsiz", (s) => ({ ...bos, ...kv(s) })],
  ["anahtar-deger + alan eslemi", (s) => ({ ...bos, ...duzelt(kv(s)) })],
  ["kaynakta yapilandirilmis", null],                  // butun satirlar tek sozlesmeyle yazilir
];
const yapilandirilmis = kayit.map((k) => `zaman=2026-08-02T02:00:00Z ortam=uretim servis=${k.servis}` +
  ` ornek=${k.ornek} olay=okuma bolge=${k.bolge} sonuc=${k.sonuc} sure=${k.sure}`);
const enKotu = (l) => {
  const t = {};
  for (const a of l) { if (!a.bolge) continue; t[a.bolge] ??= [0, 0]; t[a.bolge][0]++; if (a.sonuc === "dustu") t[a.bolge][1]++; }
  return Object.entries(t).sort((a, b) => b[1][1] - a[1][1])[0];
};
const [gercekBolge] = enKotu(kayit), hamBayt = satir.reduce((t, x) => t + x.length, 0);

console.log("secenek                        eslesen  S7 yaniti  S7 dogru  S5 dogru   bayt   bayt/dogru soru");
for (const [ad, f] of SECENEK) {
  const kaynak = f ? satir : yapilandirilmis;
  const alan = f ? satir.map(f) : yapilandirilmis.map((s) => ({ ...bos, ...kv(s) }));
  const dolu = alan.filter((a) => a.bolge).length;
  const [b] = enKotu(alan), s7 = b === gercekBolge, s5 = dolu === kayit.length;
  const bayt = kaynak.reduce((t, x) => t + x.length, 0) +
    alan.reduce((t, a) => t + Object.values(a).join("").length + Object.keys(a).length * 2, 0);
  const dogru = 4 + (s7 ? 1 : 0) + (s5 ? 1 : 0);       // S1 S2 S3 S8 her secenekte dogru
  console.log(`${ad.padEnd(31)}${String(`${dolu}/900`).padStart(8)}${String(b).padStart(11)}` +
    `${(s7 ? "evet" : "hayir").padStart(10)}${(s5 ? "evet" : "hayir").padStart(10)}` +
    `${bayt.toLocaleString("tr-TR").padStart(8)}${Math.round(bayt / dogru).toLocaleString("tr-TR").padStart(18)}`);
}
console.log(`\nham metin ${hamBayt.toLocaleString("tr-TR")} bayt; gercek en kotu bolge ${gercekBolge}`);
secenek                        eslesen  S7 yaniti  S7 dogru  S5 dogru   bayt   bayt/dogru soru
tek kural (eski bicim)          736/900         13     hayir     hayir 121.132            30.283
iki kural (eski + yeni)         900/900         07      evet      evet 126.036            21.006
anahtar-deger, eslemsiz         736/900         13     hayir     hayir 116.154            29.039
anahtar-deger + alan eslemi     900/900         07      evet      evet 115.170            19.195
kaynakta yapilandirilmis        900/900         07      evet      evet 161.292            26.882

ham metin 88.590 bayt; gercek en kotu bolge 07

Üçüncü satır beklenmedik olanıdır. Anahtar-değer ayrıştırıcı her iki biçimi de okur ve hiçbir satırı reddetmez; buna rağmen S7 yanıtı hâlâ 13’tür. Nedeni, ayrıştırmanın başarılı olması ile alanın doğru adla çıkması arasındaki farktır: yeni biçim bölgeyi alan anahtarıyla yazar, sorgu bolge alanını arar, ikisi eşleşmez. Biçimden bağımsız bir ayrıştırıcı biçim sorununu çözer, ad sorununu çözmez.

Dördüncü satır en ucuz doğru seçenektir: aynı ayrıştırıcıya üç satırlık bir alan eşlemi eklenir ve doğru yanıt başına maliyet 30.283 bayttan 19.195 bayta iner. İki kurallı seçenek de doğrudur ama her yeni biçim bir kural daha ister; eşlem tablosu yalnız bir satır büyür.

Beşinci satır sorunu kaynağında çözer: bütün servisler tek bir sözleşmeyle yazar ve ayrıştırma kuralı hiç gerekmez. Kırılmaz, bakım istemez — ve en pahalısıdır: her satırda alan adları yinelendiği için bayt 115.170’ten 161.292’ye, yüzde kırk artar. Ayrıştırma bir bayt tasarrufudur ve tasarrufun karşılığı bakım yüküdür; hangisinin ucuz olduğu satır hacmine ve biçim değişikliği sıklığına bağlıdır.

Özet

  • Ayrıştırma kuralı 900 satırın 736’sını eşler; kalan 164 satır atılmaz, yalnız alanları boş kalır (%18,2) ve bu hiçbir sorgu sonucunda işaret bırakmaz.
  • Yapılandırılmamış metin altı sorudan yalnız birini yanıtlayabilir (bilinen tek bir dizeyi saymak); alanlar üretilince altısı da yanıtlanabilir hâle gelir, bayt %59 artar.
  • Yanıtlanabilir olmak doğru olmak değildir: altı sorunun dördü doğru yanıtlanır. S7’de yanıt bölge 07 yerine bölge 13 çıkar — kural 07 için 21 düşen okumayı hiç görmemiştir.
  • Aynı eksik veri S1’de yanıtı değiştirmez, yalnız değeri 225’ten 238 milisaniyeye kaydırır; hangi sorunun bozulacağı verinin dağılımına bağlıdır ve sorgu yazılırken bilinmez.
  • Doğru yanıt başına maliyet 35.233 bayttır; kural bakımlı olsaydı 23.489 bayt olurdu. Eşleşmeyen satır oranı bir ölçüt olarak toplanmadıkça %18,2 hiçbir yerde görünmez.
  • Biçimden bağımsız ayrıştırıcı biçim sorununu çözer, ad sorununu çözmez (hâlâ 736/900); alan eşlemi eklenince doğru yanıt başına 19.195 bayt olur. Kaynakta yapılandırılmış yazım kırılmaz ama %40 daha fazla bayt tutar.

Sonraki Adım

Buraya kadar ölçüt ve günlük ayrı ayrı toplandı, ayrıştırıldı ve soru kümesine karşı sayıldı. Ama her ölçümde saklama süresi sabit tutuldu ve bedel tek bir ayrıntı düzeyi üzerinden hesaplandı. Gerçek bir hat böyle işlemez: veri yaşlandıkça ayrıntısı azaltılır, seyreltilir, özetlenir ve sonunda atılır. Bir sonraki ders bu basamakları tarar ve her basamakta yanıtlanabilen soru sayısıyla baytı birlikte sayar: soru başına maliyet basamak basamak nereye gider, en ucuz basamak hangi soruyu kaybeder.

İ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