Ders 26 / 27
Gerçek Kullanıcı İzleme
Laboratuvar ölçümüyle alan verisinin yanıtladığı farklı sorular, alanda ölçüt toplama ve gönderme biçimi, yüzdelik dilim hesabıyla dağılımın okunması, bölütleme ve örneklem büyüklüğünün etkisi.
İçindekiler
Kararlı bir test paketi, denetim altındaki bir ortamda beklenen davranışın korunduğunu söyler. Söylemediği şey, uygulamanın gerçek kullanıcıların elinde nasıl davrandığıdır. Test makinesi hızlıdır, ağı düzgündür, önbelleği bilinen bir durumdadır. Kuzey Yamaç arayüzünü alanda kullanan gözlemci ise zayıf bir bağlantıda, yaşlı bir aygıtla ve arka planda başka sekmelerle çalışır.
Bu ders iki veri kaynağını yan yana koyar ve aradaki farkı ölçülebilir hâle getirir.
İki Kaynak, İki Soru
Laboratuvar verisi, denetimli bir koşumdan gelir: sabit aygıt profili, sabit ağ koşulları, temiz önbellek. Başarım konusundaki ölçümler bu türdendir. Değerli yanı belirlenimci olmasıdır — iki koşum arasındaki fark, koda yapılan değişikliğe bağlanabilir. Yanıtladığı soru şudur: bu değişiklik neyi bozdu?
Alan verisi, gerçek oturumlardan toplanan ölçümlerdir; toplama yöntemine gerçek kullanıcı izleme (real user monitoring) denir. Tek bir sayı değil, bir dağılım verir. Yanıtladığı soru farklıdır: kullanıcılar gerçekte ne yaşıyor?
İkisi birbirinin yerine geçmez. Laboratuvar ölçümü gerileme tespiti için, alan verisi hedef belirlemek için kullanılır.
Alanda Ne Toplanır
Tarayıcı, ölçütleri kendi başına biriktirir ve bir gözlemciyle sunar. Aşağıdaki kod boyama kayıtlarını ve eşiği aşan etkileşim kayıtlarını toplar, sayfa gizlendiğinde tek bir demet hâlinde gönderir.
// olcut-toplayici.js — alan olcutlerini toplar ve tek demet halinde gonderir const demet = { yol: location.pathname, surum: '2.4.1', olcutler: {} }; const gozlemci = new PerformanceObserver((liste) => { for (const kayit of liste.getEntries()) { demet.olcutler[kayit.name] = Math.round(kayit.startTime ?? kayit.duration); } }); gozlemci.observe({ type: 'paint', buffered: true }); gozlemci.observe({ type: 'event', buffered: true, durationThreshold: 40 }); addEventListener('visibilitychange', () => { if (document.visibilityState !== 'hidden') return; gozlemci.takeRecords(); navigator.sendBeacon('/olcut', JSON.stringify(demet)); }, { once: true });
Üç karar bu kodun içindedir. buffered seçeneği, gözlemci kurulmadan önce oluşmuş kayıtları
da getirir; ölçüm betiği geç yüklendiğinde ilk boyama kaybolmasın diye. Gönderim sayfa
kapanışında değil, sayfa gizlendiğinde yapılır: bir sekme kapanmadan arka plana atılıp
bir daha öne gelmeyebilir ve o durumda kapanış olayı hiç çalışmaz. Gönderim, sayfa
kaldırılırken de tamamlanan bir işaret isteğiyle yapılır.
Demete bağlam alanları eklenir: yol, uygulama sürümü, aygıt sınıfı, bağlantı türü, gezinme türü. Bölütleme bu alanlarla yapılır ve bir sonraki bölümde görüleceği gibi, tek bir sayının gizlediği şey burada ortaya çıkar.
Toplama her oturumda yapılmak zorunda değildir. Örnekleme, oturumların bir bölümünden veri almaktır; oranı düşürmek iletim ve saklama maliyetini düşürür. Seçim oturum başına bir kez yapılmalıdır — sayfa başına yapılırsa aynı oturumun bazı sayfaları örnekleme girer, bazıları girmez ve oturum düzeyindeki sorular yanıtsız kalır.
Dağılımı Okumak
Alan verisi bir dağılımdır ve dağılımı tek sayıya indirmenin birden çok yolu vardır. Aşağıdaki modül belirlenimci bir alan örneklemi üretir — üç aygıt sınıfı, farklı paylarla ve farklı süre aralıklarıyla — ve yüzdelik dilim hesabını verir.
// alan-verisi.mjs — alan olcumlerinin belirlenimci benzetimi ve yuzdelik hesabi export function uretec(tohum) { let s = tohum >>> 0; return () => { s = (Math.imul(s, 1664525) + 1013904223) >>> 0; return s / 4294967296; }; } export const SINIFLAR = [ { ad: 'hizli', pay: 0.60, taban: 300, yayilim: 200 }, { ad: 'orta', pay: 0.30, taban: 700, yayilim: 600 }, { ad: 'yavas', pay: 0.10, taban: 1800, yayilim: 2200 }, ]; export function alanOrneklemi(sayi, tohum = 42) { const rastgele = uretec(tohum); const ornekler = []; for (let i = 0; i < sayi; i += 1) { const secim = rastgele(); let birikim = 0; const sinif = SINIFLAR.find((s) => (birikim += s.pay) >= secim) ?? SINIFLAR.at(-1); ornekler.push({ sinif: sinif.ad, sure: Math.round(sinif.taban + sinif.yayilim * rastgele()) }); } return ornekler; } // en yakin siralama yontemi: p yuzdeliginin altinda kalan orneklerin orani en az p'dir export function yuzdelik(degerler, p) { if (degerler.length === 0) return NaN; const sirali = [...degerler].sort((a, b) => a - b); const sira = Math.ceil((p / 100) * sirali.length); return sirali[Math.min(sirali.length, Math.max(1, sira)) - 1]; } export function ortalama(degerler) { return Math.round(degerler.reduce((t, d) => t + d, 0) / degerler.length); }
// rapor.mjs — laboratuvar olcumu ile alan dagiliminin karsilastirilmasi import { alanOrneklemi, yuzdelik, ortalama, SINIFLAR } from './alan-verisi.mjs'; const alan = alanOrneklemi(5000); const sureler = alan.map((o) => o.sure); // laboratuvar kosumu: hizli aygit, kararli ag, dagilimin ortasi const hizli = SINIFLAR[0]; const laboratuvar = Math.round(hizli.taban + hizli.yayilim * 0.5); console.log(`laboratuvar olcumu ${String(laboratuvar).padStart(5)} ms`); console.log(`alan ortalamasi ${String(ortalama(sureler)).padStart(5)} ms`); for (const p of [50, 75, 90, 95]) { console.log(`alan p${String(p).padEnd(2)} ${String(yuzdelik(sureler, p)).padStart(5)} ms`); } console.log('\naygit sinifina gore p75'); for (const s of SINIFLAR) { const alt = alan.filter((o) => o.sinif === s.ad).map((o) => o.sure); console.log(` ${s.ad.padEnd(6)} ${String(alt.length).padStart(4)} oturum p75 = ${yuzdelik(alt, 75)} ms`); } console.log('\norneklem buyuklugu ve p75 kararliligi'); for (const n of [20, 100, 1000, 5000]) { const p75ler = [0, 1, 2, 3, 4].map((t) => yuzdelik(alanOrneklemi(n, 100 + t).map((o) => o.sure), 75)); console.log(` n=${String(n).padStart(4)} ${p75ler.join(' ')}`); }
node rapor.mjs
laboratuvar olcumu 400 ms alan ortalamasi 848 ms alan p50 471 ms alan p75 1014 ms alan p90 1853 ms alan p95 2971 ms aygit sinifina gore p75 hizli 2918 oturum p75 = 452 ms orta 1561 oturum p75 = 1143 ms yavas 521 oturum p75 = 3473 ms orneklem buyuklugu ve p75 kararliligi n= 20 775 930 837 1073 900 n= 100 1054 1027 967 1082 1049 n=1000 978 968 1006 1017 986 n=5000 985 1001 996 1016 992
Sayıların Söylediği
Laboratuvar ölçümü 400 milisaniye. Alandaki dağılımın ortancası 471, dörtte üçlük dilimi 1014. Yani denetimli koşum, kullanıcıların iyi yarısını temsil ediyor ve kalan yarının gerçeğini hiç göstermiyor. Bu, laboratuvar ölçümünün yanlış olduğu anlamına gelmez; kapsamının dar olduğu anlamına gelir.
Ortalama 848 milisaniye ve hiçbir kullanıcı grubunu tarif etmiyor. Dağılımın kuyruğu uzun olduğunda ortalama, azınlıktaki çok yavaş oturumlar tarafından yukarı çekilir; sonuç ne çoğunluğun deneyimidir ne de en kötü durumun ölçüsü. Süre dağılımlarında ortalama okunmaz.
Yüzdelik dilim seçimi bir denge sorunudur. Medyan kullanıcıların yarısını gizler. p95 ve üstü, tek tük uç oturumlarla — arka planda kalmış bir sekme, aşırı yüklü bir aygıt — oynamaya başlar ve gürültülüdür. p75 ikisinin arasındadır: dörtte üç oranında kullanıcının altında kaldığı değeri verir ve kuyruğa yeterince duyarlıdır. Alan ölçütleri bu yüzden yaygın olarak dörtte üçlük dilimden okunur.
Bölütleme tablosu tek sayının neyi gizlediğini gösteriyor. Aynı uygulamanın p75 değeri hızlı aygıtlarda 452, orta sınıfta 1143, yavaş sınıfta 3473 milisaniye. Genel değer olan 1014, bu üç gerçekten hiçbirine karşılık gelmiyor. İyileştirme kararı ancak bölütlerle verilebilir: yavaş sınıftaki 521 oturum, genel ortalamayı düzeltmeye çalışan her işten daha çok kazanç taşır.
Son tablo örneklem büyüklüğünün etkisidir. Yirmi oturumluk beş ayrı örneklemin p75 değerleri 775 ile 1073 arasında dolaşıyor — aynı dağılımdan gelmelerine rağmen. Bin oturumda aralık 968 ile 1017 arasına daralıyor. Küçük örneklemle ölçülen bir “iyileşme”, çoğu zaman yalnızca örneklem gürültüsüdür. Bir ölçütü bölütlere ayırmak da örneklem büyüklüğünü böler; her bölütün kendi başına yeterli oturumu olmalıdır.
Bütçeye Bağlamak
Başarım konusunda tanımlanan bütçe, alan verisiyle doğrulandığında anlam kazanır. Bütçe denetimi bir teste dönüştürülebilir.
// butce.test.mjs import { test } from 'node:test'; import assert from 'node:assert/strict'; import { alanOrneklemi, yuzdelik } from './alan-verisi.mjs'; const BUTCE = { p75: 1200, yavasSinifP75: 4000 }; test('genel p75 butcenin altinda', () => { const sureler = alanOrneklemi(5000).map((o) => o.sure); assert.ok(yuzdelik(sureler, 75) <= BUTCE.p75, `p75 = ${yuzdelik(sureler, 75)} ms`); }); test('yavas aygit sinifi kendi butcesinin altinda', () => { const yavas = alanOrneklemi(5000).filter((o) => o.sinif === 'yavas').map((o) => o.sure); assert.ok(yavas.length >= 300, 'yeterli ornek yok'); assert.ok(yuzdelik(yavas, 75) <= BUTCE.yavasSinifP75, `yavas p75 = ${yuzdelik(yavas, 75)} ms`); });
node --test butce.test.mjs
✔ genel p75 butcenin altinda (2.985458ms) ✔ yavas aygit sinifi kendi butcesinin altinda (0.718ms) ℹ tests 2 ℹ suites 0 ℹ pass 2 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 37.70275
İki testin ayrı bütçeleri olması bilinçlidir. Tek bir genel eşik, yavaş sınıfın bozulmasını hızlı sınıfın iyileşmesiyle örtebilir. Bölüt başına bütçe bu takası engeller.
Toplanan verinin kapsamı da bir karardır. Ölçüt demeti kullanıcıyı tanımlayan alan taşımamalıdır; yol bilgisi kimlik içeren parçalardan arındırılır, serbest metin hiç gönderilmez. Ölçüm kodunun kendisinin de bir maliyeti vardır — gözlemci geri çağrıları ana iş parçacığında çalışır ve ölçtüğü şeyi bir miktar yavaşlatır.
Özet
- Laboratuvar verisi belirlenimcidir ve gerileme tespitine yarar; alan verisi bir dağılımdır ve kullanıcıların gerçekte ne yaşadığını söyler.
- Ölçütler tarayıcının gözlemcisiyle toplanır ve sayfa gizlendiğinde tek demet hâlinde gönderilir; örnekleme kararı oturum başına bir kez verilir.
- Süre dağılımlarında ortalama okunmaz; ortanca yarıyı gizler, çok yüksek dilimler gürültülüdür, dörtte üçlük dilim ikisinin arasındadır.
- Laboratuvar ölçümü 400 milisaniyeyken alan p75 değeri 1014 çıktı; bölütlere ayrıldığında aynı ölçüt 452, 1143 ve 3473 milisaniye oldu.
- Küçük örneklemde p75 geniş bir aralıkta dolaşır; bölüt başına bütçe, bir sınıftaki bozulmanın bir başkasındaki iyileşmeyle örtülmesini engeller.
Sonraki Adım
Ölçütler uygulamanın ne kadar hızlı olduğunu söyler; çalışıp çalışmadığını söylemez. Bir gözlemci ölçümü kaydetmeye çalışırken bir istisna fırlarsa, o oturumun süre ölçütleri gayet iyi görünmeyi sürdürür. Kullanıcı ise boş bir ekrana bakmaktadır ve çoğu zaman kimseye haber vermez. Son ders bu sessiz başarısızlıkları görünür kılan düzeneği ele alır: istemci hatalarının yakalanması, gönderilmesi ve binlerce olayın tek tek değil, aynı kökene inen gruplar hâlinde okunması.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.