Ders 08 / 18
Enstrümantasyon Standartları
Beş servisin kendi adlandırmasıyla yazdığı telemetrinin servisler arası birleştirilememesi: on sorunun yerel şemalar ve ortak sözleşme üzerinde ayrı ayrı yanıtlanması, sessizce yanlış yanıtların sayılması ve sözleşmenin alan, servis değişikliği ve uyumsuz servis olarak bedelinin çıkarılması.
İçindekiler
Önceki derste beş servisin adım kayıtları tek bir ağaçta birleşti. Birleşmenin nedeni kodda yazılıydı: her servis aynı alan adlarını, aynı birimi ve aynı iz kimliğini kullanıyordu. Bu varsayım gerçek bir ağda karşılıksızdır. Beş servisi beş ekip yazar; her ekip kendi kaydını kendi alışkanlığıyla adlandırır, süreyi kendi birimine göre yazar, hatayı kendi bildiği biçimde işaretler. Kayıtlar tek tek doğrudur ve her ekip kendi gösterge tablosunda doğru sayıları görür. Kırılan şey, servisler arası sorudur.
Bu ders bölgesel ölçüm ağının (kurgu) beş servisini kendi yerel şemalarıyla yazdırır, aynı on soruyu önce bu şemalar üzerinde sorar, sonra ortak bir telemetri sözleşmesi tanımlayıp yeniden sorar. Sözleşmenin ne olduğu ortak yapısıyla anlatılır; şema ders içinde tanımlanır.
Aynı İsteğin Beş Kaydı
// soz/kayit.mjs — bes servisin ayni istegi kendi yerel semasiyla yazmasinin MODELI. Once dogru // degerler uretilir (gercek), sonra her servis kendi alan adlari ve birimiyle bir kayit yazar. // IG7: yerel semalar kurgudur. IG8: okuma ile fatura ayni "sure" adini farkli birimle yazar. // IG9: fatura zincirden gelen kimligi degil kendi istek numarasini tasir. IG10: isemri disaridan // alinmis bir bilesendir, kaydi degistirilemez. export const TOHUM = 20260803, ISTEK = 6000; export const SERVIS = ["gecit", "okuma", "dogrulama", "fatura", "isemri"]; export const TABAN = { gecit: 6, okuma: 21, dogrulama: 28, fatura: 47, isemri: 19 }; export const AKIS = "fatura_sorgu"; export function gercek() { let s = TOHUM; const r = () => ((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32); const cikti = []; for (let i = 0; i < ISTEK; i += 1) { const t0 = Date.UTC(2026, 7, 3) + Math.floor(r() * 3600000), atlandi = r() < 0.11, adimlar = []; for (const ad of SERVIS) { if (ad === "dogrulama" && atlandi) continue; adimlar.push({ servis: ad, sure_ms: Math.max(1, Math.round(TABAN[ad] * (0.6 + r() * 0.9))), durum: r() < ({ fatura: 0.014, isemri: 0.0005 }[ad] ?? 0.004) ? "hata" : "tamam" }); } cikti.push({ iz: `iz-${(i + 1).toString(36).padStart(4, "0")}`, t0, atlandi, adimlar }); } return cikti; } // Yerel semalar: ayni bilgi bes ayri adlandirma ve uc ayri birimle yazilir. export const YEREL = { gecit: (z, a) => ({ zaman: new Date(z.t0).toISOString(), servis: "gecit", islem: AKIS, sure_ms: a.sure_ms, durum: a.durum, iz: z.iz }), okuma: (z, a) => ({ t: Math.floor(z.t0 / 1000), birim: "okuma", eylem: "sorgu", sure: a.sure_ms * 1000, sonuc: a.durum === "hata" ? 1 : 0, izlem: z.iz }), dogrulama: (z, a) => ({ ts: z.t0, bilesen: "dogrulama", islem: "dogrula", gecikme: a.sure_ms / 1000, hata: a.durum === "hata", iz_kimlik: z.iz }), fatura: (z, a) => ({ zaman: new Date(z.t0).toISOString(), uygulama: "fatura", uc: "hesapla", sure: a.sure_ms, durum_kodu: a.durum === "hata" ? 500 : 200, istek_no: `f-${z.iz.slice(3)}` }), isemri: (z, a) => ({ zaman: z.t0, servis: "isemri", yordam: "emir_ac", islem_suresi: a.sure_ms, hata_kodu: a.durum === "hata" ? 7 : 0 }), }; export const yerelAkis = (G) => G.flatMap((z) => z.adimlar.map((a) => YEREL[a.servis](z, a))); if (import.meta.filename === process.argv[1]) { const G = gercek(), z = G[0]; console.log(`tohum ${TOHUM}, ${ISTEK} istek, ${SERVIS.length} servis, ` + `${G.reduce((x, y) => x + y.adimlar.length, 0)} kayit`); console.log(`\nbir istegin (${z.iz}) bes serviste yazdigi kayit:`); for (const a of z.adimlar) console.log(` ${JSON.stringify(YEREL[a.servis](z, a))}`); const alan = new Map(); for (const a of z.adimlar) for (const k of Object.keys(YEREL[a.servis](z, a))) alan.set(k, (alan.get(k) || 0) + 1); console.log(`\ntoplam ${alan.size} ayri alan adi, bes serviste ortak olan: ` + `${[...alan].filter(([, n]) => n === 5).map(([k]) => k).join(", ") || "yok"}`); }
tohum 20260803, 6000 istek, 5 servis, 29362 kayit
bir istegin (iz-0001) bes serviste yazdigi kayit:
{"zaman":"2026-08-03T00:21:34.006Z","servis":"gecit","islem":"fatura_sorgu","sure_ms":5,"durum":"tamam","iz":"iz-0001"}
{"t":1785716494,"birim":"okuma","eylem":"sorgu","sure":30000,"sonuc":0,"izlem":"iz-0001"}
{"ts":1785716494006,"bilesen":"dogrulama","islem":"dogrula","gecikme":0.035,"hata":false,"iz_kimlik":"iz-0001"}
{"zaman":"2026-08-03T00:21:34.006Z","uygulama":"fatura","uc":"hesapla","sure":54,"durum_kodu":200,"istek_no":"f-0001"}
{"zaman":1785716494006,"servis":"isemri","yordam":"emir_ac","islem_suresi":25,"hata_kodu":0}
toplam 24 ayri alan adi, bes serviste ortak olan: yok
Tek bir isteğin beş kaydında üç ayrılık birden var. Alan adı: aynı kavram beş serviste sure_ms,
sure, gecikme, islem_suresi adlarıyla geçiyor; toplam 24 ayrı alan adı sayılıyor ve beş
servisin hepsinde bulunan tek bir ortak alan yok. Birim: süre üç ayrı birimde yazılmış —
milisaniye, mikrosaniye, saniye — ve alan adı birimi söylemiyor. Değer uzayı: hata beş serviste
"hata" metni, 1 sayısı, true mantıksal değeri, 500 kodu ve 7 kodu olarak işaretlenmiş.
Dördüncü ayrılık kimlikte: fatura zincirden gelen iz kimliğini değil kendi istek numarasını
taşıyor, iş emri hiçbir kimlik taşımıyor.
Sözleşmenin Ortak Yapısı
Telemetri sözleşmesi bir araç değil, bir anlaşmadır: hangi alanın hangi adla, hangi birimde, hangi değer uzayında yazılacağını ve isteğin kimliğinin çağrıdan çağrıya nasıl taşınacağını sabitler. Bu derste kullanılan altı alanlık şema şudur:
| Alan | Anlam | Birim ve değer uzayı |
|---|---|---|
iz |
isteğin çağrı zinciri boyunca taşınan kimliği | metin, beş serviste aynı |
zaman_ms |
adımın başlangıcı | epoch milisaniye, tam sayı |
servis |
kaydı yazan servis | katalogdaki ad |
akis |
adımın ait olduğu iş akışı | ortak akış adı |
sure_ms |
adımın süresi | milisaniye |
durum |
adımın sonucu | tamam ya da hata |
Sözleşmenin karşısındaki çözüm, sorgu tarafında elle yazılmış bir eşleme tablosudur: “şu alan adını görürsen süre say, şu değeri görürsen hata say”. Aşağıdaki karşılaştırma iki yolu aynı soru kümesiyle ölçer.
On Soru, İki Aşama
// soz/sorgu.mjs — on soru iki asamada sorulur: once yerel semalar uzerinde elle yazilmis esleme // tablosuyla (IG11), sonra ortak sozlesmeye gecmis kayitlar uzerinde. Her yanit gercek degerle // karsilastirilir: sapma yuzde 5'i asarsa "yanlis", okunabilir alan yoksa "yok". import { gercek, YEREL, SERVIS, AKIS, ISTEK } from "./kayit.mjs"; const G = gercek(); // IG11: esleme tablosu ilk buldugu alani kullanir, deger uzayini bilmez. const ESLEME = { iz: ["iz", "izlem", "iz_kimlik", "istek_no"], zaman_ms: ["zaman_ms", "zaman", "ts", "t"], servis: ["servis", "birim", "bilesen", "uygulama"], akis: ["akis", "islem", "eylem", "yordam", "uc"], sure_ms: ["sure_ms", "sure", "gecikme", "islem_suresi"], durum: ["durum", "hata", "sonuc", "durum_kodu", "hata_kodu"] }; const oku = (k, alan) => { for (const a of ESLEME[alan]) if (a in k) return k[a]; return null; }; const sayi = (v) => (typeof v === "number" && Number.isFinite(v) ? v : null); const hataMi = (v) => v === "hata" || v === true; // eslemeyi yazanin bildigi iki bicim // IG12: sozlesme gecisi kaydin uretildigi yerde yapilir; isemri (IG10) yerel kaydinda kalir. const SOZLESME = ["iz", "zaman_ms", "servis", "akis", "sure_ms", "durum"], UYUMSUZ = ["isemri"]; const sozlesmeli = (z, a) => (UYUMSUZ.includes(a.servis) ? YEREL[a.servis](z, a) : { iz: z.iz, zaman_ms: z.t0, servis: a.servis, akis: AKIS, sure_ms: a.sure_ms, durum: a.durum }); const kayitlar = (f) => G.flatMap((z) => z.adimlar.map((a) => f(z, a))); const [YEREL_K, SOZ_K] = [kayitlar((z, a) => YEREL[a.servis](z, a)), kayitlar(sozlesmeli)]; const ort = (x) => (x.length ? x.reduce((a, b) => a + b, 0) / x.length : null); const p95 = (x) => (x.length ? x.slice().sort((a, b) => a - b)[Math.floor(x.length * 0.95)] : null); const grup = (K, alan) => { const m = new Map(); for (const k of K) { const g = oku(k, alan); if (g !== null) m.set(g, [...(m.get(g) ?? []), k]); } return m; }; const sure = (k) => sayi(oku(k, "sure_ms")) ?? 0; const izToplam = (K) => [...grup(K, "iz").values()].map((v) => v.reduce((x, k) => x + sure(k), 0)); const servisSure = (K, s) => (grup(K, "servis").get(s) ?? []).map((k) => sayi(oku(k, "sure_ms"))) .filter((v) => v !== null); const enYavas = (K) => SERVIS.map((s) => [s, p95(servisSure(K, s)) ?? -1]).sort((a, b) => b[1] - a[1])[0]; const gS = (s) => G.flatMap((z) => z.adimlar.filter((a) => a.servis === s).map((a) => a.sure_ms)); const gAdim = G.flatMap((z) => z.adimlar), uctanUca = G.map((z) => z.adimlar.reduce((x, a) => x + a.sure_ms, 0)); const SORU = [ ["en yavas servis", "fatura", (K) => enYavas(K)[0]], ["en yavas servisin p95 (ms)", p95(gS("fatura")), (K) => enYavas(K)[1]], ["en cok hata ureten servis", "fatura", (K) => SERVIS.map((s) => [s, (grup(K, "servis").get(s) ?? []).filter((k) => hataMi(oku(k, "durum"))).length]) .sort((a, b) => b[1] - a[1])[0][0]], ["hata orani (%)", gAdim.filter((a) => a.durum === "hata").length / gAdim.length * 100, (K) => K.filter((k) => hataMi(oku(k, "durum"))).length / K.length * 100], ["saatlik istek sayisi", ISTEK, (K) => ((g) => (g.length ? g.length : null))( (grup(K, "servis").get("gecit") ?? []).filter((k) => sayi(+oku(k, "zaman_ms")) !== null))], ["uctan uca ortalama sure (ms)", ort(uctanUca), (K) => ort(izToplam(K))], ["istek basina birlesen kayit", ort(G.map((z) => z.adimlar.length)), (K) => ort([...grup(K, "iz").values()].map((v) => v.length))], ["dogrulamasi atlanan istek", G.filter((z) => z.atlandi).length, (K) => [...grup(K, "iz").values()].filter((v) => v.every((k) => oku(k, "servis") !== "dogrulama")).length], [`${AKIS} akisinin toplam suresi (ms)`, uctanUca.reduce((a, b) => a + b, 0), (K) => (grup(K, "akis").get(AKIS) ?? []).reduce((x, k) => x + sure(k), 0) || null], ["isemri adiminin uctan uca payi (%)", ort(gS("isemri")) / ort(uctanUca) * 100, (K) => { const m = grup(K, "iz"), i = [...m.values()].flat() .filter((k) => oku(k, "servis") === "isemri" && oku(k, "iz") !== null); return i.length ? ort(i.map(sure)) / ort(izToplam(K)) * 100 : null; }], ]; const bicim = (v) => (v === null ? "-" : typeof v === "number" ? v.toFixed(v < 100 ? 2 : 0) : String(v)); const dogruMu = (g, v) => (v === null ? "yok" : typeof g === "number" ? (Math.abs(v - g) / g <= 0.05 ? "dogru" : "yanlis") : (v === g ? "dogru" : "yanlis")); console.log(`${"soru".padEnd(40)}${"gercek".padStart(9)}${"yerel sema".padStart(12)}` + `${"durum".padStart(8)}${"sozlesme".padStart(11)}${"durum".padStart(8)}`); const say = { yerel: {}, soz: {} }; for (const [ad, g, f] of SORU) { const [y, s] = [f(YEREL_K), f(SOZ_K)], [dy, ds] = [dogruMu(g, y), dogruMu(g, s)]; say.yerel[dy] = (say.yerel[dy] || 0) + 1; say.soz[ds] = (say.soz[ds] || 0) + 1; console.log(`${ad.padEnd(40)}${bicim(g).padStart(9)}${bicim(y).padStart(12)}${dy.padStart(8)}` + `${bicim(s).padStart(11)}${ds.padStart(8)}`); } const d = (o) => `dogru ${o.dogru || 0}, yanlis ${o.yanlis || 0}, yok ${o.yok || 0}`; console.log(`\nyerel sema: ${d(say.yerel)}\nsozlesme : ${d(say.soz)}`); const degisim = SERVIS.map((s) => [s, UYUMSUZ.includes(s) ? 0 : SOZLESME.filter((a) => !(a in YEREL[s](G[0], G[0].adimlar[0]))).length]); console.log(`sozlesme ${SOZLESME.length} alan; degisen alan: ${degisim.map(([s, n]) => `${s} ${n}`).join(", ")}` + `; toplam ${degisim.reduce((x, [, n]) => x + n, 0)} alan degisikligi, ` + `uyumsuz kalan ${UYUMSUZ.length}/${SERVIS.length} servis`);
soru gercek yerel sema durum sozlesme durum en yavas servis fatura okuma yanlis fatura dogru en yavas servisin p95 (ms) 68.00 31000 yanlis 68.00 dogru en cok hata ureten servis fatura dogrulama yanlis fatura dogru hata orani (%) 0.56 0.18 yanlis 0.54 dogru saatlik istek sayisi 6000 - yok 6000 dogru uctan uca ortalama sure (ms) 124 11141 yanlis 104 yanlis istek basina birlesen kayit 4.89 1.95 yanlis 3.89 yanlis dogrulamasi atlanan istek 638 6638 yanlis 638 dogru fatura_sorgu akisinin toplam suresi (ms) 744212 37943 yanlis 625249 yanlis isemri adiminin uctan uca payi (%) 15.99 - yok - yok yerel sema: dogru 0, yanlis 8, yok 2 sozlesme : dogru 6, yanlis 3, yok 1 sozlesme 6 alan; degisen alan: gecit 2, okuma 6, dogrulama 6, fatura 6, isemri 0; toplam 20 alan degisikligi, uyumsuz kalan 1/5 servis
Yerel şemalar üzerinde on sorudan hiçbiri doğru yanıtlanmadı. Asıl bulgu dağılımdadır: yalnız ikisi yanıtsız kaldı, sekizi sessizce yanlış yanıtlandı. Eşleme tablosu okuma servisinin mikrosaniye değerini milisaniye sanıp p95’i 68 yerine 31.000 buldu ve en yavaş servis olarak faturayı değil okumayı gösterdi. Hata oranı yüzde 0,56 yerine 0,18 çıktı, çünkü tablo yalnız iki biçimi tanıyor. En çok hata üreten servis sorusuna fatura yerine doğrulama yanıtı verildi. Uçtan uca süre 124 ms yerine 11.141 ms göründü. Bu yanıtların hiçbirinin yanında bir uyarı yok; sorgu çalışıyor, sayı üretiyor, gösterge tablosuna çiziliyor.
Yanıtsız kalan iki soru bu yüzden daha ucuzdur: saatlik istek sayısı hiç hesaplanamadı, çünkü geçit zamanı metin biçiminde yazıyor ve sayıya çevrilemedi. Yanıt yokluğu görülür, yanlış yanıt görülmez.
Sözleşme uygulandığında altı soru doğru yanıtlandı. Kalan dördün tamamı tek bir kaynağa çıkıyor: sözleşmeye geçirilemeyen servis. Uçtan uca süre 124 yerine 104 ms, istek başına birleşen kayıt 4,89 yerine 3,89, akış toplamı 744.212 yerine 625.249 ms — üçü de eksik bir adımın açığı. İş emri adımının uçtan uca paydaki payı ise hâlâ yanıtsız: o servisin kaydında iz kimliği yok, dolayısıyla adım hiçbir isteğe bağlanamıyor.
Sözleşmenin Bedeli
Sözleşme altı alandan oluşuyor. Dört servis bu alanlara geçti ve bu geçiş toplam 20 alan değişikliği demek: geçit iki alan ekledi, öteki üç servis altışar alanını yeniden adlandırdı ya da birimini çevirdi. Beşinci servis dışarıdan alınmış bir bileşen olduğu için değiştirilemedi ve uyumsuz kaldı.
Bu üç sayının oranı işin kararını taşıyor. Alan başına kazanç yüksek: 20 alan değişikliği karşılığı sıfır doğru yanıttan altı doğru yanıta çıkıldı, yani bir doğru yanıt ortalama üç buçuk alan değişikliğine mal oldu. Buna karşılık kazanç doğrusal değil: servislerin yüzde 80’i sözleşmeye uydu ama soruların yalnız yüzde 60’ı yanıtlandı, çünkü servisler arası soru en zayıf halkaya bağlı. Bir istek zincirinde tek bir uyumsuz adım, o adımı içeren her soruyu bozuyor.
Yanıtsız kalan sorunun bedeli burada iki katmanlıdır. Birinci katman doğrudan: iş emri adımının payı bilinmediği için o servisin yavaşlaması uçtan uca ölçümde görünmez. İkinci katman daha pahalı: sözleşme öncesi sekiz soru yanlış yanıtlanıyordu ve bu yanıtlarla karar verilmişti — en yavaş servis olarak okuma gösterildiği sürece iyileştirme çabası yanlış servise harcanır.
Özet
- Beş servis kendi adlandırmasıyla yazdığında tek bir istek 24 ayrı alan adına dağılıyor ve beş servisin hepsinde bulunan ortak alan kalmıyor.
- Yerel şemalar üzerinde on sorudan sıfırı doğru yanıtlandı; ikisi yanıtsız kaldı, sekizi sessizce yanlış yanıtlandı — yanlış yanıt yanıtsızlıktan pahalıdır, çünkü görülmez.
- Ayrılık üç katmanda birden çıkıyor: alan adı, birim (ms, µs, sn) ve değer uzayı (metin, sayı, mantıksal değer, kod); kimlik ayrılığı bunlara ek olarak birleştirmeyi tümden engelliyor.
- Altı alanlık sözleşme 20 alan değişikliği ve dört servis değişikliği karşılığında doğru yanıtı altıya çıkardı.
- Servislerin yüzde 80’i uydu ama soruların yüzde 60’ı yanıtlandı: tek uyumsuz servis kendi adımını içeren dört soruyu birden bozuyor.
Sonraki Adım
Sözleşme kurulduktan sonra sorular yanıtlanabilir hâle geldi, ama yanıtın nerede okunacağı hâlâ açık. Bir kişi olay sırasında sorgu yazmaz; önüne çıkan panellere bakar. Panellerin hangi soruya yanıt verdiği ise çoğu zaman hiç sorulmamıştır — tablolar zamanla eklenen, kimsenin silmediği panellerle dolar. Sonraki ders bir gösterge tablosunu soru kümesine karşı denetler: kaç panel bir soruya yanıt veriyor, kaç panel hiçbir soruya yanıt vermiyor, kaç soru panelsiz kalıyor ve bir soruya yanıt bulmak kaç panel gezmeyi gerektiriyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.