İçeriğe geç
academia.sh

Ders 06 / 19

Bağıntılandırma

Üç sinyalin bağıntı kimliği üzerinden tek olayda birleştirilmesi: aynı sorunun birleştirmesiz ve birleştirmeli kaç adımda ve kaç sinyalle yanıtlandığı, birleştirmenin bir kez ödenen maliyeti ve örnekleme oranının üç sinyalli olay sayısına etkisi.

İçindekiler

Üç sinyal de kodda üretiliyor, hiçbiri toplayıcıya bağlı değil, hepsi aynı bağıntı kimliğini taşıyor. Ama hâlâ üç ayrı yerde duruyorlar. Bir soru sorulduğunda günlük ayrı, ölçüt ayrı, iz ayrı okunuyor ve aralarındaki bağ her seferinde yeniden kuruluyor. Bu ders o bağı bir kez kurup saklar ve iki yolun maliyetini karşılaştırır.

Sorulan soru üç parçalıdır ve üçü de ayrı bir sinyale düşer: bu istek neden başarısız oldu (günlük), nerede yavaşladı (iz), bu yavaşlık olağan mı (ölçüt). Hedef, hem yavaş raftan gelen hem ödenmemiş ücreti olan üyeye ait bir istektir; üç sorunun üçü de aynı anda geçerlidir.

Üç Sinyal, Tek Kimlik

Servis üç sinyali de yazar ve üçünde de aynı bağıntı kimliği vardır. İkinci dersin kapsanmayan çağrı yeri burada kapatılmıştır: bildirim çağrısı da bağlamı taşır, dolayısıyla kopuk iz kalmaz.

TL6 — histogramın kovaladığı nicelik, isteğin harcadığı bildirilen iş süresidir. Gerekçe: duvar saati süresi koşumdan koşuma değişir ve kova sınırına yakın istekler koşuma göre yer değiştirir; bildirilen iş her koşumda aynıdır, dolayısıyla kova payları karşılaştırılabilir kalır. Duvar saati süresi ayrıca iz aralığında durur ve “nerede yavaşladı” sorusunu o yanıtlar.

// servis6.mjs — <ad> <port> [ardil...]; uc sinyal de ayni baginti kimligini tasir
import { createServer } from "node:http";
import { appendFileSync, writeFileSync } from "node:fs";
const [ad, port, ...ardil] = process.argv.slice(2);
const SINIR = [10, 45, 150, 400];                  // bildirilen is kovalari (ms)
const kova = new Array(SINIR.length + 1).fill(0);
const sayac = { istek: 0, hata: 0 };
let oran = 1, no = 0;
const karma = (s) => { let h = 2166136261; for (const c of s) { h ^= c.charCodeAt(0); h = Math.imul(h, 16777619) >>> 0; } return h; };
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));
const uc = (p, u) => `http://127.0.0.1:${p}/is?uye=${u.get("uye")}&nusha=${u.get("nusha")}`;
const sifirla = () => { writeFileSync(`gunluk-${ad}.txt`, ""); writeFileSync(`iz-${ad}.txt`, ""); };
sifirla();

createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://y").searchParams;
  if (istek.url.startsWith("/oran")) {
    oran = Number(u.get("deger")); sayac.istek = 0; sayac.hata = 0; kova.fill(0); no = 0;
    sifirla(); yanit.end("tamam"); return;
  }
  if (istek.url === "/olcut") { yanit.end(JSON.stringify({ servis: ad, ...sayac, sinir: SINIR, kova })); return; }
  const uye = u.get("uye"), nusha = Number(u.get("nusha"));
  const gelen = istek.headers["x-iz"];
  const [izNo, ust, orn] = gelen ? gelen.split("|") : [`odunc-${nusha}`, "", ""];
  const ornek = gelen ? orn === "1" : karma(izNo) % 1000 < oran * 1000;
  const aralik = `${ad}${(no += 1)}`, t0 = performance.now();
  sayac.istek += 1;
  const kendiIs = ad === "katalog" && nusha % 5 === 0 ? 80 : 3;   // bildirilen is: kosumdan bagimsiz
  await bekle(kendiIs);
  let durum = ad === "ucretlendirme" && uye === "4023" ? 402 : 200, altIs = 0;
  for (const p of ardil) {
    const y = await fetch(uc(p, u), { headers: { "x-iz": `${izNo}|${aralik}|${ornek ? 1 : 0}` } });
    const govde = await y.json(); altIs += govde.is;
    if (y.status !== 200) durum = y.status;
  }
  const is = kendiIs + altIs, sure = Math.round(performance.now() - t0);
  if (durum !== 200) sayac.hata += 1;
  let k = 0; while (k < SINIR.length && is > SINIR[k]) k += 1; kova[k] += 1;
  appendFileSync(`gunluk-${ad}.txt`, JSON.stringify({ baginti: izNo, servis: ad, uye, nusha, durum }) + "\n");
  if (ornek) appendFileSync(`iz-${ad}.txt`,
    JSON.stringify({ iz: izNo, aralik, ust, servis: ad, sure, is, kova: k }) + "\n");
  yanit.statusCode = durum; yanit.end(JSON.stringify({ is, durum }));
}).listen(Number(port));

