Ders 07 / 10
Kenar Konumları
Kullanıcıya yakınlığın bedelle birlikte sayılması: kenar konumu eklendikçe ortalama mesafenin ve gecikmenin düşüşü, konum başına düşen kazancın nerede durduğu, kenarın karşılayamadığı iş türlerinin payı ve kenardan dönen yanıtın günlük bayatlık sayısı.
İçindekiler
Önceki ders coğrafyayı arıza tarafından okudu: kalem nereye konursa bir kayıp neyi alır ve ayrımın bedeli kaç birimdir. Aynı coğrafyanın ikinci yüzü hiçbir arıza olmadan da vardır. Bir abone bölgeden ne kadar uzaksa her isteği o kadar uzun yol gider; bu yol her gün, her istekte ödenir ve hiçbir sağlık denetimine yakalanmaz.
Kenar konumu, kaynak bölgeden bağımsız, kullanıcıya yakın duran ve isteklerin bir bölümünü oradan karşılayan bir yerleşim noktasıdır. Kiralanan şey burada bir makine değil bir konum kümesidir: sağlayıcının işlettiği noktalardan hangilerinin kullanılacağı seçilir, o noktalarda ne çalışacağı seçilmez. Bu ders yakınlığın karşılığını ve karşılamadığı işi birlikte sayar.
Konum Modeli
Aşağıdaki düzenek bir modeldir. Kurgu bölgesel ölçüm ağının aboneleri bir kurgu düzlemde dağıtılır; gerçek bir şehir, ülke, bölge ya da sağlayıcı yoktur.
- BC7. Hizmet coğrafyası 1000×600 kurgu birimlik bir dikdörtgendir; bölge (150, 500) noktasındadır.
- BC8. 40 abone kümesi beş yoğunluk odağı çevresinde üretilir. Üreteç kendi yazılmıştır ve tohum 20260802 olarak görünür; her koşum aynı sayıları verir.
- BC9. Gecikme mesafeye doğrusaldır: 2,0 ms taban + birim başına 0,075 ms (kurgu).
- BC10. Bir kenar konumunun kirası 3,5 kurgu
birim/saat; bölgenin kirası bu dersin dışındadır. - BC11. Günlük istek 2.400.000 ve yedi türün payları sabittir.
- BC12. Yerleşik kenar önbelleğinde isabet oranı 0,93; taşınma sonrası soğuk önbellekte ilk gün 0,55.
// kenar/model.mjs — kullanici dagilimi ve konum kumesi MODELIDIR. Kurgu hizmet cografyasi // 1000x600 birimlik bir dikdortgendir; gercek sehir, bolge ya da saglayici yoktur. export function uretec(tohum) { // kendi yazilan dogrusal eslemeli uretec let s = tohum >>> 0; return () => ((s = (Math.imul(s, 1664525) + 1013904223) >>> 0) / 4294967296); } export const TOHUM = 20260802; // gorunur tohum; her kosumda ayni sayilar export const BOLGE = { ad: "bolge", x: 150, y: 500 }; export const TABAN_MS = 2.0, MS_BIRIM = 0.075; // kurgu: gecikme = taban + mesafe * katsayi export const KONUM_KIRA = 3.5; // kurgu birim/saat, kenar konumu basina export function abonatlar() { // 5 yogunluk odagi, odak basina 8 kume const r = uretec(TOHUM); const odak = Array.from({ length: 5 }, () => [r() * 1000, r() * 600]); const k = []; for (const [ox, oy] of odak) for (let i = 0; i < 8; i += 1) k.push({ x: Math.min(1000, Math.max(0, ox + (r() + r() - 1) * 130)), y: Math.min(600, Math.max(0, oy + (r() + r() - 1) * 90)), agirlik: 1 + Math.floor(r() * 9), // istek payi, 1..9 }); return k; } export function adaylar() { // saglayicinin sundugu kenar konumu kumesi const r = uretec(TOHUM ^ 0x5f5f); return Array.from({ length: 14 }, (_, i) => ({ ad: `k${i + 1}`, x: r() * 1000, y: r() * 600 })); } export const mesafe = (a, b) => Math.hypot(a.x - b.x, a.y - b.y); export const gecikme = (d) => TABAN_MS + MS_BIRIM * d; export function ortalama(kumeler, konumlar) { // her kume en yakin konumdan karsilanir let ag = 0, top = 0; for (const c of kumeler) { top += c.agirlik * Math.min(...konumlar.map((k) => mesafe(c, k))); ag += c.agirlik; } return top / ag; } // istek karmasi: kenardan donebilen turler ve donemeyenler (yazma, tutarlilik isteyen okuma) export const KARMA = [ ["statik-kabuk", 0.28, "kenar", 3600, 0.2], ["tarife-tablosu", 0.10, "kenar", 900, 2], ["abone-ozeti", 0.08, "kenar", 60, 96], ["son-okuma", 0.26, "tutarli-okuma", 0, 0], ["okuma-yukleme", 0.18, "yazma", 0, 0], ["is-emri-acma", 0.06, "yazma", 0, 0], ["fatura-kesme", 0.04, "yazma", 0, 0], ]; export const GUNLUK = 2_400_000, GUN_SN = 86_400; export const ISABET = 0.93, SOGUK_ISABET = 0.55;
Konum Eklendikçe
Konumlar açgözlü eklenir: her adımda ortalama ağırlıklı mesafeyi en çok düşüren aday seçilir. Her adımda kazanç ve kira birlikte yazılır.
// kenar/yerlesim.mjs — konum eklendikce ortalama mesafe, gecikme ve konum basina bedel birlikte import { BOLGE, abonatlar, adaylar, ortalama, gecikme, KONUM_KIRA, TOHUM } from "./model.mjs"; const s = (x, n) => String(x).padStart(n); const y = (x, n = 2) => x.toFixed(n); const kume = abonatlar(), aday = adaylar(); const secili = [BOLGE], kalan = [...aday]; const agirlik = kume.reduce((a, c) => a + c.agirlik, 0); console.log(`tohum ${TOHUM}: ${kume.length} abone kumesi, toplam agirlik ${agirlik}, ` + `${aday.length} aday kenar konumu (kurgu cografya).`); console.log(); console.log("konum | eklenen | ort. mesafe | ort. gecikme | kazanc ms | kira | birim basina ms | toplam kira"); console.log("------|---------|-------------|--------------|-----------|------|-----------------|------------"); let oncekiG = gecikme(ortalama(kume, secili)); console.log(`${s(0, 5)} | ${"-".padEnd(7)} | ${s(y(ortalama(kume, secili)), 11)} | ` + `${s(y(oncekiG), 12)} | ${s("-", 9)} | ${s("-", 4)} | ${s("-", 15)} | ${s(y(0), 11)}`); for (let n = 1; n <= 8; n += 1) { let enIyi = null; for (const a of kalan) { const d = ortalama(kume, [...secili, a]); if (enIyi === null || d < enIyi.d) enIyi = { a, d }; } secili.push(enIyi.a); kalan.splice(kalan.indexOf(enIyi.a), 1); const g = gecikme(enIyi.d), kazanc = oncekiG - g; console.log(`${s(n, 5)} | ${enIyi.a.ad.padEnd(7)} | ${s(y(enIyi.d), 11)} | ${s(y(g), 12)} | ` + `${s(y(kazanc), 9)} | ${s(y(KONUM_KIRA), 4)} | ${s(y(kazanc / KONUM_KIRA), 15)} | ` + `${s(y(n * KONUM_KIRA), 11)}`); oncekiG = g; }
tohum 20260802: 40 abone kumesi, toplam agirlik 210, 14 aday kenar konumu (kurgu cografya).
konum | eklenen | ort. mesafe | ort. gecikme | kazanc ms | kira | birim basina ms | toplam kira
------|---------|-------------|--------------|-----------|------|-----------------|------------
0 | - | 338.60 | 27.39 | - | - | - | 0.00
1 | k2 | 215.03 | 18.13 | 9.27 | 3.50 | 2.65 | 3.50
2 | k12 | 172.38 | 14.93 | 3.20 | 3.50 | 0.91 | 7.00
3 | k13 | 146.26 | 12.97 | 1.96 | 3.50 | 0.56 | 10.50
4 | k3 | 137.53 | 12.31 | 0.65 | 3.50 | 0.19 | 14.00
5 | k7 | 134.13 | 12.06 | 0.26 | 3.50 | 0.07 | 17.50
6 | k14 | 131.84 | 11.89 | 0.17 | 3.50 | 0.05 | 21.00
7 | k1 | 130.96 | 11.82 | 0.07 | 3.50 | 0.02 | 24.50
8 | k9 | 130.41 | 11.78 | 0.04 | 3.50 | 0.01 | 28.00
İlk konum tek başına ortalama gecikmeyi 27,39 ms’den 18,13 ms’ye indiriyor: 3,5 birimlik kira karşılığında 9,27 ms, yani birim başına 2,65 ms. İkinci konum aynı kirayla 3,20 ms, üçüncüsü 1,96 ms getiriyor. Dördüncüde birim başına kazanç 0,19 ms’ye düşüyor — üçüncünün on dörtte biri. Azalan getiri noktası üçüncü konumdur.
Sınırın sertliği son satırlarda görünüyor. Üç konum 10,50 birim kirayla gecikmeyi 12,97 ms’ye indiriyor; sekiz konum 28,00 birim kirayla 11,78 ms’ye. Fazladan beş konum ve 17,50 birim, toplam 1,19 ms kazandırıyor. Eğri düzleşiyor çünkü konum sayısı arttıkça yeni aday, çoğunluğu zaten karşılanmış bir kümenin artıklarına yaklaşıyor; kullanıcı dağılımı odaklıysa ilk üç odak alındıktan sonra alınacak yoğunluk kalmıyor.
Kenarın Yapamadığı İş
Yukarıdaki tablo bütün isteklerin kenardan karşılandığını varsayıyor ve bu varsayım yanlıştır. Kenar konumu bir kopyadır; yazma işlemini kabul edemez, çünkü kabul ettiği anda kayıt kaynakla ayrışır. Tutarlılık isteyen okuma da dönemez: bir abonenin az önce yüklediği okumayı sorması, kaynağın en son durumunu gerektirir.
// kenar/karma.mjs — kenarin yapamadigi is, karma gercek gecikme ve kenardan donen yanitin bayatligi import { BOLGE, abonatlar, adaylar, ortalama, gecikme, KARMA, GUNLUK, GUN_SN, ISABET, SOGUK_ISABET, KONUM_KIRA } from "./model.mjs"; const s = (x, n) => String(x).padStart(n); const y = (x, n = 2) => x.toFixed(n); const kume = abonatlar(), aday = adaylar(); const secili = [BOLGE]; // acgozlu secimin ilk uc konumu yeniden kurulur for (let n = 0; n < 3; n += 1) { let e = null; for (const a of aday) if (!secili.includes(a)) { const d = ortalama(kume, [...secili, a]); if (e === null || d < e.d) e = { a, d }; } secili.push(e.a); } const KENAR_MS = gecikme(ortalama(kume, secili)), BOLGE_MS = gecikme(ortalama(kume, [BOLGE])); console.log("tur | pay | kenardan doner | TTL (sn) | gunluk degisim | bayat oran | gunluk bayat"); console.log("---------------|------|----------------|----------|----------------|------------|-------------"); let bayatTop = 0, kenarPay = 0; for (const [ad, pay, sinif, ttl, degisim] of KARMA) { const oran = sinif === "kenar" ? (degisim * (ttl / 2)) / GUN_SN : 0; const bayat = Math.round(pay * GUNLUK * ISABET * oran); bayatTop += bayat; kenarPay += sinif === "kenar" ? pay : 0; console.log(`${ad.padEnd(14)} | ${s(y(pay), 4)} | ${s(sinif === "kenar" ? "evet" : sinif, 14)} | ` + `${s(ttl || "-", 8)} | ${s(degisim || "-", 14)} | ${s(oran ? `%${y(100 * oran)}` : "-", 10)} | ` + `${s(bayat || "-", 12)}`); } const kacirma = KENAR_MS + BOLGE_MS; const kenarKarma = ISABET * KENAR_MS + (1 - ISABET) * kacirma; const karma = kenarPay * kenarKarma + (1 - kenarPay) * BOLGE_MS; console.log(); console.log(`kenardan donebilen pay ${y(kenarPay)}; donemeyen ${y(1 - kenarPay)} (yazma + tutarli okuma).`); console.log(`gecikme: bolge ${y(BOLGE_MS)} ms, uc konumlu kenar ${y(KENAR_MS)} ms, ` + `kacirmada ${y(kacirma)} ms, isabet ${ISABET} ile kenar ortalamasi ${y(kenarKarma)} ms.`); console.log(`karma gercek gecikme ${y(karma)} ms — dusus ${y(BOLGE_MS - karma)} ms ` + `(%${y((100 * (BOLGE_MS - karma)) / BOLGE_MS, 1)}), oysa yalniz kenar sayilsaydi ` + `%${y((100 * (BOLGE_MS - KENAR_MS)) / BOLGE_MS, 1)} gorunurdu.`); console.log(`gunluk bayat yanit ${bayatTop.toLocaleString("tr-TR")} / ${GUNLUK.toLocaleString("tr-TR")} ` + `(%${y((100 * bayatTop) / GUNLUK, 2)}); en uzun bayatlik ${Math.max(...KARMA.map((k) => k[3]))} sn.`); const yerlesik = GUNLUK * (1 - kenarPay + kenarPay * (1 - ISABET)); const soguk = GUNLUK * (1 - kenarPay + kenarPay * (1 - SOGUK_ISABET)); console.log(`kaynaga giden istek: yerlesik onbellekle ${Math.round(yerlesik).toLocaleString("tr-TR")}, ` + `tasinma sonrasi soguk onbellekle ${Math.round(soguk).toLocaleString("tr-TR")} ` + `(+%${y((100 * (soguk - yerlesik)) / yerlesik, 1)}); uc konumun kirasi ${y(3 * KONUM_KIRA)} birim.`);
tur | pay | kenardan doner | TTL (sn) | gunluk degisim | bayat oran | gunluk bayat ---------------|------|----------------|----------|----------------|------------|------------- statik-kabuk | 0.28 | evet | 3600 | 0.2 | %0.42 | 2604 tarife-tablosu | 0.10 | evet | 900 | 2 | %1.04 | 2325 abone-ozeti | 0.08 | evet | 60 | 96 | %3.33 | 5952 son-okuma | 0.26 | tutarli-okuma | - | - | - | - okuma-yukleme | 0.18 | yazma | - | - | - | - is-emri-acma | 0.06 | yazma | - | - | - | - fatura-kesme | 0.04 | yazma | - | - | - | - kenardan donebilen pay 0.46; donemeyen 0.54 (yazma + tutarli okuma). gecikme: bolge 27.39 ms, uc konumlu kenar 12.97 ms, kacirmada 40.36 ms, isabet 0.93 ile kenar ortalamasi 14.89 ms. karma gercek gecikme 21.64 ms — dusus 5.75 ms (%21.0), oysa yalniz kenar sayilsaydi %52.7 gorunurdu. gunluk bayat yanit 10.881 / 2.400.000 (%0.45); en uzun bayatlik 3600 sn. kaynaga giden istek: yerlesik onbellekle 1.373.280, tasinma sonrasi soguk onbellekle 1.792.800 (+%30.5); uc konumun kirasi 10.50 birim.
Kenardan dönebilen pay 0,46; kalan 0,54 yazma ile tutarlılık isteyen okumadır ve konum sayısı kaç olursa olsun bölgeye gider. Bu yüzden karma gerçek gecikme 21,64 ms’de kalıyor ve düşüş yüzde 21,0 oluyor — yalnız kenar sütununa bakan bir ölçüm ise yüzde 52,7 raporlardı. İki sayı arasındaki fark bir ölçüm hatası değil, hangi işin kenardan dönebildiği sorusunun cevabıdır.
Kaçırma satırı ikinci bir uyarı taşıyor. Önbellekte bulunmayan bir istek kenara gidip oradan kaynağa geçtiği için 40,36 ms’ye çıkıyor, yani bölgeye doğrudan gitmekten daha uzun. Kenar konumu her isteği hızlandırmaz; isabet ettiklerini hızlandırır, kaçırdıklarını yavaşlatır.
Bayatlık ve Çıkışta Taşınan
Kenardan dönen bir yanıt tanımı gereği bir kopyadır ve kopyanın yaşı vardır. Ölçü, önbellek ömrünün ve verinin değişim sıklığının çarpımıdır: abone özeti 60 saniyelik ömürle günde 96 kez değişiyor ve isteklerin yüzde 3,33’ü bayat dönüyor; tarife tablosu 900 saniye ve günde 2 değişimle yüzde 1,04; statik kabuk 3600 saniye ama günde 0,2 değişimle yüzde 0,42. Sıralama önbellek ömrünün değil, ömür ile değişim hızının çarpımının sıralamasıdır.
Toplamda günde 10.881 bayat yanıt dönüyor. Bütün isteklerin yüzde 0,45’i olan bu sayı, kabul edilip edilmeyeceği kararının verilebilmesi için önce yazılması gereken sayıdır; en uzun bayatlık 3600 saniyedir ve bu, tarifesi az önce değişmiş bir abonenin bir saate kadar eski tarifeyi görebileceği anlamına gelir.
Devredilen karar burada üç kalemdir: hangi konumların işletildiği, bir isteğin hangi konuma yönlendirildiği ve bir girdinin önbellekten ne zaman çıkarıldığı. Karşılığında alınan, 10,50 birimlik kirayla 5,75 ms’lik karma düşüş ve kaynağa giden isteğin 2.400.000’den 1.373.280’e inmesidir. Çıkışta taşınan ise yalnız kurallardır: üç tür için önbellek ömrü, anahtar ve geçersizleştirme tanımı yazılıp aynen kurulabilir. Taşınamayan iki şey var — sağlayıcının konum kümesi ve ısınmış içerik. Soğuk önbellekle ilk gün kaynağa giden istek 1.792.800’e, yani yüzde 30,5 yukarı çıkıyor; taşınmanın bedeli bir kereliğine bu farktır.
Özet
- Kenar konumu bir konum kümesi kiralamaktır: hangi noktaların kullanılacağı seçilir, o noktalarda ne çalışacağı seçilmez.
- Ortalama gecikme ilk konumda 27,39 ms’den 18,13 ms’ye iner; birim başına kazanç 2,65 ms’den üçüncü konumda 0,56 ms’ye, dördüncüde 0,19 ms’ye düşer — azalan getiri noktası üçüncü konumdur.
- Üç konum 10,50 birimle 12,97 ms verirken sekiz konum 28,00 birimle 11,78 ms verir: fazladan 17,50 birim toplam 1,19 ms kazandırır.
- Kenardan dönemeyen pay 0,54’tür (yazma ve tutarlılık isteyen okuma); karma gerçek gecikme düşüşü yüzde 21,0 iken yalnız kenar sayılsaydı yüzde 52,7 görünürdü. Kaçırılan istek 40,36 ms ile bölgeye doğrudan gitmekten yavaştır.
- Bayatlık ömür ile değişim hızının çarpımıdır: abone özeti yüzde 3,33, tarife tablosu yüzde 1,04, statik kabuk yüzde 0,42; günde 10.881 bayat yanıt ve en uzun bayatlık 3600 saniye.
- Çıkışta önbellek kuralları taşınır, konum kümesi ve ısınmış içerik taşınmaz: soğuk başlangıçta kaynağa giden istek yüzde 30,5 artar.
Sonraki Adım
İki ders iki ekseni ayrı ayrı ölçtü ve ikisi birbirini çekiştiriyor. Üç alana yayılmak dayanıklılığı yükseltti, eşzamanlı yazmayı yavaşlattı. Üç kenar konumu gecikmeyi düşürdü, günde 10.881 bayat yanıt ve 10,50 birimlik kira üretti. Her kararın bir kazanç ve en az bir kayıp sütunu var, ama bu sütunlar şimdiye kadar ders ders ve dağınık sayıldı. Kararları tek bir tabloda tutan, her birini aynı beş ölçüte göre puanlayan ve hangi ölçütün hangisiyle çeliştiğini görünür kılan bir düzen var. Sonraki ders o beş sütunu tür olarak tanımlar ve bir karar kümesini üzerinde puanlayarak sütunlar arası gerilimi sayar: bir sütunu yükselten kaç karar başka bir sütunu düşürüyor ve hiçbir kararın iyileştirmediği sütun hangisi.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.