İki Yol

Sorgu aracı aynı soruyu iki kez yanıtlar. Birinci yolda üç depo ayrı ayrı okunur, her biri hedef kimliğe göre süzülür ve üç sonuç elde hizalanır. İkinci yolda önce bütün kayıtlar bağıntı kimliğine göre tek olayda toplanır, sonra soru o tek depodan sorulur. Adım tanımı bütün konuda olduğu gibidir: bir dosya ya da uç okuma, bir ayrıştırma, bir süzme, bir hizalama birer adımdır.

// sor6.mjs — <oran> [soru]; ayni soru once uc depodan, sonra birlesik olaydan yanitlanir
import { readFileSync } from "node:fs";
const SERVIS = ["odunc", "uyelik", "katalog", "ucretlendirme", "bildirim"];
const PORT = { odunc: 9601, uyelik: 9602, katalog: 9603, ucretlendirme: 9604, bildirim: 9605 };
const HEDEF = "odunc-15";                          // hem yavas raf hem odenmemis ucret
const [oran, kip] = process.argv.slice(2);
let adim = 0; const say = () => { adim += 1; };
const oku = (on, alan) => {                        // 5 dosya okuma + 1 ayristirma gecisi
  const k = SERVIS.flatMap((s) => { say(); return readFileSync(`${on}-${s}.txt`, "utf8").split("\n").filter(Boolean); });
  say(); return k.map((l) => JSON.parse(l)).map((r) => ({ ...r, anahtar: r[alan] }));
};
const olcutOku = async () => { const o = {}; for (const s of SERVIS) { say(); o[s] = await (await fetch(`http://127.0.0.1:${PORT[s]}/olcut`)).json(); } return o; };
const etiket = (m, k) => `[${k === 0 ? 0 : m.sinir[k - 1]},${k < m.sinir.length ? m.sinir[k] : "+"})`;

// birlestirmesiz: her sinyal ayri okunur, sonuclar elde hizalanir
adim = 0;
const g = oku("gunluk", "baginti"), iz = oku("iz", "iz"), m = await olcutOku();
const gh = g.filter((r) => r.anahtar === HEDEF); say();
const ih = iz.filter((r) => r.anahtar === HEDEF); say();
const kaynak = gh.find((r) => r.durum !== 200 && r.servis !== "odunc" && r.servis !== "uyelik");
const enUzun = ih.filter((r) => r.servis !== "odunc").sort((a, b) => b.sure - a.sure)[0];
const kokIz = ih.find((r) => r.servis === "odunc");
const pay = Math.round((100 * m.odunc.kova[kokIz.kova]) / m.odunc.istek);
say();
const yanit = `uye ${gh[0].uye}, durum ${gh[0].durum} (kaynak ${kaynak.servis}); en uzun ardil ` +
  `${enUzun.servis}; is ${etiket(m.odunc, kokIz.kova)} kovasinda, isteklerin %${pay}'si`;
if (kip === "soru") {
  console.log(`soru: ${HEDEF} istegi neden basarisiz oldu, nerede yavasladi, bu is olagan mi`);
  console.log(`${"yontem".padEnd(17)}${"adim".padStart(5)}${"sinyal".padStart(8)}  yanit`);
  console.log(`${"birlestirmesiz".padEnd(17)}${String(adim).padStart(5)}${"3".padStart(8)}  ${yanit}`);
}

// birlestirme: uc sinyal baginti kimligine gore tek olayda toplanir
adim = 0;
const g2 = oku("gunluk", "baginti"), iz2 = oku("iz", "iz"), m2 = await olcutOku();
const olay = new Map();
for (const r of g2) {
  if (!olay.has(r.anahtar)) olay.set(r.anahtar, { baginti: r.anahtar, sinyal: 2, aralik: 0 });
  const o = olay.get(r.anahtar);
  if (r.servis === "odunc") { o.uye = r.uye; o.durum = r.durum; }
  if (r.durum !== 200 && r.servis !== "odunc" && r.servis !== "uyelik") o.kaynak = r.servis;
}
for (const r of iz2) {
  const o = olay.get(r.anahtar); o.aralik += 1;
  if (o.sinyal === 2) o.sinyal = 3;
  if (r.servis === "odunc") { o.kova = etiket(m2.odunc, r.kova); o.pay = Math.round((100 * m2.odunc.kova[r.kova]) / m2.odunc.istek); }
  else if (!o.enUzun || r.sure > o.enUzunSure) { o.enUzun = r.servis; o.enUzunSure = r.sure; }
}
say();
const birlestirme = adim;
adim = 0; say(); const o = olay.get(HEDEF); say();
if (kip === "soru") {
  console.log(`${"birlestirmeli".padEnd(17)}${String(adim).padStart(5)}${"1".padStart(8)}  ` +
    `uye ${o.uye}, durum ${o.durum} (kaynak ${o.kaynak}); en uzun ardil ${o.enUzun}; ` +
    `is ${o.kova} kovasinda, isteklerin %${o.pay}'si`);
  console.log(`birlestirme maliyeti: ${birlestirme} adim, ${g2.length + iz2.length} kayit -> ` +
    `${olay.size} olay (bir kez odenir, her sorguda degil)`);
  console.log(`\n${"oran".padStart(5)}${"olay".padStart(6)}${"3 sinyalli".padStart(12)}${"2 sinyalli".padStart(12)}  eksik sinyal`);
}
const uc = [...olay.values()].filter((x) => x.sinyal === 3).length;
console.log(`${oran.padStart(5)}${String(olay.size).padStart(6)}${String(uc).padStart(12)}` +
  `${String(olay.size - uc).padStart(12)}  ${olay.size - uc === 0 ? "-" : "iz (orneklenmeyen istek)"}`);
cat > kos.mjs <<'EOF'
const UYE = ["4021", "4022", "4023", "4024"];      // 4023 odenmemis ucreti olan uye
const PORT = [9601, 9602, 9603, 9604, 9605];
const oran = process.argv[2];
for (const p of PORT) await fetch(`http://127.0.0.1:${p}/oran?deger=${oran}`);
for (let i = 1; i <= 100; i += 1)
  await fetch(`http://127.0.0.1:9601/is?uye=${UYE[(i - 1) % 4]}&nusha=${i}`);
EOF
node servis6.mjs ucretlendirme 9604 &
node servis6.mjs katalog 9603 &
node servis6.mjs bildirim 9605 &
node servis6.mjs uyelik 9602 9604 &
node servis6.mjs odunc 9601 9602 9603 9605 &
sleep 1

node kos.mjs 1.00 && node sor6.mjs 1.00 soru
node kos.mjs 0.50 && node sor6.mjs 0.50
pkill -f "node servis6.mjs"
soru: odunc-15 istegi neden basarisiz oldu, nerede yavasladi, bu is olagan mi
yontem            adim  sinyal  yanit
birlestirmesiz      20       3  uye 4023, durum 402 (kaynak ucretlendirme); en uzun ardil katalog; is [45,150) kovasinda, isteklerin %20'si
birlestirmeli        2       1  uye 4023, durum 402 (kaynak ucretlendirme); en uzun ardil katalog; is [45,150) kovasinda, isteklerin %20'si
birlestirme maliyeti: 18 adim, 1000 kayit -> 100 olay (bir kez odenir, her sorguda degil)

 oran  olay  3 sinyalli  2 sinyalli  eksik sinyal
 1.00   100         100           0  -
 0.50   100          49          51  iz (orneklenmeyen istek)

Yirmi Adım ile İki Adım

İki yanıt kelimesi kelimesine aynı. Fark maliyettedir: birleştirmesiz yol yirmi adım harcadı ve üç sinyale ayrı ayrı gitti, birleştirmeli yol iki adım harcadı ve tek bir olaya baktı. Yirmi adımın dağılımı da öğreticidir — on beşi yalnız okuma (beş günlük dosyası, beş iz dosyası, beş ölçüt ucu), ikisi ayrıştırma, üçü süzme ve hizalama. Soru üç parçalı olduğu için üç depoya da gitmek zorunluydu.

Birleştirme bu işi ortadan kaldırmıyor, bir kere yapıyor. On sekiz adımlık maliyet çıktıda ayrıca duruyor ve şu cümleyle işaretli: bir kez ödenir, her sorguda değil. Bin kayıt okunup yüz olaya indirgendi. İkinci soru sorulduğunda birleştirmesiz yol yine yirmi adım harcayacak, birleştirmeli yol yine iki. Bağıntılandırmanın kazancı tek bir sorguda değil, sorgu sayısında büyür.

Birleştirmenin mümkün olmasını sağlayan tek şey, üç sinyalin de aynı kimliği taşımasıdır. O kimlik ikinci derste günlüğe, dördüncü derste iz bağlamına girdi; ölçüt ise kimlik taşımıyor ve taşıyamaz — üçüncü ders bunu kimliğin toplamın içinde kaybolması olarak ölçmüştü. Ölçütün olaya katılma biçimi bu yüzden farklıdır: tekil istek ölçüte bağlanamaz, ama isteğin hangi kovaya düştüğü ve o kovanın popülasyondaki payı bağlanabilir. Birleşik olayın son alanı budur ve “bu yavaşlık olağan mı” sorusunu yanıtlayan tek şeydir: 15 numaralı isteğin işi, isteklerin yüzde yirmisinin düştüğü kovada. Tek başına iz “seksen milisaniye katalogda geçti” der, tek başına ölçüt “isteklerin yüzde yirmisi bu kovada” der; ikisi birleşince “bu istek, olağan olan yavaş yüzde yirmiden biri” olur.

Alt tablo birleştirmenin sınırını veriyor. Tam örneklemede yüz olayın yüzü de üç sinyal taşıyor. Örnekleme oranı yarıya inince olay sayısı değişmiyor — günlük her istek için yazıldığı için yüz olay duruyor — ama elli biri yalnız iki sinyal taşıyor: izleri hiç yazılmamış. Bu olaylarda “neden” ve “olağan mı” sorulabiliyor, “nerede” sorulamıyor. Dördüncü dersin örnekleme ölçüsü burada ikinci kez görünüyor: seyreltilen şey iz kayıtlarının hacmi, kaybedilen şey birleşik olayların çözünürlüğü.

Üç sinyalin bölüşümü de son hâlini alıyor. Günlük her istekte yazılır, kimlik taşır ve “kim, ne, neden” sorularını yanıtlar; maliyeti istek sayısıyla büyür. İz örneklenen isteklerde yazılır, kimlik ve yapı taşır, “nerede” sorusunu yanıtlar; maliyeti örnekleme oranıyla ayarlanır. Ölçüt kimlik taşımaz, sabit alan sayısı tutar ve “olağan mı, ne kadar sık” sorularını yanıtlar. Üçü aynı kimlikte buluşunca tek bir kayıttan üç ayrı soruya yanıt çıkıyor.

Özet

  • Üç sinyal de aynı bağıntı kimliğini taşıyınca tek olayda birleştirilebildi: 1000 kayıt 100 olaya indi.
  • Aynı soru birleştirmesiz 20 adım ve 3 sinyal, birleştirmeli 2 adım ve 1 sinyal harcadı; iki yanıt birebir aynı çıktı.
  • Birleştirmenin 18 adımlık maliyeti bir kez ödeniyor; kazanç tek sorguda değil, sorgu sayısında birikiyor.
  • Ölçüt kimlik taşımadığı için olaya tekil olarak değil, isteğin düştüğü kova ve o kovanın payı olarak katıldı — “bu yavaşlık olağan mı” sorusunu yanıtlayan tek alan bu oldu.
  • Örnekleme birleştirmenin çözünürlüğünü sınırlıyor: oran 0,50’ye inince 100 olayın 51’i iz sinyalinden yoksun kaldı ve “nerede” sorusu o olaylarda kapandı.

Sonraki Adım

Konu burada kapanıyor. Bir isteğin yolu artık görünüyor: kim istedi, nerede yavaşladı, hangi serviste hata doğdu, bu istek popülasyonun neresine düşüyor — hepsi tek kayıttan, iki adımda. Beş süreç arasında kaybolan istek diye bir şey kalmadı.

Kalan şey şu: bu kayıtların hiçbiri isteğin iyi mi kötü mü olduğunu söylemiyor. Ölçüm 15 numaralı isteğin işinin isteklerin yüzde yirmisinin düştüğü kovada olduğunu söylüyor; o yüzde yirminin kabul edilebilir olup olmadığını söylemiyor. Ücretlendirme servisinin 402 döndürdüğünü söylüyor; yüz istekte yirmi beş hatanın normal mi felaket mi olduğunu söylemiyor. Telemetri gözlem üretir, karar üretmez; kararın dayanacağı şey henüz kurulmadı. Sonraki ders o eksiği kapatır: ölçülen sinyalden bir gösterge tanımlanır, göstergeye bir hedef bağlanır ve hedefin dışarıya verilen sözle ilişkisi kurulur.

İ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