---
title: 'Başarım Karşıt Kalıpları ve İzleme'
source: 'https://academia.sh/tr/kurslar/basarim-ve-izleme'
language: tr
updated: '2026-08-17T04:43:06+00:00'
license: 'CC BY-SA 4.0'
lessons: 16
outcomes:
    - 'Yaygın başarım karşıt kalıplarını belirtilerinden tanır'
    - 'İzleme kategorilerini eksiksiz kurar'
    - 'Yük testi sonuçlarını kapasite kararına çevirir'
---

# Başarım Karşıt Kalıpları ve İzleme

Arızanın fark edilmesi ve yavaşlığın adının konması: on yaygın başarım karşıt kalıbının belirtiden nedene giden bir tanı yolu olarak kurulması, her kalıbın ölçüyle tanınması ve düzeltmenin karşılığında ne büyüttüğünün sayılması, izleme kategorilerinin eksiksiz kurulması, neyin ölçülmeye değer olduğunun enstrümantasyon kararına çevrilmesi, yük testi sonuçlarının kapasite tahminine ve tasarım kararlarının işletme maliyetine bağlanması.

## Karşıt Kalıplar

Belirtiden nedene giden on tanı: hesabın yanlış katmanda yapılması, ana iş parçacığına yığılan ağır iş, çok sayıda küçük çağrı, ihtiyaçtan fazlasının okunması, pahalı istemcilerin yeniden yaratılması, bütün verinin tek depoya yığılması, yinelenen pahalı hesabın önbelleksiz bırakılması, paylaşılan kaynakta izolasyon eksikliği, engelleyen çağrıların kapasiteye etkisi ve arızayı büyüten yeniden deneme döngüsü.

### Meşgul Veritabanı

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/mesgul-veritabani

Aynı belirtiyi veren iki karşıt nedenin bir ölçümle ayrılması: depo katmanı doluyken uygulama katmanının boşta kalması, azaltmayan işin depoda bırakılması ile azaltan işin uygulamaya taşınmasının aynı belirtiyi vermesi, sınır oranının düzeltmenin yönünü seçmesi ve tek bir sayının hangi durumda yanlış planı önerdiği.

Önceki kurs yirmi kalıbı iki ayrı günün sayısıyla savundu, ama hepsi aynı varsayımın üstünde
durdu: arıza **fark edilmiş** olacak. Devre kesici bir eşiğin aşıldığını, devralma bir kopyanın
düştüğünü bilecekti. Farkın nasıl edileceği tasarlanmadı. Bu kurs oradan başlar ve arızadan
değil, arızasız günün kendisinden: bir sistem hiç bozulmadan da kötüleşir, kötüleşmenin adı
konmadan da düzeltilemez.

Bu dersin ve ardından gelen dokuzun yürüttüğü yol üç adımdır. **Belirti** bir sayıdır; "yavaş"
bir belirti değildir. **Ayırt etme**, aynı belirtiyi veren en az iki nedeni birbirinden ayıran
bir ölçümdür; ölçüm yoksa yapılan şey tahmindir ve bu kursta tahmin diye adlandırılır.
**Düzeltme**, kalıbı giderir ve karşılığında neyin büyüdüğünü sayar. Üçü tamamlanmadan bir
**karşıt kalıp** (antipattern) tanınmış olmaz. Ve hiçbir karşıt kalıp her ölçekte yanlış
değildir; her ders aynı biçimin doğru olduğu koşulu bir sayıyla sınırlar.

#### Belirti: Bir Katman Dolu, Öteki Boş

Belirti gün sonu ücretlendirme akışında görünüyor. K01'in `kabaca büyüklük hesabı` bu işi
otuz günün 12.000.000 kaydını dört saatlik pencerede taramak olarak yazmıştı: saniyede 833,33
kayıt. İş pencereye sığmıyor. Aynı anda iki **doluluk** okuması var ve ikisi ters yönde: depo
katmanının işlemci doluluğu tepe yapıyor, uygulama düğümlerinin doluluğu düşük kalıyor.
Doluluk M19/K01 `temel-nitelikler/01`'de tanımlandı, burada yeniden tanımlanmaz.

İlk refleks bir tahmindir ve tahmin olduğu için adı konur: "depo yetmiyor, daha büyük makine
gerekiyor." Bu cümlenin tehlikesi yanlış olması değil, **yönsüz** olmasıdır. Aşağıdaki iki neden
aynı belirtiyi verir ve düzeltmeleri birbirinin tersidir; yön seçilmeden yapılan her müdahalenin
yarısı belirtiyi kötüleştirir.

**Neden A — azaltmayan iş depoda.** Depo, sonuç kayıt sayısını hiç değiştirmeyen bir iş yapıyor:
satır başına dize üretimi, biçimlendirme, sunum için kod çözümü. Girdi kaydı ile çıktı kaydı
eşit; harcanan işlemci işi sınırdan geçen veriyi bir bayt bile azaltmıyor.

**Neden B — azaltan iş uygulamada.** Uygulama ham kayıtları çekip toplamayı kendi yapıyor. Depo
satır başına hiç hesap yapmıyor, ama on iki milyon kaydı okuyup seri hâle getirip göndermek
zorunda. Depo yine dolu, işin cinsi başka.

**Meşgul veritabanı** (busy database) bu iki nedenin ortak adıdır: hesabın yanlış katmanda
yapılması. Adın tek başına bir işe yaramadığı buradan görünüyor — ad iki nedeni de kapsıyor,
düzeltme ise nedeni bilmeyi gerektiriyor.

#### Dört Plan, Tek Düzenek

Ayırt etme için aynı gün sonu işi dört planla koşuluyor. İki istek var: satıcının **kalem
dökümü** (her gönderi bir satır) ve **fatura satırı** (satıcı-gün başına bir satır, K01'in
V13 = 100 kalem oranı). Her istek iki katman seçimiyle yazılıyor.

Ölçüm gerçek bir motorda yapılıyor ve model ölçeği M19/K04'ün ölçeğidir: 20 satıcı, 30 gün,
günde 100 gönderi. Sayılan üç şey var — sınırdan geçen kayıt, iki katmanda yapılan **satır işi**
(satır başına çalıştırılan hesap sayısı, motora kayıtlı bir sayaç işleviyle sayılıyor) ve sınırdan
geçen bayt. Süre ölçülmüyor: süre bu makineye bağlıdır, sayılan üç nicelik değildir.

```js
// katman/plan.mjs — ayni gun sonu isinin dort plani: sinirdan gecen kayit, sinir orani,
// iki katmanda yapilan satir isi ve tasinan bayt. node:sqlite ile gercek bir motor kosar.
import { DatabaseSync } from "node:sqlite";

const SATICI = 20, GUN = 30, GUNLUK = 100, TARIFE = 40, BOLGE = 12;   // M19/K04 model olcegi
const N = SATICI * GUN * GUNLUK, FATURA = SATICI * GUN;               // 60000 gonderi, 600 fatura satiri
const db = new DatabaseSync(":memory:");
db.exec(`CREATE TABLE gonderi(no TEXT PRIMARY KEY, satici INT, gun INT, durum INT,
  agirlik REAL, oran REAL, katsayi REAL, indirim REAL);`);
db.exec("BEGIN");
const ekle = db.prepare("INSERT INTO gonderi VALUES(?,?,?,?,?,?,?,?)");
for (let s = 0; s < SATICI; s += 1) for (let d = 0; d < GUN; d += 1) for (let k = 0; k < GUNLUK; k += 1) {
  const ti = (s * 7 + d + k) % TARIFE, bi = (s + k) % BOLGE;
  ekle.run(`TR-${s}-${d}-${k}`, s, d, k % 5, 1 + (k % 30),
    12 + (ti % 7) * 0.5, 1 + (bi % 5) * 0.1, (s % 9) * 0.01);
}
db.exec("COMMIT");

const TUTAR = "agirlik * oran * katsayi * (1 - indirim)";
const DURUM = "CASE durum WHEN 0 THEN 'kabul' WHEN 1 THEN 'aktarma' WHEN 2 THEN 'dagitimda' " +
  "WHEN 3 THEN 'teslim' ELSE 'bekliyor' END";
const AD = ["kabul", "aktarma", "dagitimda", "teslim", "bekliyor"];
const SQL = (i) => ({                                    // i: satir isini sayan sarmalayici
  D1: `SELECT no, ${i(`printf('%s / %02d. gun', no, gun)`)} AS baslik,
    ${i(`printf('%.2f', ${i(TUTAR)})`)} AS tutar, ${i(`printf('%.1f kg', agirlik)`)} AS agirlik,
    ${i(DURUM)} AS durum FROM gonderi`,
  D2: `SELECT no, gun, ${i(TUTAR)} AS tutar, agirlik, durum FROM gonderi`,
  F1: `SELECT satici, gun, count(*) AS kalem, sum(${i(TUTAR)}) AS tutar FROM gonderi GROUP BY satici, gun`,
  F2: `SELECT satici, gun, agirlik, oran, katsayi, indirim FROM gonderi`,
});
const UYGULAMA = {                                       // uygulama katmaninda yapilan satir isi
  D1: (r) => 0,
  D2: (r) => { let c = 0; for (const x of r) { c += 4;
    void [`${x.no} / ${String(x.gun).padStart(2, "0")}. gun`, x.tutar.toFixed(2),
      `${x.agirlik.toFixed(1)} kg`, AD[x.durum]]; } return c; },
  F1: (r) => { let c = 0; for (const x of r) { c += 2; void [x.tutar.toFixed(2), `${x.kalem} kalem`]; }
    return c; },
  F2: (r) => { const t = new Map(); let c = 0;
    for (const x of r) { c += 1; const k = `${x.satici}/${x.gun}`;
      const v = t.get(k) ?? { kalem: 0, tutar: 0 }; v.kalem += 1;
      v.tutar += x.agirlik * x.oran * x.katsayi * (1 - x.indirim); t.set(k, v); }
    for (const v of t.values()) { c += 2; void [v.tutar.toFixed(2), `${v.kalem} kalem`]; } return c; },
};
const GEREKLI = { D1: N, D2: N, F1: FATURA, F2: FATURA };   // istegin sonucundaki kayit sayisi

let sayac = 0;
db.function("i", (x) => { sayac += 1; return x; });
const olcu = {};
for (const [ad, sorgu] of Object.entries(SQL((e) => `i(${e})`))) {
  sayac = 0;
  const satir = db.prepare(sorgu).all();
  const depo = sayac;
  const bayt = Buffer.byteLength(JSON.stringify(satir.map((r) => Object.values(r))));
  olcu[ad] = { gecen: satir.length, gerekli: GEREKLI[ad], oran: satir.length / GEREKLI[ad],
    depo, uygulama: UYGULAMA[ad](satir), bayt };
}

const PLAN = ["D1", "D2", "F1", "F2"];
const ETIKET = { D1: "D1 dokum, bicim depoda", D2: "D2 dokum, bicim uygulamada",
  F1: "F1 fatura, toplama depoda", F2: "F2 fatura, toplama uygulamada" };
const say = (x) => x.toLocaleString("tr-TR");
console.log(`model: ${say(N)} gonderi, ${SATICI} satici, ${GUN} gun; dokum istegi ${say(N)} satir, ` +
  `fatura istegi ${FATURA} satir dondurur; taranan kayit dort planda da ${say(N)}`);
console.log(`\n${"plan".padEnd(28)}${"gecen kayit".padStart(13)}${"gerekli".padStart(10)}` +
  `${"sinir orani".padStart(13)}${"depoda satir isi".padStart(18)}${"uygulamada".padStart(12)}` +
  `${"gecen bayt".padStart(12)}${"kayit basina".padStart(14)}`);
for (const p of PLAN) {
  const o = olcu[p];
  console.log(ETIKET[p].padEnd(28) + say(o.gecen).padStart(13) + say(o.gerekli).padStart(10) +
    o.oran.toFixed(2).padStart(13) + say(o.depo).padStart(18) + say(o.uygulama).padStart(12) +
    say(o.bayt).padStart(12) + (o.bayt / o.gecen).toFixed(1).padStart(14));
}

const OLCEK = 12_000_000 / N, PENCERE = 4 * 3600;        // K01: 30 gunun 12.000.000 kaydi, 4 saat
console.log(`\nK01 olceginde (olcek ${OLCEK}, pencere ${PENCERE} s, tarama ` +
  `${(12e6 / PENCERE).toFixed(2)} kayit/s); sinir bandi F2 = K01'in 6 Mbit/s'i olarak olceklendi`);
console.log(`${"plan".padEnd(28)}${"depoda satir isi/s".padStart(20)}${"uygulamada/s".padStart(14)}` +
  `${"sinir bandi Mbit/s".padStart(20)}`);
for (const p of PLAN) {
  const o = olcu[p];
  console.log(ETIKET[p].padEnd(28) + ((o.depo * OLCEK) / PENCERE).toFixed(2).padStart(20) +
    ((o.uygulama * OLCEK) / PENCERE).toFixed(2).padStart(14) +
    ((6 * o.bayt) / olcu.F2.bayt).toFixed(3).padStart(20));
}

console.log(`\nKK1 (depoda bir satir isi uygulamadakinin k katina mal olur) ile plan cifti secimi`);
console.log(`${"plan cifti".padEnd(14)}${"k=2".padStart(12)}${"k=6".padStart(12)}${"k=12".padStart(12)}` +
  `${"gecen kayit".padStart(14)}${"sinir bandi Mbit/s".padStart(20)}`);
for (const [a, b] of [["D1", "F1"], ["D2", "F1"], ["D1", "F2"], ["D2", "F2"]]) {
  const depo = olcu[a].depo + olcu[b].depo, uyg = olcu[a].uygulama + olcu[b].uygulama;
  const bayt = olcu[a].bayt + olcu[b].bayt;
  console.log(`${a} + ${b}`.padEnd(14) + [2, 6, 12].map((k) => say(depo * k + uyg).padStart(12)).join("") +
    say(olcu[a].gecen + olcu[b].gecen).padStart(14) + ((6 * bayt) / olcu.F2.bayt).toFixed(3).padStart(20));
}
```

```
model: 60.000 gonderi, 20 satici, 30 gun; dokum istegi 60.000 satir, fatura istegi 600 satir dondurur; taranan kayit dort planda da 60.000

plan                          gecen kayit   gerekli  sinir orani  depoda satir isi  uygulamada  gecen bayt  kayit basina
D1 dokum, bicim depoda             60.000    60.000         1.00           300.000           0   3.991.401          66.5
D2 dokum, bicim uygulamada         60.000    60.000         1.00            60.000     240.000   1.988.049          33.1
F1 fatura, toplama depoda             600       600         1.00            60.000       1.200      13.407          22.3
F2 fatura, toplama uygulamada       60.000       600       100.00                 0      61.200   1.302.519          21.7

K01 olceginde (olcek 200, pencere 14400 s, tarama 833.33 kayit/s); sinir bandi F2 = K01'in 6 Mbit/s'i olarak olceklendi
plan                          depoda satir isi/s  uygulamada/s  sinir bandi Mbit/s
D1 dokum, bicim depoda                   4166.67          0.00              18.386
D2 dokum, bicim uygulamada                833.33       3333.33               9.158
F1 fatura, toplama depoda                 833.33         16.67               0.062
F2 fatura, toplama uygulamada                0.00        850.00               6.000

KK1 (depoda bir satir isi uygulamadakinin k katina mal olur) ile plan cifti secimi
plan cifti             k=2         k=6        k=12   gecen kayit  sinir bandi Mbit/s
D1 + F1            721.200   2.161.200   4.321.200        60.600              18.448
D2 + F1            481.200     961.200   1.681.200        60.600               9.220
D1 + F2            661.200   1.861.200   3.661.200       120.000              24.386
D2 + F2            421.200     661.200   1.021.200       120.000              15.158
```

Tablonun sayıları **ölçüm** sınıfındadır; K01 ölçeğine çevrilmiş satırlar **hesap** sınıfındadır.
Sınır bandı sütununda mutlak bir bayt iddiası yoktur: modelin ham satırı K01'in 900 baytlık
gönderi kaydından dardır, bu yüzden band F2 planı K01'in yazdığı 6 Mbit/s'e eşitlenerek
ölçeklendi. Taşınabilir olan planların birbirine **oranıdır**.

#### Sınır Oranı Yönü Seçiyor

Üçüncü sütun tanının kendisidir. **Sınır oranı**, bir isteğin sınırdan geçirdiği kayıt sayısının
o isteğin sonucunda gerçekten bulunan kayıt sayısına bölümüdür. Dört planın üçünde 1,00; F2'de
100,00.

Oran 1,00 olduğunda depo zaten azaltmıştır ve depo yine de doluysa suçlu **azaltmayan iştir**.
D1 bunu tam olarak gösteriyor: sınır oranı 1,00 ama depoda satır işi 300.000, yani kayıt başına
beş. D2 aynı sonucu üretiyor ve depodaki iş 60.000'e, kayıt başına bire iniyor. Fark biçimlendirme
işidir ve o iş sınırdan geçen kaydı bir tane bile azaltmıyor — üstelik **artırıyor**: geçen bayt
3.991.401'den 1.988.049'a, kayıt başına 66,5 bayttan 33,1 bayta iniyor. Depoda üretilen dizeler,
üretildikleri sayılardan geniştir. Düzeltmenin yönü yukarıdır.

Oran 1'in çok üstünde olduğunda azaltma yanlış katmandadır. F2'de depoda satır işi **sıfır** —
depo hiç hesap yapmıyor — ama 60.000 kaydı okuyup seri hâle getirip gönderiyor, oysa isteğin
sonucu 600 satır. F1 aynı sonucu 600 kayıtla veriyor ve sınır bandını 6,000'den 0,062 Mbit/s'e
indiriyor: **96,8 kat.** Düzeltmenin yönü aşağıdır.

İki neden de "depo dolu, uygulama boşta" belirtisini verir, ama biri işi yukarı, öteki aşağı
taşımayı ister. Sınır oranı bu iki durumu tek bir sayıyla ayırır ve ayırdığı şey düzeltmenin
**yönüdür**.

#### Tek Sayı Tanı Vermiyor

Yönü bulmak planı seçmeye yetmez. Son tablo bir varsayımla plan çiftlerini karşılaştırıyor.

**KK1 — depoda yapılan bir satır işi, uygulamada yapılan bir satır işinin $k$ katına mal olur;
temel değer $k = 6$.** Gerekçe: uygulama katmanı durumsuzdur ve düğüm eklenerek yatay ölçeklenir
(M19/K01 `temel-nitelikler/03`), depo katmanında ise yazma tek noktadadır ve çekirdek eklemek
makine değiştirmektir. İki katmandaki bir birim iş aynı fiyata alınmaz. Duyarlılık $k = 2$ ve
$k = 12$ sütunlarında verildi.

Ağırlıklı satır işi ölçüsü üç $k$ değerinde de aynı çifti öneriyor: **D2 + F2**, 661.200 birimle
(k = 6). Bu öneri yanlıştır. Aynı satırda sınırdan geçen kayıt 120.000, sınır bandı 15,158
Mbit/s; D2 + F1 çiftinde ise 60.600 kayıt ve 9,220 Mbit/s. Satır işi ölçüsü F2'yi ucuz sanıyor,
çünkü F2'nin depoya yüklediği iş satır işi değil — okuma, seri hâle getirme ve taşımadır ve o
sütunda hiç görünmüyor.

Kursun ilk kuralı budur: **bir belirti tek bir sayıyla açıklanmaz.** Depo iki ayrı para
biriminde meşgul olur — satır başına hesap ve sınırdan geçen bayt — ve bir plan yalnız birine
bakılarak seçilirse öteki büyür. Doğru seçim D2 + F1'dir: her iki istekte de gereksiz iş
katmandan çıkarılmıştır.

#### Depoda Kalmanın Doğru Olduğu Koşul

Hesabın depoda yapılması bir yasak değildir; F1 tam olarak bunu yapıyor ve dört planın en
ucuzudur. Koşul ölçülebilir: **depoda yapılan iş, sınırdan geçen kaydı azalttığı ölçüde
haklıdır.** F1'de 60.000 satır işi, sınırdan geçen kaydı 60.000'den 600'e indiriyor — kayıt
başına 100 birim azaltma, K01'in V13 = 100 oranının kendisi. D1'de 240.000 fazladan satır işi
sınırdan geçen kaydı hiç azaltmıyor, baytı 2,01 katına çıkarıyor.

Koşul bir ölçekle sınırlıdır. Fatura satırı başına düşen kalem sayısı 100'den 1'e inseydi —
yani her gönderi kendi fatura satırını doğursaydı — F1 ile F2 arasındaki 96,8 katlık band farkı
kaybolur ve toplamayı depoda yapmanın hiçbir karşılığı kalmazdı. Aynı biçim, aynı sistemde,
başka bir kırılım oranında yanlış tarafa geçer.

#### Düzeltmenin Karşılığında Büyüyen

D1'den D2'ye geçiş depodaki satır işini 300.000'den 60.000'e indirdi; uygulamadaki satır işi
0'dan 240.000'e çıktı. K01 ölçeğinde bu, depoda saniyede 4166,67 satır işinden 833,33'e inmek ve
uygulamada 0'dan 3333,33'e çıkmaktır. KK1'in $k = 6$ değerinde takas kârlıdır, $k = 1$ olsaydı
olmazdı — yani düzeltmenin gerekçesi katmanların ölçekleme asimetrisidir, işin kendisi değil.

İkinci karşılık koddadır ve sayılmaz ama görülür: biçimlendirme kuralı depodan çıkınca sorgunun
her tüketicisi onu kendi tarafında tekrar yazmak zorunda kalır. Depoda tek bir yerde duran
kural, uygulamada tüketici sayısı kadar yerde durur. Bu bedel D2'yi yanlış yapmaz; bedelin
adının konmamış olması yanlış yapar.

#### Özet

- Tanı üç adımdır: belirti bir sayıdır, ayırt etme aynı belirtiyi veren iki nedeni ayıran bir
  ölçümdür, düzeltme karşılığında büyüyeni sayar. Ölçümsüz yapılan şey tahmindir.
- Meşgul veritabanının iki nedeni aynı belirtiyi verir: azaltmayan iş depoda (D1) ve azaltan iş
  uygulamada (F2). Düzeltmeleri birbirinin tersidir.
- Sınır oranı yönü seçer: 1,00 ise iş yukarı taşınır (D1 → D2, geçen bayt 3.991.401 → 1.988.049),
  1'in çok üstündeyse aşağı indirilir (F2 → F1, band 6,000 → 0,062 Mbit/s, 96,8 kat).
- Tek sayı tanı vermez: ağırlıklı satır işi ölçüsü D2 + F2'yi öneriyor (661.200, k = 6), oysa o
  çift sınırdan 120.000 kayıt ve 15,158 Mbit/s geçiriyor; doğru seçim D2 + F1'dir.
- Hesabın depoda kalması, sınırdan geçen kaydı azalttığı ölçüde haklıdır: F1'de 60.000 satır işi
  kaydı 100 kat azaltıyor. Kırılım oranı 1'e inseydi aynı seçim yanlışa dönerdi.
- Düzeltmenin karşılığı uygulama katmanında büyüyor: satır işi 0 → 240.000 (K01 ölçeğinde
  saniyede 0 → 3333,33) ve biçimlendirme kuralı tek yerden tüketici sayısı kadar yere dağılıyor.

#### Sonraki Adım

Sınır oranı, işin hangi katmanda yapılacağını iki katman için çözdü: depo ve uygulama. Oysa
zincirin bir ucu daha var ve orası hiç ölçülmedi. D2 planı biçimlendirmeyi depodan çıkardı ve
uygulamaya koydu; aynı iş bir adım daha ileri gidip **istemciye** de konabilir — sunucu ham
kaydı gönderir, tarayıcı biçimlendirir. Bu, aynı takasın üçüncü katmanıdır ve orada ölçünün
adı değişir: uygulama katmanında iş düğüm eklenerek dağıtılır, istemcide dağıtılacak bir düğüm
yoktur. Sonraki ders bu katmanı ele alır: sunucunun gönderdiği yükün istemcideki işi nasıl
büyüttüğü, aynı yavaşlık belirtisinin sunucu kaynaklı mı istemci kaynaklı mı olduğunu ayıran
ölçüm ve yükü küçültmenin karşılığında sunucuda büyüyen iş.

### Meşgul Ön Yüz

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/mesgul-on-yuz

Sunucunun gönderdiği yükün istemcideki işi büyütmesi: sunucu yanıt süresi değişmezken kullanıcıya görünen sürenin büyümesi, aynı belirtiyi veren yük büyümesi ile istemci kodu büyümesinin kayıt başına iş ölçüsüyle ayrılması, sıralamayı sunucuya taşımanın 26,8 katlık kazancı ve biçimlendirmeyi taşımanın önbellek anahtarına ödettiği bedel.

Sınır oranı işin hangi katmanda yapılacağını depo ile uygulama arasında çözdü. Zincirin bir ucu
daha var ve orada takas kural değiştirir: uygulama katmanında iş düğüm eklenerek dağıtılır,
istemcide dağıtılacak düğüm yoktur. Bu ders o üçüncü katmanı ele alır ve aynı üç adımı yürütür —
belirti bir sayıdır, iki neden bir ölçümle ayrılır, düzeltmenin karşılığında büyüyen sayılır.

Ana iş parçacığı, uzun görev ve kullanıcı merkezli ölçütler Frontend Kalitesi kursunda tanımlandı
ve ölçüldü; burada yeniden tanımlanmaz. Buradaki soru sistem tasarımı sorusudur: **sunucunun
gönderdiği yük, istemcideki işi hangi çarpanla büyütür ve bu çarpan hangi ölçümle görülür.**

#### Belirti: Sunucu Sakin, Kullanıcı Bekliyor

Satıcı panelinde açık gönderi listesi açılıyor. Kullanıcıya görünen süre son iki sürümde büyüdü.
Sunucu tarafındaki yanıt süresi ölçütü değişmedi; uç, K01'in tepe yükünü aynı sayılarla
karşılıyor. Yanıtın baytı da denetlendi ve aktarımın payı değişmemiş. Belirti şu hâle geliyor:
**işin tamamı istemciye geçtikten sonra büyüyor.**

Bu noktada iki neden vardır ve ikisi de sunucu ölçütlerinde görünmez.

**Neden A — sunucu gönderdiği kayıt sayısını artırdı.** İstemci kodu değişmedi, ama yük büyüdü;
aynı kod daha çok kayıt üzerinde koşuyor.

**Neden B — istemci kayıt başına iş ekledi.** Yük aynı, ama yeni sürüm her kayıtta iki adım
fazla yapıyor.

Ölçmeden verilen yanıt tahmindir ve tahminin iki yönü de vardır: "sunucu çok veri gönderiyor"
ile "ön yüz ağırlaştı" cümleleri aynı belirtiden çıkarılır ve biri yanlıştır.

#### Ölçüm Düzeneği

Aşağıdaki koşum bir **modeldir**. Tarayıcı süresi ölçülmüyor ve uydurulmuyor; sayılan şey
istemcinin tek bir iş parçacığında yapmak zorunda olduğu adımlardır: ayrıştırılan alan, dönüşüm
adımı ve sıralama karşılaştırması. Üçü de gerçekten koşuyor, sayaçlar gerçek çağrıları sayıyor
ve sıralamanın girdisi tohumlu bir üreteçten geliyor.

**KK2 — panelde bir ekranda görünen satır: 25.** Gerekçe: liste görünümü sayfa boyu kadar satır
gösterir, kalanı kaydırmayla gelir. Duyarlılığı bir sonraki derste çağrı sayısına dönüşür.

```js
// onyuz/isparcacigi.mjs — sunucunun gonderdigi yukun on yuzdeki isi nasil buyuttugu.
// MODELDIR: is birimi soyut bir adimdir, sure olculmez; sayilan sey ayristirilan alan,
// donusum adimi ve siralama karsilastirmasidir. Ucu de gercekten kosar.
const OLAY = 7, GORUNEN = 25, BOLGE = 12, ROTA = 40;      // K01 V4 = 7 durum olayi; KK2 = 25 satir

function uret(N) {                                        // sunucunun elindeki tam veri
  let s = 20260801 % 2147483647;                          // tohum gorunur; siralama sirasi belirlenimli
  const rast = () => (s = (s * 48271) % 2147483647) % 100000;
  const g = [];
  for (let i = 0; i < N; i += 1) {
    const olaylar = [];
    for (let j = 0; j < OLAY; j += 1)
      olaylar.push({ kod: (i * 3 + j) % 5, ts: rast(),
        bolge: (i + j) % BOLGE, rota: `R-${(i + j) % ROTA}`, tasiyici: (i + j) % 6 });
    g.push({ no: `TR-${i}`, satici: i % 20, agirlik: 1 + (i % 30), guncelleme: rast(), olaylar });
  }
  return g;
}

const AD = ["kabul", "aktarma", "dagitimda", "teslim", "bekliyor"];

function sunucu(N, plan) {                                // uctan cikan yuk
  const g = uret(N);
  if (plan === "ham") return g;
  const ust = [...g].sort((a, b) => b.guncelleme - a.guncelleme).slice(0, GORUNEN);
  return ust.map((x) => ({ no: x.no, guncelleme: x.guncelleme,
    olaylar: [...x.olaylar].sort((a, b) => b.ts - a.ts).slice(0, 3)
      .map((o) => (plan === "bicimli"
        ? { satir: `${AD[o.kod]} / ${o.rota} / ${o.ts}` }
        : { kod: o.kod, ts: o.ts, rota: o.rota })) }));
}

const alanSay = (x) => Array.isArray(x) ? x.reduce((a, y) => a + alanSay(y), 0)
  : (x && typeof x === "object") ? Object.values(x).reduce((a, y) => a + alanSay(y), 0) : 1;

function istemci(metin, { donusum, sirala }) {
  const s = { ayristirma: 0, donusum: 0, karsilastirma: 0, cizim: 0 };
  const veri = JSON.parse(metin);
  s.ayristirma = alanSay(veri);
  let liste = veri;
  if (sirala) {                                           // gonderi listesi: N log N
    liste = [...veri].sort((a, b) => { s.karsilastirma += 1; return b.guncelleme - a.guncelleme; });
    for (const x of liste) [...x.olaylar].sort((a, b) => { s.karsilastirma += 1; return b.ts - a.ts; });
  }
  if (donusum > 0) for (const x of veri) for (const o of x.olaylar) {   // once bicimlendir, sonra dilimle
    s.donusum += donusum;
    void [AD[o.kod] ?? "", String(o.ts).padStart(6, "0"), `${o.rota}`, `${o.bolge ?? 0}`,
      `${o.tasiyici ?? 0}`, `${o.kod ?? 0}`].slice(0, donusum);
  }
  for (const x of liste.slice(0, GORUNEN)) { s.cizim += 3; void x.no; }
  s.toplam = s.ayristirma + s.donusum + s.karsilastirma + s.cizim;
  return s;
}

const DURUM = [
  ["ham yuk, istemci s1", 200, "ham", { donusum: 4, sirala: true }],
  ["ham yuk, istemci s1", 400, "ham", { donusum: 4, sirala: true }],
  ["ham yuk, istemci s2", 200, "ham", { donusum: 6, sirala: true }],
  ["sirali yuk, istemci s1", 200, "sirali", { donusum: 4, sirala: false }],
  ["bicimli yuk", 200, "bicimli", { donusum: 0, sirala: false }],
];
const say = (x) => x.toLocaleString("tr-TR");
console.log(`model: bir gonderi ${OLAY} durum olayi tasiyor (K01 V4), ekranda ${GORUNEN} satir ` +
  `gorunuyor (KK2); is birimi soyut bir adimdir, sure olculmez`);
console.log(`\n${"durum".padEnd(24)}${"N".padStart(6)}${"gecen kayit".padStart(13)}` +
  `${"ayristirma".padStart(12)}${"donusum".padStart(10)}${"karsilastirma".padStart(15)}` +
  `${"toplam is".padStart(12)}${"kayit basina".padStart(14)}${"gorunen satir basina".padStart(22)}`);
const r = {};
for (const [ad, N, plan, ist] of DURUM) {
  const yuk = sunucu(N, plan), metin = JSON.stringify(yuk);
  const s = istemci(metin, ist);
  r[`${ad}|${N}`] = { ...s, gecen: yuk.length, bayt: Buffer.byteLength(metin) };
  console.log(ad.padEnd(24) + String(N).padStart(6) + say(yuk.length).padStart(13) +
    say(s.ayristirma).padStart(12) + say(s.donusum).padStart(10) + say(s.karsilastirma).padStart(15) +
    say(s.toplam).padStart(12) + (s.toplam / yuk.length).toFixed(2).padStart(14) +
    (s.toplam / GORUNEN).toFixed(1).padStart(22));
}

console.log(`\nolcekleme (ham yuk, istemci s1): N iki katina cikarken toplam is kac kat`);
console.log(`${"N".padStart(6)}${"toplam is".padStart(12)}${"kat".padStart(8)}` +
  `${"kayit basina".padStart(14)}${"karsilastirma/N".padStart(17)}`);
let onceki = 0;
for (const N of [50, 100, 200, 400, 800]) {
  const s = istemci(JSON.stringify(sunucu(N, "ham")), { donusum: 4, sirala: true });
  console.log(String(N).padStart(6) + say(s.toplam).padStart(12) +
    (onceki ? (s.toplam / onceki).toFixed(3) : "-").padStart(8) +
    (s.toplam / N).toFixed(2).padStart(14) + (s.karsilastirma / N).toFixed(2).padStart(17));
  onceki = s.toplam;
}

const a = r["ham yuk, istemci s1|200"], b = r["ham yuk, istemci s1|400"];
const c = r["ham yuk, istemci s2|200"], d = r["sirali yuk, istemci s1|200"], e = r["bicimli yuk|200"];
console.log(`\nayirt eden olcum — kayit basina is: A (sunucu N'i 200 -> 400) ` +
  `${(a.toplam / a.gecen).toFixed(2)} -> ${(b.toplam / b.gecen).toFixed(2)}, ` +
  `B (istemci s1 -> s2, N sabit) ${(a.toplam / a.gecen).toFixed(2)} -> ${(c.toplam / c.gecen).toFixed(2)}`);
console.log(`duzeltme: sirali yukta toplam is ${say(a.toplam)} -> ${say(d.toplam)} ` +
  `(${(a.toplam / d.toplam).toFixed(1)} kat), bicimli yukta ${say(e.toplam)} ` +
  `(${(a.toplam / e.toplam).toFixed(1)} kat); gecen bayt ${say(a.bayt)} -> ${say(d.bayt)} -> ${say(e.bayt)}`);

const UC = 513.89, ISABET = 0.9;                          // K01 tepe uctaki istek/s, K02 onbellek isabeti
console.log(`\nisi sunucuya tasimanin kosulu: onbelleklenebilir is ${(UC * (1 - ISABET)).toFixed(2)} kez/s, ` +
  `onbelleklenemez is ${UC.toFixed(2)} kez/s kosar (oran ${(1 / (1 - ISABET)).toFixed(2)}); ` +
  `istemcide her iki durumda da kullanici basina 1 kez`);
```

```
model: bir gonderi 7 durum olayi tasiyor (K01 V4), ekranda 25 satir gorunuyor (KK2); is birimi soyut bir adimdir, sure olculmez

durum                        N  gecen kayit  ayristirma   donusum  karsilastirma   toplam is  kayit basina  gorunen satir basina
ham yuk, istemci s1        200          200       7.800     5.600          3.920      17.395         86.97                 695.8
ham yuk, istemci s1        400          400      15.600    11.200          8.243      35.118         87.80                1404.7
ham yuk, istemci s2        200          200       7.800     8.400          3.920      20.195        100.97                 807.8
sirali yuk, istemci s1     200           25         275       300              0         650         26.00                  26.0
bicimli yuk                200           25         125         0              0         200          8.00                   8.0

olcekleme (ham yuk, istemci s1): N iki katina cikarken toplam is kac kat
     N   toplam is     kat  kayit basina  karsilastirma/N
    50       4.321       -         86.42            17.92
   100       8.649   2.002         86.49            18.74
   200      17.395   2.011         86.97            19.60
   400      35.118   2.019         87.80            20.61
   800      70.963   2.021         88.70            21.61

ayirt eden olcum — kayit basina is: A (sunucu N'i 200 -> 400) 86.97 -> 87.80, B (istemci s1 -> s2, N sabit) 86.97 -> 100.97
duzeltme: sirali yukta toplam is 17.395 -> 650 (26.8 kat), bicimli yukta 200 (87.0 kat); gecen bayt 95.028 -> 3.771 -> 3.775

isi sunucuya tasimanin kosulu: onbelleklenebilir is 51.39 kez/s, onbelleklenemez is 513.89 kez/s kosar (oran 10.00); istemcide her iki durumda da kullanici basina 1 kez
```

#### Kayıt Başına İş Nedeni Ayırıyor

Ayırt eden ölçüm **kayıt başına iş birimidir**: toplam işin, sunucunun gönderdiği kayıt sayısına
bölümü. İki neden bu tek sayıda ayrışıyor.

Neden A'da sunucu 200 kayıttan 400'e çıkıyor. Toplam iş 17.395'ten 35.118'e gidiyor — kullanıcı
belirtiyi hisseder. Ama kayıt başına iş 86,97'den 87,80'e çıkıyor; yüzde 0,95. İstemci kodu
değişmemiştir ve ölçüm bunu söylüyor.

Neden B'de gönderilen kayıt 200'de kalıyor, toplam iş 17.395'ten 20.195'e çıkıyor. Kayıt başına
iş 86,97'den 100,97'ye gidiyor; yüzde 16,1. Yük değişmemiştir, kod değişmiştir.

**Aynı belirti, aynı yönde büyüyen toplam, ters yönde işaret eden bölüm.** Toplam işe bakmak
tanı vermiyor; bölmek veriyor.

Ölçekleme tablosu üçüncü bir şey söylüyor. N iki katına çıktığında toplam iş 2,002'den 2,021'e
tırmanan bir katsayıyla büyüyor ve kayıt başına iş 86,42'den 88,70'e çıkıyor. Bu, kodun değiştiği
anlamına gelmez; karşılaştırma sütunu nedeni veriyor: kayıt başına karşılaştırma 17,92'den
21,61'e çıkıyor. İstemci listeyi kendi sıralıyor ve sıralama kayıt sayısıyla doğrusal büyümüyor.
Yani yük büyümesi, kayıt başına işi de bir miktar büyütür — bu payın **logaritmik**, kod
değişikliğinin payının **basamaklı** olduğu bilinirse iki neden yine ayrılabilir.

#### İki Düzeltme, Bir Farkla

Belirtinin asıl büyüklüğü son sütunda. Ham yükte görünen satır başına ana iş parçacığı işi
695,8 birim; ekranda 25 satır var ve istemci 200 kaydın hepsini ayrıştırıp biçimlendiriyor,
sonra dilimliyor. Bu, karşıt kalıbın kendisidir: **iş, gösterilecek olanla değil, gönderilenle
ölçekleniyor.**

Sunucu sıralamayı ve seçmeyi üstlendiğinde toplam iş 17.395'ten 650'ye iniyor — **26,8 kat**.
Görünen satır başına iş 695,8'den 26,0'a düşüyor. Sunucu ayrıca biçimlendirmeyi de üstlenirse iş
200'e iniyor; ham yüke göre 87,0 kat, sıralı yüke göre 3,25 kat.

İki düzeltme aynı şey değil ve son satır farkı veriyor. Sıralı yüke geçiş sınırdan geçen baytı
95.028'den 3.771'e indiriyor. Biçimli yüke geçiş baytı 3.771'den 3.775'e **çıkarıyor** —
biçimlendirme sunucuda yapıldığında yanıt küçülmüyor. Yani ikinci adım yalnız iş birimi
kazandırıyor, üçüncü bir maliyeti var ve o maliyet bir sonraki bölümdedir.

#### İşi İstemcide Bırakmanın Doğru Olduğu Koşul

İstemcide iş yapmak bir hata değildir; sunucuya taşımanın da bir fiyatı vardır ve fiyat sayıyla
yazılabilir. İstemcide bir iş kullanıcı başına **bir kez** koşar ve kullanıcının kendi
donanımında koşar. Sunucuya taşındığında aynı iş K01'in tepe yükünde saniyede 513,89 kez koşar —
tek bir kaynak havuzunda.

Koşulu belirleyen şey işin **önbelleklenebilir** olmasıdır. K02'nin kenar önbelleği okumaların
onda dokuzunu karşılıyor; sunucuya taşınan iş önbelleklenebiliyorsa saniyede 51,39 kez koşar,
önbelleklenemiyorsa 513,89 kez. Oran 10,00. Sıralama ile seçme kullanıcıya özel değildir; aynı
satıcının aynı listesi herkes için aynıdır, önbelleklenebilir, taşınmalıdır. Biçimlendirme
kullanıcının yerel ayarına bağlıdır; taşınırsa yanıt kullanıcıya özelleşir ve önbellek anahtarı
o boyut sayısı kadar parçaya bölünür. Aynı isabeti korumak için önbellek o kat kadar büyütülür.

Bu yüzden ölçüm iki düzeltmeyi ayırıyor: **26,8 katlık kazanç alınır, 3,25 katlık kazanç
alınmaz.** İkincisinin karşılığında büyüyen şey ölçülebilir ve büyük; birincisinin karşılığı
yalnız sunucuda 51,39 kez/s koşan bir sıralamadır.

#### Karşılığında Büyüyen

Sıralamanın ve seçmenin sunucuya taşınmasının üç karşılığı var. Sunucu artık saniyede 51,39 kez
sıralama yapıyor — önbellek arkasında, K01'in depoya ulaşan 138,89 istek/s'lik yükünün üstüne
biner. İkincisi, hangi 25 kaydın gösterileceği kararı sunucuya geçti; kullanıcı listede
ilerledikçe bu karar yeni bir istek doğurur. Üçüncüsü, istemcinin elinde artık tam liste yok:
yerel bir süzme ya da yeniden sıralama da sunucuya gitmek zorunda.

Son iki karşılık aynı yöne bakıyor ve bir sonraki dersin konusudur.

#### Özet

- Belirti sunucu ölçütlerinde görünmez: yanıt süresi ve bayt sabitken kullanıcıya görünen süre
  büyür. Aynı belirtiyi yük büyümesi de istemci kodu büyümesi de verir.
- Ayırt eden ölçüm kayıt başına iş birimidir: yük 200'den 400'e çıkarken 86,97 → 87,80 (%0,95),
  kod s1'den s2'ye geçerken 86,97 → 100,97 (%16,1).
- Yük büyümesi kayıt başına işi de bir miktar büyütür ama logaritmik olarak: kayıt başına
  karşılaştırma 17,92'den 21,61'e çıkıyor, çünkü sıralama istemcide yapılıyor.
- Karşıt kalıbın ölçüsü görünen satır başına iştir: ham yükte 695,8 birim, oysa ekranda 25 satır
  var. Sıralama ve seçme sunucuya taşındığında toplam iş 26,8 kat düşüyor.
- Biçimlendirmenin de taşınması 3,25 kat daha kazandırır ama baytı 3.771'den 3.775'e çıkarır ve
  yanıtı kullanıcıya özelleştirir; önbellek anahtarı bölünür.
- Koşul önbelleklenebilirliktir: sunucuya taşınan iş önbelleklenebiliyorsa 51,39 kez/s, değilse
  513,89 kez/s koşar (oran 10,00); istemcide her iki durumda da kullanıcı başına bir kez.

#### Sonraki Adım

Sunucu artık 200 kayıt yerine 25 kayıt gönderiyor ve hangi 25 kaydın gösterileceğine kendisi
karar veriyor. Kullanıcı listede ilerledikçe bu karar yenilenmek zorunda: her kaydırma bir istek,
her süzme bir istek, satır açıldığında ayrıntı için bir istek daha. Yük küçüldü, **çağrı sayısı**
büyüdü ve bu sayı hiç ölçülmedi. Sonraki ders o sayıyı ele alır: çağrı sayısı ile taşınan bayt
arasındaki ödünleşim, sabit çağrı maliyetinin hangi kayıt sayısında taşınan bayta baskın geldiği
ve tek büyük çağrının ne zaman çok sayıda küçük çağrıdan daha kötü olduğu.

### Gevezelik Eden Girdi/Çıktı

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/gevezelik-eden-girdi-cikti

Çağrı sayısı ile taşınan bayt arasındaki ödünleşim: aynı panel açılışının üç çağrı taneciğinde ölçülmesi, taşınan baytın işe yarayan payının zarf payı ile kullanılmayan yük payına ayrılması, tek büyük çağrının hangi kullanım oranının altında daha kötü olduğu ve yüksek önbellek isabetinin neden az bayt anlamına gelmediği.

Önceki ders yükü küçülttü: sunucu 200 kayıt yerine 25 kayıt gönderiyor ve hangi 25 kaydın
gösterileceğine kendisi karar veriyor. Bu kararın bir bedeli vardı ve o bedel sayılmadı. Kullanıcı
listede ilerledikçe karar yenilenir, bir satır açıldığında ayrıntısı istenir, bir süzme yapıldığında
liste yeniden alınır. **Çağrı sayısı** büyüdü.

Bu, Veri Erişim Katmanı kursunda kurulan **N+1 sorgu sorununun** servis çağrısı ölçeğindeki
karşılığıdır. Oradaki mekanik — sorgu sayısının kayıt sayısıyla büyümesi, toplu getirme ve
birleştirme — burada tekrarlanmaz. Buradaki soru başkadır: **çağrı sayısını azaltmanın karşılığında
taşınan bayt ne kadar büyür ve bu takasın nerede tersine döndüğü hangi ölçümle bulunur.**

#### Belirti: Hiçbir Ölçütte Görünmeyen Yavaşlık

Panel açılışı uçtan uca yavaş. Her çağrının sunucu tarafındaki süresi küçük ve sabit; hiçbir uç
eşiğini aşmıyor. Toplam yararlı yük dokuz kilobayt dolayında, yani aktarım açıklama olamaz. Depo
tarafında sınır oranı 1,00 — 01. dersin ölçüsü temiz. Belirti her ölçütün arasından geçiyor.

**Gevezelik eden girdi/çıktı** (chatty I/O), bir işin çok sayıda küçük çağrıya bölünmesidir. Ama
adı koymak yine yetmez, çünkü aynı belirtiyi ters bir düzenleme de verir.

**Neden A — çağrı sayısı.** Her çağrı, gövdesinden bağımsız bir **zarf** (envelope) taşır: istek
satırı, kimlik, izleme bağlamı, içerik başlıkları, yanıt başlıkları. Zarf sabittir; çağrı sayısıyla
çarpılır. Ayrıca çağrılar birbirini beklerse **tur** sayısı büyür.

**Neden B — aşırı paketleme.** Çağrı sayısı bire indirilmiştir, ama tek çağrı ihtiyaç duyulmayanı
da taşır. Zarf payı sıfıra yakındır, taşınanın çoğu hiç kullanılmaz.

İkisi de "çağrılar hızlı ama açılış yavaş" belirtisini verir ve düzeltmeleri birbirinin tersidir.

#### Ölçüm Düzeneği

Aşağıdaki koşum bir **modeldir**: tur soyut bir adımdır ve süre ölçülmez. Ölçülen şey bayttır ve
zarfın baytı uydurulmaz — kodun içindeki üstbilgi metninden gerçekten hesaplanır.

**KK3 — istemcinin aynı anda açık tutabildiği çağrı sayısı: 6.** Gerekçe: bağımlı çağrılar bir
turda değil, bu sınırın böldüğü kadar turda tamamlanır. Duyarlılığı tur sütununda görünür.

Üç tanecik karşılaştırılıyor. **G1**, liste, satıcı ve bölge için birer çağrı ve açılan her satır
için bir ayrıntı çağrısı yapar. **G2**, listeyi ve yirmi beş ayrıntıyı tek çağrıda alır, satıcıyı
ve bölgeyi ayrı bırakır. **G3**, hepsini tek çağrıda alır. Değişken $k$, yirmi beş satırın kaçının
ayrıntısının gerçekten açıldığıdır.

```js
// cagri/tanecik.mjs — ayni panel acilisinin uc cagri taneciginde olculmesi. Olcu: tasinan
// baytin ise yarayan payi ve onun iki bileseni (zarf payi, kullanilmayan yuk payi).
// MODELDIR: tur soyut bir adimdir, sure olculmez; zarfin bayti asagidaki ustbilgiden olculur.
const GORUNEN = 25, OLAY = 7, TARIFE = 40, BOLGE = 12;   // KK2 = 25 satir, K01 V4 = 7 olay
const ESZAMAN = 6;                                       // KK3: istemcinin ayni anda actigi cagri

const ZARF = ["GET /panel/liste HTTP/1.1", "host: uc", "accept: application/json",
  "accept-encoding: gzip", "authorization: Bearer <belirtec>", "traceparent: <baglam>",
  "HTTP/1.1 200 OK", "content-type: application/json", "cache-control: max-age=30",
  "etag: <surum>", "content-length: <n>", "traceparent: <baglam>"].join("\r\n") + "\r\n\r\n";
const Z = Buffer.byteLength(ZARF);

const bayt = (x) => Buffer.byteLength(JSON.stringify(x));
const liste = () => Array.from({ length: GORUNEN }, (_, i) => ({ no: `TR-${i}`,
  guncelleme: (i * 97) % 100000, durum: i % 5, agirlik: 1 + (i % 30) }));
const ayrinti = (i) => ({ no: `TR-${i}`, olaylar: Array.from({ length: OLAY }, (_, j) => ({
  kod: (i * 3 + j) % 5, ts: (i * 97 + j * 13) % 100000, rota: `R-${(i + j) % 40}` })) });
const satici = () => ({ ad: "satici-10", sozlesme: 0.07,
  tarife: Array.from({ length: TARIFE }, (_, i) => ({ id: i, oran: 12 + (i % 7) * 0.5 })) });
const bolge = () => ({ bolgeler: Array.from({ length: BOLGE }, (_, i) => ({ id: i, katsayi: 1 + (i % 5) * 0.1 })) });

const L = bayt(liste()), S = bayt(satici()), B = bayt(bolge());
const A = Array.from({ length: GORUNEN }, (_, i) => bayt(ayrinti(i)));
const ilkK = (k) => A.slice(0, k).reduce((a, x) => a + x, 0);
const TUM_A = ilkK(GORUNEN);

// k: 25 satirin kacinin ayrintisi gercekten aciliyor
const PLAN = {
  "G1 gevezelik": (k) => ({ cagri: 3 + k, tur: 1 + Math.ceil(k / ESZAMAN), yuk: L + S + B + ilkK(k) }),
  "G2 toplu": (k) => ({ cagri: 3, tur: 1, yuk: L + S + B + TUM_A }),
  "G3 tek cagri": (k) => ({ cagri: 1, tur: 1, yuk: L + S + B + TUM_A }),
};
const olc = (ad, k) => {
  const p = PLAN[ad](k), zarf = p.cagri * Z, tasinan = p.yuk + zarf;
  const kullanilan = L + S + B + ilkK(k);
  return { ...p, zarf, tasinan, kullanilan, zarfPayi: zarf / tasinan,
    bosPayi: (p.yuk - kullanilan) / tasinan, isePayi: kullanilan / tasinan };
};

const AD = Object.keys(PLAN), say = (x) => x.toLocaleString("tr-TR");
console.log(`model: panel ${GORUNEN} satir gosteriyor, zarf ${Z} bayt (ustbilgi metninden olculdu), ` +
  `eszamanli cagri siniri ${ESZAMAN} (KK3); liste ${L}, satici ${S}, bolge ${B}, ` +
  `${GORUNEN} ayrinti ${say(TUM_A)} bayt`);
for (const k of [25, 3]) {
  console.log(`\n${GORUNEN} satirin ${k} tanesinin ayrintisi aciliyor`);
  console.log(`${"plan".padEnd(16)}${"cagri".padStart(7)}${"tur".padStart(5)}${"tasinan bayt".padStart(14)}` +
    `${"zarf payi".padStart(11)}${"kullanilmayan yuk".padStart(19)}${"ise yarayan pay".padStart(17)}`);
  for (const ad of AD) {
    const o = olc(ad, k);
    console.log(ad.padEnd(16) + String(o.cagri).padStart(7) + String(o.tur).padStart(5) +
      say(o.tasinan).padStart(14) + o.zarfPayi.toFixed(4).padStart(11) +
      o.bosPayi.toFixed(4).padStart(19) + o.isePayi.toFixed(4).padStart(17));
  }
}

console.log(`\nkac ayrinti aciliyorsa hangi plan az tasir (soguk acilis)`);
console.log(`${"acilan".padStart(7)}${"G1 bayt".padStart(10)}${"G2 bayt".padStart(10)}` +
  `${"G1/G2".padStart(8)}${"G1 tur".padStart(8)}${"en az tasiyan".padStart(15)}`);
for (const k of [1, 3, 6, 12, 13, 18, 25]) {
  const g = AD.map((ad) => olc(ad, k));
  const en = AD[g.indexOf(g.reduce((a, b) => (b.tasinan < a.tasinan ? b : a)))];
  console.log(String(k).padStart(7) + say(g[0].tasinan).padStart(10) + say(g[1].tasinan).padStart(10) +
    (g[0].tasinan / g[1].tasinan).toFixed(3).padStart(8) + String(g[0].tur).padStart(8) + en.padStart(15));
}

const PARCA = { liste: 5, ayrinti: 20, satici: 100, bolge: 1000 };   // kac acilista bir degisiyor
const ICERIK = { "G1 gevezelik": [["liste"], ["satici"], ["bolge"],
    ...Array.from({ length: GORUNEN }, () => ["ayrinti"])],
  "G2 toplu": [["liste", "ayrinti"], ["satici"], ["bolge"]],
  "G3 tek cagri": [["liste", "ayrinti", "satici", "bolge"]] };
const GOVDE = { "G1 gevezelik": [L, S, B, ...A], "G2 toplu": [L + TUM_A, S, B],
  "G3 tek cagri": [L + TUM_A + S + B] };
const T = 100;
const sonuc = {};
for (const ad of AD) {
  let isabet = 0, tasinan = 0, zarf = 0;
  for (let t = 1; t <= T; t += 1) ICERIK[ad].forEach((parcalar, i) => {
    const degisti = t === 1 || parcalar.some((p) => t % PARCA[p] === 0);
    tasinan += Z + (degisti ? GOVDE[ad][i] : 0);   // isabet eden cagri da zarf oder: kosullu istek
    zarf += Z;
    if (!degisti) isabet += 1;
  });
  sonuc[ad] = { isabet: isabet / (T * ICERIK[ad].length), tasinan, zarf };
}
console.log(`\n${T} acilista, tum ayrintilar aciliyor, her acilis kosullu istekle dogruluyor; ` +
  `parca degisim araligi liste ${PARCA.liste} / ayrinti ${PARCA.ayrinti} / ` +
  `satici ${PARCA.satici} / bolge ${PARCA.bolge} acilis`);
console.log(`${"plan".padEnd(16)}${"isabet".padStart(9)}${"tasinan bayt".padStart(14)}` +
  `${"bunun zarfi".padStart(13)}${"zarf payi".padStart(11)}${"G3'e gore".padStart(11)}`);
for (const ad of AD) {
  const o = sonuc[ad];
  console.log(ad.padEnd(16) + o.isabet.toFixed(4).padStart(9) + say(o.tasinan).padStart(14) +
    say(o.zarf).padStart(13) + (o.zarf / o.tasinan).toFixed(4).padStart(11) +
    (o.tasinan / sonuc["G3 tek cagri"].tasinan).toFixed(3).padStart(11));
}
```

```
model: panel 25 satir gosteriyor, zarf 280 bayt (ustbilgi metninden olculdu), eszamanli cagri siniri 6 (KK3); liste 1368, satici 869, bolge 286, 25 ayrinti 6.459 bayt

25 satirin 25 tanesinin ayrintisi aciliyor
plan              cagri  tur  tasinan bayt  zarf payi  kullanilmayan yuk  ise yarayan pay
G1 gevezelik         28    6        16.822     0.4661             0.0000           0.5339
G2 toplu              3    1         9.822     0.0855             0.0000           0.9145
G3 tek cagri          1    1         9.262     0.0302             0.0000           0.9698

25 satirin 3 tanesinin ayrintisi aciliyor
plan              cagri  tur  tasinan bayt  zarf payi  kullanilmayan yuk  ise yarayan pay
G1 gevezelik          6    2         4.941     0.3400             0.0000           0.6600
G2 toplu              3    1         9.822     0.0855             0.5825           0.3320
G3 tek cagri          1    1         9.262     0.0302             0.6177           0.3521

kac ayrinti aciliyorsa hangi plan az tasir (soguk acilis)
 acilan   G1 bayt   G2 bayt   G1/G2  G1 tur  en az tasiyan
      1     3.884     9.822   0.395       2   G1 gevezelik
      3     4.941     9.822   0.503       2   G1 gevezelik
      6     6.531     9.822   0.665       2   G1 gevezelik
     12     9.750     9.822   0.993       3   G3 tek cagri
     13    10.294     9.822   1.048       4   G3 tek cagri
     18    13.014     9.822   1.325       4   G3 tek cagri
     25    16.822     9.822   1.713       6   G3 tek cagri

100 acilista, tum ayrintilar aciliyor, her acilis kosullu istekle dogruluyor; parca degisim araligi liste 5 / ayrinti 20 / satici 100 / bolge 1000 acilis
plan               isabet  tasinan bayt  bunun zarfi  zarf payi  G3'e gore
G1 gevezelik       0.9379       853.506      784.000     0.9186      3.940
G2 toplu           0.9200       250.391       84.000     0.3355      1.156
G3 tek cagri       0.7900       216.622       28.000     0.1293      1.000
```

#### İşe Yarayan Pay Nedeni Adlandırıyor

Ayırt eden ölçüm **taşınan baytın işe yarayan payıdır**: gerçekten kullanılan yararlı baytın,
zarfıyla birlikte taşınan toplam bayta bölümü. Kaybın nereye gittiği iki bileşende görünüyor ve
büyük olan bileşen nedeni adlandırıyor.

Yirmi beş satırın hepsi açıldığında G1'in işe yarayan payı 0,5339 — taşınanın neredeyse yarısı
zarftır (0,4661). Kullanılmayan yük sıfır: hiçbir bayt boşuna taşınmıyor, yine de plan en pahalısı.
Tanı gevezeliktir.

Üç satır açıldığında tablo yön değiştiriyor. G3'ün işe yarayan payı 0,3521 ve kaybın kaynağı zarf
değil, kullanılmayan yüktür: 0,6177. G1 aynı durumda 0,6600 ile en iyisi. Tanı aşırı paketlemedir.

**Tek bir ölçüm, iki bileşen, iki karşıt düzeltme.** Zarf payı büyükse çağrılar birleştirilir;
kullanılmayan yük payı büyükse çağrılar ayrılır. Toplam bayta bakmak bunu söylemez — k = 3'te G3'ün
9.262 baytı, k = 25'teki 9.262 baytla aynıdır, oysa biri sağlıklı, öteki israftır.

#### Ödünleşimin Döndüğü Nokta

Üçüncü tablo takası sayıyla sınırlıyor. Yirmi beş satırın kaçının ayrıntısının açıldığı, hangi
planın az taşıdığını belirliyor ve dönüm noktası **12 ile 13 arasındadır**. On iki satırda G1
9.750 bayt, G3 9.262 bayt taşıyor; on üç satırda G1 10.294'e çıkıp G2'yi 1,048 katıyla geçiyor.
Bir satırda oran 0,395 — gevezelik planı üçte birinden az taşıyor.

Bu, karşıt kalıbın **doğru olduğu koşulun** sayısıdır: **taşınan yükün kullanılma oranı 12/25 =
0,48'in altındaysa çok sayıda küçük çağrı daha azını taşır.** Aynı panel, aynı sistem, aynı zarf —
değişen tek şey kullanıcının kaç satır açtığıdır. Kalıp bir yasak değil, bir orana bağlı bir
seçimdir.

Tur sütunu takasın ikinci yüzüdür ve baytla aynı yöne bakmaz. G1 bir satırda 2 tur, yirmi beş
satırda 6 tur alıyor; G2 ve G3 her durumda 1 tur. KK3'ün altı çağrılık sınırı burada görünüyor:
çağrılar paralel gitse bile sınır onları turlara böler. Az taşıyan plan, daha çok tur bekletebilir.

#### Yüksek İsabet Az Bayt Demek Değil

Son tablo bir sezgiyi bozuyor. Yüz açılışta gevezelik planının önbellek isabeti en yüksektir:
0,9379, G3'ün 0,7900'üne karşı. Nedeni tanecikliktir — küçük çağrılar birbirinden bağımsız eskir,
birleşik yanıt ise parçalarının en sık değişenine göre eskir.

Buna rağmen G1 toplamda **3,94 kat** fazla bayt taşıyor: 853.506'ya karşı 216.622. Tablo nedeni
veriyor — taşınanın 784.000 baytı zarftır, yani yüzde 91,86'sı. İsabet eden bir çağrı gövdesini
taşımaz ama zarfını yine öder; koşullu istek de bir istektir.

Kural şudur: **isabet oranı bir tanecik ölçüsü değildir.** Yirmi sekiz çağrılık bir plan, isabetini
0,94'e çıkarsa bile tek çağrılık plandan dört kat fazla taşıyabilir. İki sayı birlikte okunmalıdır:
isabet ve zarf payı.

#### Karşılığında Büyüyen

G1'den G2'ye geçmek taşınanı 3,94 kattan 1,156 kata indiriyor. Karşılığında üç şey büyüyor.

Önbellek isabeti düşüyor: 0,9379'dan 0,9200'e, tek çağrıda 0,7900'e. Liste beş açılışta bir
değiştiği için ona bağlanan her parça da beş açılışta bir bayatlar; ayrıntının kendi yirmi
açılışlık ömrü kayboluyor.

Sunucu tarafında birleştirme işi doğuyor: tek çağrı yirmi beş ayrıntıyı toplamak zorunda ve bu
toplama K01'in tepe yükünde her istekte yapılır. 01. dersin sorusu burada geri geliyor — bu
birleştirmenin hangi katmanda yapılacağı ayrı bir karardır.

Arıza yalıtımı kayboluyor: yirmi sekiz çağrılık planda bir ayrıntı çağrısının düşmesi bir satırı
eksik bırakır, tek çağrılık planda aynı arıza panelin tamamını boş bırakır. Dayanıklılık ve
Güvenilirlik kursundaki **zarif bozulma** bu yüzden tanecikliğe bağlıdır; burada yeniden
anlatılmaz, ama toplu çağrıya geçerken ödenen bedelin bir parçasıdır.

#### Özet

- Belirti hiçbir çağrı ölçütünde görünmez: her çağrı hızlıdır, toplam yararlı yük küçüktür, açılış
  yine de yavaştır.
- Ayırt eden ölçüm taşınan baytın işe yarayan payıdır; kaybın büyük bileşeni nedeni adlandırır.
  k = 25'te G1 için zarf payı 0,4661 (gevezelik), k = 3'te G3 için kullanılmayan yük payı 0,6177
  (aşırı paketleme).
- Düzeltmeler karşıttır: zarf payı büyükse çağrılar birleştirilir, kullanılmayan yük payı büyükse
  ayrılır. Toplam bayt tek başına ikisini ayırmaz.
- Takasın dönüm noktası ölçülür: kullanım oranı 12/25 = 0,48'in altında çok sayıda küçük çağrı az
  taşır (bir satırda oran 0,395), üstünde tek çağrı kazanır (yirmi beş satırda 1,713).
- Tur sayısı baytla aynı yöne bakmaz: G1 yirmi beş satırda 6 tur alır, KK3'ün altı çağrılık
  eşzamanlılık sınırı yüzünden.
- Yüksek isabet az bayt demek değildir: G1'in isabeti 0,9379 ama taşıdığı bayt G3'ün 3,94 katı,
  çünkü taşınanın yüzde 91,86'sı zarftır ve koşullu istek de zarf öder.

#### Sonraki Adım

Toplu çağrı taşınanı üçte bire indirdi, ama taşıdığı şeyin **içine** hiç bakılmadı. Yirmi beş
ayrıntı 6.459 bayt tutuyor ve bu baytın içinde panelin hiç okumadığı alanlar var: her olayın
taşıyıcı kodu, bölge kimliği, tam zaman damgası. Kullanılmayan yük payı ölçüsü bunları göremez,
çünkü o ölçü kayıt tanecikliğinde çalışır; israf **alan** tanecikliğinde de olur. Sonraki ders o
katmanı ele alır: ihtiyaçtan fazla okunan alanın uçtan uca ne büyüttüğü — önbelleğe sığan giriş,
isabet oranı, depoya ulaşan istek, seri hâle getirilen alan ve ağ çıkışı — ve gereksiz baytı
kesmenin karşılığında hangi kararın sertleştiği.

### Gereksiz Veri Çekme

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/gereksiz-veri-cekme

Aşırı veri çekmenin uçtan uca ölçülmesi: geniş kaydın önbelleğe sığan giriş sayısını düşürmesi, aynı isabet düşüşünü anahtar çeşitlenmesinin de vermesi, ikisini çalışma kümesinin iki çarpanına ayıran ölçüm ve fazla alan taşımanın haklı olduğu sınırın bir çağrının zarfıyla sınırlanması.

Toplu çağrı taşınanı üçte bire indirdi, ama taşıdığı şeyin içine bakılmadı. Önceki dersin ölçüsü
kayıt tanecikliğinde çalışıyordu: bir kaydın gelip gelmediğini sayıyordu, kaydın **içindeki**
alanların okunup okunmadığını değil. İsraf alan tanecikliğinde de olur ve bu dersin konusu odur.

Katalogdaki başlık **Gereksiz Veri Çekme**dir; gövdede kullanılan terim **aşırı veri çekme**
(over-fetching), Backend müfredatındaki Veri Erişim Katmanı kursuyla aynı biçimdir. Orada kavram
kuruldu ve baytı ölçüldü: sütun seçimi, satır sınırlaması, yıldız seçiminin sessiz bedeli. Burada
o mekanik tekrarlanmaz. Buradaki soru sistem tasarımı sorusudur: **gereksiz bayt, deponun
dışındaki katmanlarda neyi büyütür ve kesildiğinde ne sertleşir.**

#### Belirti: İsabet Düşüyor, İstek Sayısı Sabit

Belirti kenar önbelleğinde görünüyor. K02 okumaların onda dokuzunu kenarda karşılıyordu; K01'in
V9 varsayımı da 0,90 isabet öngörüyor ve buna göre depoya ulaşan okuma saniyede 41,67. Ölçülen
isabet 0,90'ın altına indi, depoya ulaşan okuma büyüdü. Uçtaki istek sayısı değişmedi.

**Neden A — kayıt genişledi.** Aynı sayıda giriş, her biri daha çok bayt. Önbelleğe sığan giriş
sayısı düştü, tahliye arttı.

**Neden B — anahtar çeşitlendi.** Kayıt aynı boyutta, ama aynı gönderi için birden çok anahtar
üretiliyor: dil, biçim ya da tüketiciye göre ayrışan yanıtlar. Giriş sayısı aynı kaldı, çalışma
kümesi büyüdü.

İkisi de aynı belirtiyi verir ve düzeltmeleri farklı yerdedir: biri yanıtın **genişliğini**,
öteki yanıtın **çeşidini** ilgilendirir.

#### Ölçüm Düzeneği

Önbellek gerçek bir en az kullanılanı atan (LRU) düzendir ve bayt kapasitesiyle sınırlıdır. İstek
izi tohumlu bir Zipf üretecinden geliyor; birkaç takip numarası çok, çoğu az sorgulanıyor.

**KK4 — kenar önbelleğinin bu kayıt türüne ayırdığı bellek: 1 MiB (model ölçeğinde).** Gerekçe:
yirmi bin gönderilik evrenin tamamı sığmamalı, yoksa tahliye hiç olmaz ve genişliğin bedeli
görünmez. Duyarlılığı iki ve dört katıyla son tabloda verildi.

```js
// alan/uctan-uca.mjs — gereksiz baytin ucdan uca bedeli: onbellege sigan giris, isabet,
// depoya ulasan istek ve ag cikisi. Onbellek gercek bir LRU, iz tohumlu bir Zipf uretecinden.
const EVREN = 20_000, IZ = 200_000, KAPASITE = 1 << 20;   // KK4: onbellek 1 MiB (model olcegi)
const OLAY = 7;                                           // K01 V4

const kayit = (i) => ({
  no: `TR-2026-${String(i).padStart(9, "0")}`, satici: i % 20, gun: i % 30,
  tarife_id: i % 40, bolge_id: i % 12, agirlik: 1 + (i % 30), hacim: 10 + (i % 90),
  sozlesme: `SZ-${i % 20}`, oran: 12 + (i % 7) * 0.5, katsayi: 1 + (i % 5) * 0.1,
  indirim: (i % 9) * 0.01, olusma: 1700000000 + i * 37, guncelleme: 1700000000 + i * 41,
  durum: i % 5, tasiyici: `TSY-${i % 6}`, rota_kodu: `R-${i % 40}-${i % 12}`,
  teslim_penceresi: `${8 + (i % 4)}:00-${12 + (i % 4)}:00`,
  olaylar: Array.from({ length: OLAY }, (_, j) => ({
    kod: (i * 3 + j) % 5, ts: 1700000000 + i * 41 + j * 900, bolge: (i + j) % 12,
    rota: `R-${(i + j) % 40}`, tasiyici: `TSY-${(i + j) % 6}`, sira: j,
    aciklama: `adim ${j} kayit alindi merkez ${(i + j) % 12}` })),
});
const AD = ["kabul", "aktarma", "dagitimda", "teslim", "bekliyor"];
const BICIM = {
  tam: (r) => r,
  orta: (r) => ({ no: r.no, durum: r.durum, bolge_id: r.bolge_id, guncelleme: r.guncelleme,
    tasiyici: r.tasiyici, rota_kodu: r.rota_kodu, agirlik: r.agirlik,
    olaylar: r.olaylar.slice(-3).map((o) => ({ kod: o.kod, ts: o.ts, rota: o.rota,
      bolge: o.bolge, aciklama: o.aciklama })) }),
  ihtiyac: (r) => ({ no: r.no, durum: AD[r.durum], bolge: r.bolge_id, guncelleme: r.guncelleme,
    olaylar: r.olaylar.slice(-3).map((o) => ({ kod: o.kod, ts: o.ts, rota: o.rota })) }),
};
const alanSay = (x) => Array.isArray(x) ? x.reduce((a, y) => a + alanSay(y), 0)
  : (x && typeof x === "object") ? Object.values(x).reduce((a, y) => a + alanSay(y), 0) : 1;

function iz() {                                           // tohumlu Zipf: populer takip numarasi
  let s = 20260801 % 2147483647;
  const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
  const w = Array.from({ length: EVREN }, (_, i) => 1 / (i + 1));
  const kum = w.reduce((a, x, i) => (a.push((a[i - 1] ?? 0) + x), a), []);
  const top = kum[EVREN - 1];
  const cek = () => { const h = rast() * top; let a = 0, b = EVREN - 1;
    while (a < b) { const m = (a + b) >> 1; if (kum[m] < h) a = m + 1; else b = m; } return a; };
  return Array.from({ length: IZ }, cek);
}
const IZ_DIZI = iz();

function kosum(bicim, cesit, kapasite = KAPASITE) {        // cesit: ayni kayit kac ayri anahtar uretiyor
  const onbellek = new Map();                             // LRU: Map ekleme sirasini korur
  let dolu = 0, isabet = 0, tasinan = 0, seriAlan = 0;
  for (let t = 0; t < IZ_DIZI.length; t += 1) {
    const i = IZ_DIZI[t], anahtar = `${i}|${t % cesit}`;
    const govde = BICIM[bicim](kayit(i));
    const b = Buffer.byteLength(JSON.stringify(govde));
    tasinan += b;
    if (onbellek.has(anahtar)) { isabet += 1; onbellek.delete(anahtar); onbellek.set(anahtar, b); continue; }
    seriAlan += alanSay(govde);                           // isabetsizlikte seri hale getiriliyor
    while (dolu + b > kapasite && onbellek.size > 0) {
      const [ilk, ib] = onbellek.entries().next().value;
      onbellek.delete(ilk); dolu -= ib;
    }
    onbellek.set(anahtar, b); dolu += b;
  }
  const bayt = Buffer.byteLength(JSON.stringify(BICIM[bicim](kayit(1))));
  return { bayt, alan: alanSay(BICIM[bicim](kayit(1))), cesit,
    giris: Math.floor(kapasite / bayt), isabet: isabet / IZ, seriAlan, tasinan };
}

const OKUMA = 416.67, K01_ISABET = 0.9;                   // K01: tepe okuma istek/s, V9
const SENARYO = [["tam yanit", "tam", 1], ["orta yanit", "orta", 1],
  ["ihtiyac kadar", "ihtiyac", 1], ["ihtiyac, 4 cesitli anahtar", "ihtiyac", 4]];
const say = (x) => x.toLocaleString("tr-TR");
console.log(`model: ${say(EVREN)} gonderi, ${say(IZ)} istek (tohumlu Zipf, tohum 20260801), ` +
  `onbellek ${KAPASITE / 1024} KiB; K01: V5 takip yaniti 480 bayt, V7 gonderi kaydi 900 bayt`);
console.log(`\n${"senaryo".padEnd(28)}${"kayit bayt".padStart(12)}${"alan".padStart(6)}` +
  `${"sigan giris".padStart(13)}${"anahtar cesidi".padStart(16)}${"isabet".padStart(9)}` +
  `${"depoya istek/s".padStart(16)}${"ag cikisi Mbit/s".padStart(18)}`);
const r = {};
for (const [ad, bicim, cesit] of SENARYO) {
  const o = kosum(bicim, cesit);
  r[ad] = o;
  console.log(ad.padEnd(28) + say(o.bayt).padStart(12) + String(o.alan).padStart(6) +
    say(o.giris).padStart(13) + String(cesit).padStart(16) + o.isabet.toFixed(4).padStart(9) +
    (OKUMA * (1 - o.isabet)).toFixed(2).padStart(16) +
    ((OKUMA * o.bayt * 8) / 1e6).toFixed(3).padStart(18));
}
console.log(`K01 varsayimi: isabet ${K01_ISABET} -> depoya ${(OKUMA * (1 - K01_ISABET)).toFixed(2)} istek/s`);

const t = r["tam yanit"], i = r["ihtiyac kadar"], c = r["ihtiyac, 4 cesitli anahtar"];
console.log(`\nayirt eden olcum — calisma kumesi = anahtar cesidi x kayit bayt`);
console.log(`${"senaryo".padEnd(28)}${"anahtar basina bayt".padStart(21)}${"cesit".padStart(7)}` +
  `${"kume bayt".padStart(12)}${"ihtiyaca gore".padStart(15)}`);
for (const ad of ["tam yanit", "ihtiyac kadar", "ihtiyac, 4 cesitli anahtar"])
  console.log(ad.padEnd(28) + say(r[ad].bayt).padStart(21) + String(r[ad].cesit).padStart(7) +
    say(r[ad].bayt * r[ad].cesit).padStart(12) +
    ((r[ad].bayt * r[ad].cesit) / i.bayt).toFixed(2).padStart(15));

console.log(`\nucdan uca fark (tam -> ihtiyac): kayit ${say(t.bayt)} -> ${say(i.bayt)} bayt ` +
  `(${(t.bayt / i.bayt).toFixed(2)} kat), sigan giris ${say(t.giris)} -> ${say(i.giris)}, ` +
  `isabet ${t.isabet.toFixed(4)} -> ${i.isabet.toFixed(4)}, depoya ` +
  `${(OKUMA * (1 - t.isabet)).toFixed(2)} -> ${(OKUMA * (1 - i.isabet)).toFixed(2)} istek/s`);
console.log(`seri hale getirilen alan (isabetsizlikte): ${say(t.seriAlan)} -> ${say(i.seriAlan)} ` +
  `(${(t.seriAlan / i.seriAlan).toFixed(2)} kat); ag cikisi ` +
  `${((OKUMA * t.bayt * 8) / 1e6).toFixed(3)} -> ${((OKUMA * i.bayt * 8) / 1e6).toFixed(3)} Mbit/s`);
console.log(`anahtar cesitlenmesinin bedeli (ihtiyac -> 4 cesit): isabet ${i.isabet.toFixed(4)} -> ` +
  `${c.isabet.toFixed(4)}, depoya ${(OKUMA * (1 - i.isabet)).toFixed(2)} -> ` +
  `${(OKUMA * (1 - c.isabet)).toFixed(2)} istek/s, ag cikisi degismedi`);

const ZARF = 280;                                         // 03. ders: bir cagrinin zarfi
console.log(`\nfazla alan tasimanin hakli oldugu sinir: bir cagriyi ortadan kaldiriyorsa fazlalik ` +
  `${ZARF} bayttan (03. dersin zarfi) kucuk olmali; olculen fazlalik ` +
  `orta ${say(r["orta yanit"].bayt - i.bayt)} bayt, tam ${say(t.bayt - i.bayt)} bayt`);

console.log(`\nKK4 duyarliligi (onbellek ${KAPASITE / 1024} KiB -> 2 ve 4 kati) isabet`);
console.log(`${"senaryo".padEnd(28)}${"1 kat".padStart(9)}${"2 kat".padStart(9)}${"4 kat".padStart(9)}`);
for (const [ad, bicim, cesit] of SENARYO)
  console.log(ad.padEnd(28) + [1, 2, 4]
    .map((k) => kosum(bicim, cesit, KAPASITE * k).isabet.toFixed(4).padStart(9)).join(""));
```

```
model: 20.000 gonderi, 200.000 istek (tohumlu Zipf, tohum 20260801), onbellek 1024 KiB; K01: V5 takip yaniti 480 bayt, V7 gonderi kaydi 900 bayt

senaryo                       kayit bayt  alan  sigan giris  anahtar cesidi   isabet  depoya istek/s  ag cikisi Mbit/s
tam yanit                          1.124    66          932               1   0.6028          165.49             3.747
orta yanit                           409    22        2.563               1   0.7226          115.58             1.363
ihtiyac kadar                        207    13        5.065               1   0.8042           81.59             0.690
ihtiyac, 4 cesitli anahtar           207    13        5.065               4   0.6348          152.15             0.690
K01 varsayimi: isabet 0.9 -> depoya 41.67 istek/s

ayirt eden olcum — calisma kumesi = anahtar cesidi x kayit bayt
senaryo                       anahtar basina bayt  cesit   kume bayt  ihtiyaca gore
tam yanit                                   1.124      1       1.124           5.43
ihtiyac kadar                                 207      1         207           1.00
ihtiyac, 4 cesitli anahtar                    207      4         828           4.00

ucdan uca fark (tam -> ihtiyac): kayit 1.124 -> 207 bayt (5.43 kat), sigan giris 932 -> 5.065, isabet 0.6028 -> 0.8042, depoya 165.49 -> 81.59 istek/s
seri hale getirilen alan (isabetsizlikte): 5.242.710 -> 509.093 (10.30 kat); ag cikisi 3.747 -> 0.690 Mbit/s
anahtar cesitlenmesinin bedeli (ihtiyac -> 4 cesit): isabet 0.8042 -> 0.6348, depoya 81.59 -> 152.15 istek/s, ag cikisi degismedi

fazla alan tasimanin hakli oldugu sinir: bir cagriyi ortadan kaldiriyorsa fazlalik 280 bayttan (03. dersin zarfi) kucuk olmali; olculen fazlalik orta 202 bayt, tam 917 bayt

KK4 duyarliligi (onbellek 1024 KiB -> 2 ve 4 kati) isabet
senaryo                         1 kat    2 kat    4 kat
tam yanit                      0.6028   0.6844   0.7684
orta yanit                     0.7226   0.8056   0.8834
ihtiyac kadar                  0.8042   0.8822   0.9157
ihtiyac, 4 cesitli anahtar     0.6348   0.7113   0.7788
```

Sayılar **ölçüm** sınıfındadır ve tohuma bağlıdır; K01 ölçeğine çevrilen istek/s ve Mbit/s
sütunları **hesap**tır. Modelin ihtiyaç kadar yanıtı 207 bayt çıkıyor, K01'in V5 varsayımı 480
baytlıktı — varsayım daha tutucuydu ve taşınabilir olan mutlak bayt değil, senaryolar arası
orandır.

#### Kayıt Genişlemesi ile Anahtar Çeşitlenmesi Aynı Belirtiyi Veriyor

İlk tablonun birinci ve dördüncü satırı bu dersin bütün meselesidir. Tam yanıtta isabet 0,6028 ve
depoya ulaşan okuma saniyede 165,49. İhtiyaç kadar ama dört çeşit anahtar üreten yanıtta isabet
0,6348 ve depoya 152,15. **İki senaryo aynı belirtiyi veriyor** — isabet 0,90'ın çok altında,
depoya ulaşan okuma K01'in öngördüğü 41,67'nin dört katı — ama biri geniş kayıttan, öteki
çeşitlenmeden geliyor.

Ayırt eden ölçüm ikinci tablodadır ve tek bir çarpımdır: **çalışma kümesi = anahtar çeşidi ×
anahtar başına bayt.** İhtiyaç kadar yanıt taban alındığında tam yanıtın kümesi 5,43 kat, dört
çeşitli yanıtın kümesi 4,00 kat. İki çarpandan hangisinin büyüdüğü nedeni söyler: tam yanıtta
bayt (1.124'e karşı 207), dört çeşitli yanıtta çeşit (4'e karşı 1).

Üçüncü bir sütun tanıyı doğruluyor. Ağ çıkışı tam yanıtta 3,747 Mbit/s, dört çeşitli yanıtta
0,690 Mbit/s — değişmemiş. **Aynı isabet düşüşü, çok farklı ağ çıkışı.** Yalnız isabete bakan bir
gösterge iki nedeni ayıramaz; kümenin iki çarpanı ayırır.

#### Gereksiz Baytın Dört Ayrı Yerdeki Bedeli

Tam yanıttan ihtiyaç kadar yanıta geçiş dört büyüklüğü birden oynatıyor ve dördü aynı oranda
değişmiyor.

Kayıt 1.124 bayttan 207 bayta iniyor: 5,43 kat. Önbelleğe sığan giriş 932'den 5.065'e çıkıyor;
oran yine 5,43 çünkü kapasite bayt cinsindendir. İsabet 0,6028'den 0,8042'ye çıkıyor — bu oran
5,43 değil, çünkü isabet giriş sayısına doğrusal bağlı değil, popülerlik dağılımına bağlı. Depoya
ulaşan okuma 165,49'dan 81,59 istek/s'ye iniyor: 2,03 kat.

Seri hâle getirilen alan sayısı 5.242.710'dan 509.093'e iniyor: **10,30 kat.** Bu, kayıt
oranının (5,43) neredeyse iki katıdır ve nedeni birleşiktir: hem kayıt başına alan sayısı 66'dan
13'e inmiş, hem de isabetsizlik seyrelmiştir. Seri hâle getirme işi iki çarpanla birden azalıyor.

Ağ çıkışı 3,747 Mbit/s'den 0,690 Mbit/s'ye iniyor, tam olarak 5,43 kat. K01'in tepe okuma çıkışı
1,6 Mbit/s'ti; tam yanıt onu 2,34 katına çıkarır.

KK4 duyarlılık tablosu bir uyarı taşıyor: önbelleği dört katına çıkarmak tam yanıtın isabetini
0,6028'den 0,7684'e getiriyor, ama ihtiyaç kadar yanıtın 1 kattaki 0,8042'sine bile yetişemiyor.
**Bellek eklemek genişliği kapatmıyor.**

#### Fazla Alan Taşımanın Haklı Olduğu Sınır

Fazla alan taşımak her zaman israf değildir; bir çağrıyı ortadan kaldırıyorsa hesabı vardır ve
hesabın eşiği önceki dersten geliyor. Bir çağrının zarfı 280 bayttı. Öyleyse kural şudur:
**taşınan fazlalık, ortadan kaldırdığı çağrının zarfından küçükse haklıdır.**

Ölçüm eşiği iki tarafa da uyguluyor. Orta yanıtın fazlalığı 202 bayt; 280'in altında, yani bu
alanlar ikinci bir çağrıyı gereksizleştiriyorsa taşınmaları kârlıdır. Tam yanıtın fazlalığı 917
bayt; üç zarftan büyük, yani üç ayrı çağrıyı ortadan kaldırmadıkça savunulamaz.

Sınır ölçeğe bağlıdır ve ölçek değişince yön değişir. Zarf sıkıştırılmış bir protokolde küçülürse
eşik düşer ve orta yanıt da savunulamaz hâle gelir; bir kaydın popülerliği çok yüksekse önbellekte
zaten duruyordur ve fazlalığın isabet bedeli sıfıra yaklaşır. Karşıt kalıp bir yasak değil, bu iki
sayının karşılaştırılmasıdır.

#### Karşılığında Büyüyen

Alanı daraltmanın karşılığı dördüncü satırda duruyor ve tuzak da orada. Yanıtı bir tüketicinin
tam ihtiyacına göre kesmek, ikinci tüketiciyi karşılamaz; ikinci tüketici kendi alan kümesini
ister ve ortaya ikinci bir çeşit çıkar. Dört tüketici dört çeşit demektir ve ölçüm bunun bedelini
veriyor: isabet 0,8042'den 0,6348'e, depoya ulaşan okuma 81,59'dan 152,15 istek/s'ye.

**Neden A'nın düzeltmesi Neden B'yi doğurur.** Daraltma sonsuza kadar sürdürülemez; bir noktada
tek bir orta yanıt, dört dar yanıttan ucuza gelir. Ölçüm hangisinin ucuz olduğunu söyler: orta
yanıt tek çeşitle 0,7226 isabet ve 115,58 istek/s verirken, dar yanıt dört çeşitle 0,6348 ve
152,15 verir. Bu model ölçeğinde **orta yanıt kazanıyor**.

İkinci karşılık sözleşmededir ve sayılmaz: dar yanıt, tüketicinin bugünkü ihtiyacını yanıtın
biçimine yazar. Yeni bir alan her istendiğinde ya yanıt genişler ya yeni bir çeşit doğar; ikisinin
de bedeli yukarıdaki tablodadır.

#### Özet

- Belirti kenar önbelleğindedir: istek sayısı sabitken isabet K01'in V9 varsayımı olan 0,90'ın
  altına iniyor ve depoya ulaşan okuma 41,67 istek/s'nin katlarına çıkıyor.
- Aynı belirtiyi kayıt genişlemesi (isabet 0,6028) ve anahtar çeşitlenmesi (0,6348) verir; ayırt
  eden ölçüm çalışma kümesinin iki çarpanıdır — tam yanıtta bayt 5,43 kat, dört çeşitli yanıtta
  çeşit 4,00 kat. Ağ çıkışı doğrulamayı yapar: 3,747'ye karşı 0,690 Mbit/s.
- Gereksiz bayt dört yeri birden büyütür ve aynı oranda değil: kayıt 5,43 kat, sığan giriş 5,43
  kat, depoya ulaşan okuma 2,03 kat, seri hâle getirilen alan 10,30 kat.
- Bellek eklemek genişliği kapatmaz: önbellek dört katına çıktığında tam yanıtın isabeti 0,7684
  oluyor, ihtiyaç kadar yanıtın tek kattaki 0,8042'sine yetişmiyor.
- Fazlalık, ortadan kaldırdığı çağrının zarfından (280 bayt) küçükse haklıdır: orta yanıtın
  fazlalığı 202 bayt, tam yanıtınki 917 bayt.
- Daraltmanın karşılığı çeşitlenmedir: her tüketiciye ayrı dar yanıt vermek isabeti 0,8042'den
  0,6348'e indiriyor ve bu model ölçeğinde tek bir orta yanıt (0,7226) daha ucuza geliyor.

#### Sonraki Adım

Buraya kadarki dört ölçüm de aynı yerde duruyordu: bir isteğin taşıdığı veri. Hesabın katmanı,
istemcinin işi, çağrının taneciği ve alanın genişliği — hepsi **taşınan** şeyle ilgiliydi. Oysa
bir isteğin maliyetinin bir parçası hiç taşınmaz. İstek, kendisine hizmet edecek nesneler
kurulmadan başlayamaz: depo bağlantısı, seri hâle getirici, imza doğrulayıcı, biçim çözümleyici.
Bu nesnelerin ne zaman kurulduğu bugüne kadar hiç sorulmadı; hepsinin hazır durduğu varsayıldı.
Sonraki ders bu varsayımı ölçer: pahalı bir istemcinin her istekte yeniden yaratılması, nesne
başına kurulum işinin tekrar sayısıyla çarpımı ve havuzun karşılığında büyüttüğü şey.

### Hatalı Nesne Yaratımı

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/hatali-nesne-yaratimi

Pahalı istemcilerin her istekte yeniden yaratılması: eşzamanlılık artarken verimin artmamasının iki karşıt nedeni, yaratım oranının düzeltmenin yönünü seçmesi, kurulum işinin tekrar sayısıyla erimesi ve havuzun karşılığında tuttuğu nesne-turun bekleyen istek-tura karşı tartılması.

Buraya kadarki dört ölçüm bir isteğin **taşıdığı** veriyi saydı. Bir isteğin maliyetinin bir
parçası ise hiç taşınmaz: istek, kendisine hizmet edecek nesneler kurulmadan başlayamaz. Depo
bağlantısı, seri hâle getirici, imza doğrulayıcı, biçim çözümleyici. Bu nesnelerin ne zaman
kurulduğu bugüne kadar sorulmadı; hepsinin hazır durduğu varsayıldı.

Bağlantı havuzunun kendisi Veritabanı İşletimi ve Servis Tasarımı kurslarında kuruldu; havuz
boyutunun bağlantı sınırına bağlılığı orada ölçüldü. Burada o mekanik tekrarlanmaz. Buradaki soru
tanıdır: **hangi belirti nesne yaratımını gösterir, aynı belirtiyi başka ne verir ve ikisini hangi
sayı ayırır.**

#### Belirti: Verim Eşzamanlılıkla Artmıyor

Belirti K01'in yazma yolunda görünüyor. Depoya ulaşan tepe istek hızı saniyede 138,89. İşçi süreç
eklendikçe verim artmıyor; her isteğin yaptığı iş sabit — sınır oranı 1,00, taşınan bayt
değişmemiş, alan sayısı aynı. Önceki dört ölçüt de temiz.

**Neden A — nesne her istekte yeniden yaratılıyor.** Kurulum işi istek sayısıyla çarpılıyor ve
işlemcinin turdaki bütçesini yiyor. **Hatalı nesne yaratımı** (improper instantiation) budur.

**Neden B — tek bir paylaşılan nesne var.** Hiç yeniden yaratılmıyor, ama aynı anda tek istek
kullanabiliyor. Verim nesnenin tutuş süresine kilitlenmiş.

İkisi de "verim düz" belirtisini verir ve düzeltmeleri birbirinin tersidir: biri **daha az**
yaratmayı, öteki **daha çok** yaratmayı ister. Yanlış yöne gidilirse belirti kötüleşir.

#### Ölçüm Düzeneği

Aşağıdaki koşum bir **modeldir**: tur soyut bir adımdır, süre ölçülmez. Sayılan şey yaratım
sayısı, iş birimi, bekleyen istek-tur ve tutulan nesne-turdur. Varışlar tohumlu bir üreteçten
Poisson gelir.

**KK5 — bir turda yapılabilen iş 100 birim; nesne kurulumu 60, kullanımı 10 birim; nesnenin tutuş
süresi 3 tur; alt kaynağın kabul ettiği eşzamanlı nesne 24.** Gerekçe: kurulum el sıkışma, kimlik
doğrulama ve tampon ayırmayı içerir, kullanım tek bir çağrının hazırlanmasıdır, tutuş alt sistemin
yanıt beklemesidir; son sayı her nesnenin alt tarafta bir oturum tuttuğunu ve bunun sonsuz
olmadığını söyler. Duyarlılığı son tabloda kurulum maliyeti üzerinden verildi. Yirmi dört nesnelik
sınır bu koşumda bağlayıcı olmuyor.

```js
// nesne/yaratim.mjs — pahali istemcinin her istekte yeniden yaratilmasi. MODELDIR: tur soyut
// bir adimdir, sure olculmez; sayilan sey yaratim, is birimi, bekleyen istek-tur ve tutulan
// nesne-turdur. Varislar tohumlu bir uretecten Poisson.
const T = 4000, CEVRIM = 100;                    // 4000 tur, 100 tur = 1 s
const IS = 100, KUR = 60, KUL = 10, TUTUS = 3;   // KK5: tur basina is butcesi ve nesne maliyetleri
const SINIR = 24;                                // KK5: alt kaynagin kabul ettigi eszamanli nesne
const TEPE = 138.89, ORT = 46.30;                // K01: depoya ulasan tepe ve ortalama istek/s

function kosum({ havuz, lam }) {                 // havuz: null ise her istekte yeni nesne
  let s = 20260801 % 2147483647;
  const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
  const poisson = (l) => { const L = Math.exp(-l); let k = 0, p = 1;
    do { k += 1; p *= rast(); } while (p > L); return k - 1; };
  const kuyruk = [];                             // bekleyen istek sayisi
  let bosta = 0, acik = 0;                       // havuzda bos duran nesne, alt kaynakta acik nesne
  const mesgul = [];                             // { bitis } — kullanimdaki nesneler
  const c = { gelen: 0, biten: 0, yaratim: 0, bekleyen: 0, tutulan: 0,
    kurulumIs: 0, kullanimIs: 0 };

  for (let t = 0; t < T; t += 1) {
    for (let n = mesgul.length - 1; n >= 0; n -= 1) if (mesgul[n].bitis <= t) {
      mesgul.splice(n, 1);
      if (havuz === null) acik -= 1; else bosta += 1;     // havuzsuz duzen nesneyi atar
    }
    const gelen = poisson(lam);
    c.gelen += gelen;
    for (let n = 0; n < gelen; n += 1) kuyruk.push(t);
    let butce = IS;
    while (kuyruk.length > 0) {
      let nesne = false;
      if (havuz === null) {                               // her istekte yarat
        if (acik >= SINIR) break;
        if (butce < KUR + KUL) break;
        butce -= KUR; c.kurulumIs += KUR; c.yaratim += 1; acik += 1; nesne = true;
      } else if (bosta > 0) {                             // havuzdan al
        bosta -= 1; nesne = true;
      } else if (acik < Math.min(havuz, SINIR)) {         // havuzu buyut
        if (butce < KUR + KUL) break;
        butce -= KUR; c.kurulumIs += KUR; c.yaratim += 1; acik += 1; nesne = true;
      }
      if (!nesne) break;
      if (butce < KUL) break;
      butce -= KUL; c.kullanimIs += KUL;
      kuyruk.shift(); c.biten += 1; mesgul.push({ bitis: t + TUTUS });
    }
    c.bekleyen += kuyruk.length;
    c.tutulan += acik;
  }
  return c;
}

const say = (x) => x.toLocaleString("tr-TR");
const PLAN = [["her istekte yarat", null], ["tek paylasilan nesne", 1],
  ["havuz 5", 5], ["havuz 12", 12], ["havuz 24", 24]];
console.log(`model: ${T} tur (${T / CEVRIM} s), tur basina is butcesi ${IS} birim, nesne kurulumu ` +
  `${KUR} + kullanimi ${KUL} birim, tutus ${TUTUS} tur, alt kaynak siniri ${SINIR} nesne (KK5); ` +
  `varislar Poisson, tohum 20260801`);

for (const [etiket, lam] of [["ortalama", ORT / CEVRIM], ["tepe", TEPE / CEVRIM]]) {
  console.log(`\n${etiket} yuk: ${(lam * CEVRIM).toFixed(2)} istek/s (${lam.toFixed(4)} varis/tur)`);
  console.log(`${"plan".padEnd(22)}${"biten".padStart(8)}${"verim/s".padStart(10)}` +
    `${"yaratim".padStart(9)}${"yaratim orani".padStart(15)}${"tekrar".padStart(9)}` +
    `${"kurulum is payi".padStart(17)}${"bekleyen istek-tur".padStart(20)}${"tutulan nesne-tur".padStart(19)}`);
  for (const [ad, havuz] of PLAN) {
    const k = kosum({ havuz, lam });
    const tekrar = k.biten / Math.max(k.yaratim, 1);
    console.log(ad.padEnd(22) + say(k.biten).padStart(8) + (k.biten / (T / CEVRIM)).toFixed(2).padStart(10) +
      say(k.yaratim).padStart(9) + (k.yaratim / Math.max(k.biten, 1)).toFixed(4).padStart(15) +
      tekrar.toFixed(2).padStart(9) +
      (k.kurulumIs / Math.max(k.kurulumIs + k.kullanimIs, 1)).toFixed(4).padStart(17) +
      say(k.bekleyen).padStart(20) + say(k.tutulan).padStart(19));
  }
}

const t1 = kosum({ havuz: null, lam: TEPE / CEVRIM }), t5 = kosum({ havuz: 5, lam: TEPE / CEVRIM });
const t24 = kosum({ havuz: 24, lam: TEPE / CEVRIM });
console.log(`\ntavanlar (hesap, tur basina tam istek): her istekte yarat ` +
  `${Math.floor(IS / (KUR + KUL)) * CEVRIM} istek/s; isinmis havuz p ile ` +
  `${(CEVRIM / TUTUS).toFixed(2)} x p, is butcesi tavani ${Math.floor(IS / KUL) * CEVRIM} istek/s; ` +
  `alt kaynak siniri ${((SINIR / TUTUS) * CEVRIM).toFixed(2)} istek/s (p=5 icin ${((5 / TUTUS) * CEVRIM).toFixed(2)})`);
console.log(`tepe yukte: yaratim ${say(t1.yaratim)} -> ${say(t5.yaratim)} ` +
  `(${(t1.yaratim / Math.max(t5.yaratim, 1)).toFixed(1)} kat az), kurulum isi ` +
  `${say(t1.kurulumIs)} -> ${say(t5.kurulumIs)} birim, bekleyen istek-tur ` +
  `${say(t1.bekleyen)} -> ${say(t5.bekleyen)}`);
console.log(`havuzun bedeli: tutulan nesne-tur havuz 5'te ${say(t5.tutulan)}, havuz 24'te ` +
  `${say(t24.tutulan)} (${(t24.tutulan / t5.tutulan).toFixed(2)} kat); havuz 24 verimi ` +
  `${(t24.biten / (T / CEVRIM)).toFixed(2)} istek/s, havuz 5 verimi ${(t5.biten / (T / CEVRIM)).toFixed(2)}`);

console.log(`\nkurulum isinin payi tekrarla nasil eriyor (hesap): pay = ${KUR} / (${KUR} + tekrar x ${KUL})`);
console.log(`${"tekrar".padStart(8)}${"kurulum is payi".padStart(17)}${"istek basina is".padStart(17)}`);
for (const tekrar of [1, 2, 5, 10, 50, 200])
  console.log(String(tekrar).padStart(8) + (KUR / (KUR + tekrar * KUL)).toFixed(4).padStart(17) +
    (KUR / tekrar + KUL).toFixed(2).padStart(17));

console.log(`\nkurulum maliyeti degisirse havuzun kazanci (hesap, tekrar 200)`);
console.log(`${"kurulum".padStart(8)}${"tekrar 1".padStart(10)}${"tekrar 200".padStart(12)}` +
  `${"kazanc kat".padStart(12)}${"kurtarilan birim".padStart(18)}`);
for (const kur of [5, 10, 30, 60]) {
  const bir = kur + KUL, ikiyuz = kur / 200 + KUL;
  console.log(String(kur).padStart(8) + bir.toFixed(2).padStart(10) + ikiyuz.toFixed(2).padStart(12) +
    (bir / ikiyuz).toFixed(2).padStart(12) + (bir - ikiyuz).toFixed(2).padStart(18));
}
```

```
model: 4000 tur (40 s), tur basina is butcesi 100 birim, nesne kurulumu 60 + kullanimi 10 birim, tutus 3 tur, alt kaynak siniri 24 nesne (KK5); varislar Poisson, tohum 20260801

ortalama yuk: 46.30 istek/s (0.4630 varis/tur)
plan                     biten   verim/s  yaratim  yaratim orani   tekrar  kurulum is payi  bekleyen istek-tur  tutulan nesne-tur
her istekte yarat        1.867     46.67    1.867         1.0000     1.00           0.8571                 866              5.598
tek paylasilan nesne     1.333     33.33        1         0.0008  1333.00           0.0045           1.141.381              3.997
havuz 5                  1.870     46.75        5         0.0027   374.00           0.0158                  29             19.966
havuz 12                 1.870     46.75        7         0.0037   267.14           0.0220                   2             27.439
havuz 24                 1.870     46.75        7         0.0037   267.14           0.0220                   2             27.439

tepe yuk: 138.89 istek/s (1.3889 varis/tur)
plan                     biten   verim/s  yaratim  yaratim orani   tekrar  kurulum is payi  bekleyen istek-tur  tutulan nesne-tur
her istekte yarat        4.000    100.00    4.000         1.0000     1.00           0.8571           3.205.927             11.997
tek paylasilan nesne     1.334     33.35        1         0.0007  1334.00           0.0045           8.539.260              4.000
havuz 5                  5.601    140.03        5         0.0009  1120.20           0.0053               6.456             19.990
havuz 12                 5.608    140.20       12         0.0021   467.33           0.0127                  28             45.257
havuz 24                 5.608    140.20       12         0.0021   467.33           0.0127                  28             45.257

tavanlar (hesap, tur basina tam istek): her istekte yarat 100 istek/s; isinmis havuz p ile 33.33 x p, is butcesi tavani 1000 istek/s; alt kaynak siniri 800.00 istek/s (p=5 icin 166.67)
tepe yukte: yaratim 4.000 -> 5 (800.0 kat az), kurulum isi 240.000 -> 300 birim, bekleyen istek-tur 3.205.927 -> 6.456
havuzun bedeli: tutulan nesne-tur havuz 5'te 19.990, havuz 24'te 45.257 (2.26 kat); havuz 24 verimi 140.20 istek/s, havuz 5 verimi 140.03

kurulum isinin payi tekrarla nasil eriyor (hesap): pay = 60 / (60 + tekrar x 10)
  tekrar  kurulum is payi  istek basina is
       1           0.8571            70.00
       2           0.7500            40.00
       5           0.5455            22.00
      10           0.3750            16.00
      50           0.1071            11.20
     200           0.0291            10.30

kurulum maliyeti degisirse havuzun kazanci (hesap, tekrar 200)
 kurulum  tekrar 1  tekrar 200  kazanc kat  kurtarilan birim
       5     15.00       10.03        1.50              4.97
      10     20.00       10.05        1.99              9.95
      30     40.00       10.15        3.94             29.85
      60     70.00       10.30        6.80             59.70
```

Koşum sayıları **ölçüm** sınıfındadır ve tohuma bağlıdır; tavanlar ve son iki tablo **hesap**tır.

#### Yaratım Oranı Yönü Seçiyor

Ayırt eden ölçüm **yaratım oranıdır**: yaratılan nesne sayısının tamamlanan istek sayısına bölümü.
Tepe yükte her istekte yaratan düzende 1,0000, tek paylaşılan nesnede 0,0007. İki plan da aynı
belirtiyi veriyor — verim gelen 138,89 istek/s'nin altında kalıyor, sırasıyla 100,00 ve 33,35 —
ama tanı zıttır.

Oran 1'e yakınsa kurulum işi her istekte yeniden ödeniyordur; düzeltme yaratımı **azaltmaktır**.
Oran sıfıra yakın ve verim yine düzse nesne yeterince yaratılmamıştır; düzeltme yaratımı
**artırmaktır**. Tek bir sayı, iki karşıt yön.

Kurulum işi payı sütunu birinci nedenin büyüklüğünü veriyor: her istekte yaratan düzende toplam
işin yüzde 85,71'i kurulumdur. İstek başına 70 birim işin 60'ı, isteğin kendi işiyle hiç ilgisi
olmayan hazırlıktır. Havuz 5'te aynı pay yüzde 0,53'e iniyor.

#### Kalıp Ortalama Yükte Görünmüyor

Ortalama yük tablosu bir uyarı taşıyor. Saniyede 46,30 istekte, her istekte yaratan düzen 46,67
istek/s veriyor; havuz 5'in 46,75'iyle neredeyse aynı. Bekleyen istek-tur 866'ya karşı 29 —
görünür ama küçük. **Ortalama yükte karşıt kalıp yok gibidir.**

Tepede tablo kopuyor. Her istekte yaratan düzenin tavanı hesapla 100 istek/s'dir: bir turda 100
birim iş vardır ve bir istek kurulumla birlikte 70 birim yer, yani tura bir istek sığar. Gelen
138,89 istek/s bu tavanın üstündedir ve fark kuyruğa yazılır: bekleyen istek-tur 866'dan
3.205.927'ye çıkıyor, **3702 kat**. Havuz 5'te aynı sayı 29'dan 6.456'ya çıkıyor, 223 kat.

Tek paylaşılan nesne her iki yükte de aynı yerde: 33,33 ve 33,35 istek/s. Tavanı yükten değil,
tutuştan gelir — tur çevrimi 100 ve tutuş 3 tur olduğuna göre tek nesne saniyede 33,33 istek
taşıyabilir. Bu plan ortalama yükte bile yetersizdir; bekleyen istek-tur 1.141.381.

#### Her İstekte Yaratmanın Doğru Olduğu Koşul

Nesneyi her istekte yaratmak bir hata değildir; hata, **pahalı** bir nesneyi her istekte
yaratmaktır ve pahalılığın ölçüsü kurulum işinin kullanım işine oranıdır.

Kurulum payı tablosu bu oranın tekrar sayısıyla nasıl eridiğini veriyor. Tekrar 1'de pay 0,8571
ve istek başına iş 70 birim; tekrar 10'da 0,3750 ve 16 birim; tekrar 200'de 0,0291 ve 10,30 birim.
Kazanç tekrarın kendisiyle değil, kurulumun büyüklüğüyle sınırlıdır: son tablo aynı tekrarda
kurulum 60 birimken 6,80 kat, 5 birimken yalnız 1,50 kat kazanıldığını söylüyor.

Koşul bir sayıyla yazılabilir: **kurulum işi kullanım işinin yarısından küçükse havuzun
kazanabileceği en büyük şey 1,50 kattır** — model ölçeğinde kurulum 5, kullanım 10 birim.
Havuz tutulan nesne-turu ve bakım yükünü de getirdiğine göre, bu ölçekte havuz kurmamak
savunulabilir. Aynı nesne kurulumu 60 birime çıktığında aynı karar 6,80 katı bırakmak olur.

#### Havuzun Karşılığında Büyüyen

Havuz iki şeyi birden büyütür ve ikisi de sayılabilir.

**Tutulan nesne-tur.** Havuz 5 tepe yükte 19.990 nesne-tur tutuyor; havuz 24, 45.257 tutuyor —
2,26 kat. Bu nesneler alt kaynakta oturum tutar ve KK5'in yirmi dört nesnelik sınırını yer. Havuz
büyütmenin bedeli budur ve bedelin karşılığı verimde görünmüyor: havuz 24 saniyede 140,20 istek
tamamlıyor, havuz 5 ise 140,03. Fark yüzde 0,12.

**Ama bekleyende görünüyor.** Havuz 5'in bekleyen istek-turu 6.456, havuz 12'nin 28. Havuz
boyutunu 5'ten 12'ye çıkarmak kaynağı 2,26 kat büyütüp bekleyeni 231 kat küçültüyor. İki sayı ters
yönde ve seçim hangisinin tavan verdiğine bağlıdır. Havuz 24, havuz 12'yle birebir aynı sonucu
veriyor — talep on ikiyi hiç aşmıyor, yani on ikiden sonrası yalnız tutulan kaynaktır.

Üçüncü karşılık sayılmaz ama görülür: havuzdan alınan bir nesne geri verilmezse havuz kalıcı
olarak küçülür. Her istekte yaratan düzende böyle bir sızıntı isteği yavaşlatır ve biter; havuzlu
düzende sistemi kalıcı olarak daraltır. Havuz, bir hatayı geçici olmaktan çıkarıp kalıcı yapar.

#### Özet

- Belirti verimin eşzamanlılıkla artmamasıdır ve iki karşıt neden aynı belirtiyi verir: her
  istekte yaratma (tepede 100,00 istek/s) ve tek paylaşılan nesne (33,35 istek/s).
- Ayırt eden ölçüm yaratım oranıdır: 1,0000 ise yaratım azaltılır, 0,0007 gibi sıfıra yakınsa
  yaratım artırılır. Tek sayı, iki karşıt yön.
- Kalıp ortalama yükte görünmez: 46,30 istek/s'de her istekte yaratan düzen 46,67, havuz 46,75
  veriyor. Tepede bekleyen istek-tur 866'dan 3.205.927'ye çıkıyor (3702 kat).
- Kurulum işinin payı tekrarla erir: tekrar 1'de 0,8571 ve istek başına 70 birim, tekrar 200'de
  0,0291 ve 10,30 birim. Havuz 5 yaratımı 4.000'den 5'e indiriyor (800 kat).
- Koşul kurulumun kullanıma oranıdır: kurulum 5, kullanım 10 birimken havuzun kazanabileceği en
  çok 1,50 kattır; kurulum 60 birimken 6,80 kat.
- Havuzun karşılığı tutulan nesne-turdur: havuz 24, havuz 5'in 2,26 katı kaynak tutup verimi
  yüzde 0,12 artırıyor; buna karşılık havuz 12 bekleyeni 6.456'dan 28'e indiriyor.

#### Sonraki Adım

Beş kalıp da aynı yerde ölçüldü: **tek bir sürecin içinde ve tek bir deponun önünde.** Hesabın
katmanı, istemcinin işi, çağrının taneciği, alanın genişliği ve nesnenin ömrü — hepsi bir isteğin
kendi israfını saydı ve hepsinin düzeltmesi o sürecin ya da o yanıtın içinde kaldı. Beşinin ortak
varsayımı hiç sorulmadı: bütün bu istekler **aynı depoya** gidiyor. Takip sorgusu, durum olayı ve
gün sonu ücretlendirme aynı motorda, aynı kaynak havuzunda, aynı bakım penceresinde. Deponun neden
tek olduğu bir tasarım kararı değil, bir varsayımdı. Sonraki ders o varsayımı belirtiden yola
çıkarak sınar: tek deponun sınırının hangi ölçümle göründüğü ve ayırmanın karşılığında neyin
büyüdüğü.

### Tek Parça Kalıcılık

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/tek-parca-kalicilik

Bütün veri sınıflarının tek depoya yığılmasının belirtiden tanınması: aynı yavaşlamayı veren iki nedenin ayrılması, okuma yolunun hiçbir şey göstermemesi, dönem taramasının kendi sınıfının 801 katı kaydın üstünden geçmesi, bir yazmanın kendi sorgusuna hizmet etmeyen iki dizini de bakması ve ayırmanın karşılığında yazma yolunun iki depoya uzaması.

Buraya kadarki beş kalıp tek bir deponun ve tek bir sürecin içindeki israfı ölçtü: yanlış katmanda
yapılan hesap, ana iş parçacığına yığılan iş, çok sayıda küçük çağrı, ihtiyaçtan fazlasının okunması
ve pahalı istemcinin yeniden yaratılması. Hepsi deponun **tek** olduğunu veri olarak aldı. Bu ders o
varsayımı sorar.

Belirti şudur: gün sonu ücretlendirme işi dört saatlik penceresini doldurmuyor, ama aynı pencerede
depoya inen yazma yolunun istek başına yaptığı iş ölçülebilir biçimde büyümüş durumda. K01'in
hesabında tepe yazma 97,22 istek/s ve bu sayı değişmedi; değişen, bir yazmanın depoda ne kadar iş
doğurduğu. **Tek parça kalıcılık** (monolithic persistence), bütün veri sınıflarının tek bir depoya
yığılmasıdır. Bu addaki `tek parça`, Mimari Biçemler kursundaki `tek parça` (monolith) ile aynı
sözcüktür ama ayrı şeydir: orada tek olan **dağıtım birimi**, burada tek olan **veri deposudur**.

#### Aynı Belirti, İki Neden

Yazma başına işin büyümesinin iki ayrı nedeni olabilir ve ikisi ölçülmeden ayrılamaz.

**Birinci neden: kayıt büyüdü.** Durum olayına yeni alanlar eklenmiştir; her yazma daha çok bayt
taşır ve daha çok yapıyı günceller. Bu, dördüncü dersin ölçtüğü israfın yazma tarafındaki karşılığıdır.

**İkinci neden: depo paylaşılıyor.** Kayıt hiç değişmemiştir. Aynı tabloya, başka bir veri sınıfının
sorgusu için bir dizin eklenmiştir ve o günden sonra her durum olayı yazması, kendi sorgusunun hiç
kullanmadığı bir yapıyı da bakmak zorundadır.

Tahmin burada işe yaramaz; ikisi de yazma yolunu ağırlaştırır ve ikisi de aynı belirtiyi verir.
Ayıran ölçüm şudur: **bir erişim yolunun dokunduğu kaydı ve baktığı dizini sınıfına göre
ayrıştırmak.** Birinci nedende bir yazmanın işi büyür ama işin tamamı kendi sınıfına hizmet eder;
ikinci nedende iş, kendi sınıfına hizmet etmeyen bir paya bölünür. Bu pay ölçülebilir.

#### İki Yerleşim

Aşağıdaki kurulum bir model değil, gerçek bir ölçümdür: aynı mantıksal veri iki yerleşime de
girilir ve sayımlar deponun kendisinden okunur. Veri Katmanı Ölçekleme kursunun federasyon kararı
burada yeniden anlatılmaz; buradaki soru, o kararın hangi belirtiden doğduğudur.

**KK6 — ölçek.** K01'in günlük hacminin binde biri, otuz günlük dönem üzerinden. Gerekçe: sınıflar
arası oranlar K01'in varsayım tablosundan gelir (V3 günde 400.000 gönderi, V4 gönderi başına 7 olay,
V13 fatura satırına 100 kalem); bölen yalnız koşum süresini kısaltır, oranları değiştirmez.
Duyarlılığı aşağıda sınıf karışımı üzerinden verilir. Bu varsayım K01'in tablosuna eklenmez.

```js
// kalicilik/depo.mjs — ayni mantiksal veri iki yerlesimde kurulur: butun siniflar tek
// tabloda ve her sinif kendi deposunda. node:sqlite ile gercek kurulur; sayimlar SQL'den
// okunur. Olcek K01'in gunluk hacminin binde biridir (KK6), donem 30 gundur.
import { DatabaseSync } from "node:sqlite";

export const KK6 = 1000;                 // olcek bolen
export const GUN = 30;                   // K01 V11: ucretlendirme donemi
export const K01 = { gonderi: 400_000, olayCarpani: 7, faturaKalemi: 100, tepeYazma: 97.22 };

export const SAYI = {
  gonderi: (K01.gonderi / KK6) * GUN,
  olay: ((K01.gonderi * K01.olayCarpani) / KK6) * GUN,
  donem: (K01.gonderi / K01.faturaKalemi / KK6) * GUN,
};

// Veri bir kez uretilir, iki yerlesime de ayni satirlar girer.
export function satirlar() {
  const c = [];
  const gunluk = { gonderi: SAYI.gonderi / GUN, olay: SAYI.olay / GUN, donem: SAYI.donem / GUN };
  for (let g = 1; g <= GUN; g += 1) {
    for (let i = 1; i <= gunluk.gonderi; i += 1) {
      const no = `TK${g}-${i}`;
      c.push({ tur: "gonderi", anahtar: no, satici: null, gun: g });
      for (let k = 1; k <= K01.olayCarpani; k += 1) c.push({ tur: "olay", anahtar: no, satici: null, gun: g });
    }
    for (let s = 1; s <= gunluk.donem; s += 1) c.push({ tur: "donem", anahtar: null, satici: `S${s}`, gun: g });
  }
  return c;
}

// Tek parca yerlesim: tek tablo, butun siniflarin sorgularina hizmet eden dizinler.
export function tekParca(veri) {
  const d = new DatabaseSync(":memory:");
  d.exec(`CREATE TABLE kayit(id INTEGER PRIMARY KEY, tur TEXT, anahtar TEXT, satici TEXT, gun INTEGER, govde BLOB);
          CREATE INDEX ix_anahtar ON kayit(anahtar);
          CREATE INDEX ix_gun ON kayit(gun);
          CREATE INDEX ix_satici ON kayit(satici)`);
  const y = d.prepare("INSERT INTO kayit(tur,anahtar,satici,gun,govde) VALUES(?,?,?,?,?)");
  d.exec("BEGIN");
  for (const r of veri) y.run(r.tur, r.anahtar, r.satici, r.gun, null);
  d.exec("COMMIT");
  return d;
}

// Ayrilmis yerlesim: her sinif kendi deposunda, yalniz kendi sorgusunun dizini.
export function ayrilmis(veri) {
  const gonderi = new DatabaseSync(":memory:");
  const olay = new DatabaseSync(":memory:");
  const donem = new DatabaseSync(":memory:");
  gonderi.exec("CREATE TABLE gonderi(anahtar TEXT PRIMARY KEY, gun INTEGER, govde BLOB)");
  olay.exec(`CREATE TABLE olay(id INTEGER PRIMARY KEY, anahtar TEXT, gun INTEGER, govde BLOB);
             CREATE INDEX ix_olay_anahtar ON olay(anahtar)`);
  donem.exec("CREATE TABLE donem(satici TEXT, gun INTEGER, govde BLOB, PRIMARY KEY(satici,gun)) WITHOUT ROWID");
  const yg = gonderi.prepare("INSERT INTO gonderi VALUES(?,?,?)");
  const yo = olay.prepare("INSERT INTO olay(anahtar,gun,govde) VALUES(?,?,?)");
  const yd = donem.prepare("INSERT INTO donem VALUES(?,?,?)");
  for (const d of [gonderi, olay, donem]) d.exec("BEGIN");
  for (const r of veri) {
    if (r.tur === "gonderi") yg.run(r.anahtar, r.gun, null);
    else if (r.tur === "olay") yo.run(r.anahtar, r.gun, null);
    else yd.run(r.satici, r.gun, null);
  }
  for (const d of [gonderi, olay, donem]) d.exec("COMMIT");
  return { gonderi, olay, donem };
}

// Bir tabloya yazarken bakilan dizin sayisi SQL'den okunur (PRAGMA index_list).
export function dizinSayisi(d, tablo) {
  return d.prepare(`SELECT count(*) n FROM pragma_index_list(?)`).get(tablo).n;
}
```

```js
// kalicilik/olcum.mjs — uc erisim yolunun iki yerlesimde dokundugu kayit ve bakilan dizin
import { KK6, GUN, K01, SAYI, satirlar, tekParca, ayrilmis, dizinSayisi } from "./depo.mjs";

const veri = satirlar();
const tek = tekParca(veri);
const ayri = ayrilmis(veri);
const sor = (d, s, ...p) => d.prepare(s).get(...p).n;

console.log(`olcek 1/${KK6}, donem ${GUN} gun -> gonderi ${SAYI.gonderi}, olay ${SAYI.olay}, donem ${SAYI.donem}`);
console.log(`tek parca deponun kayit sayisi = ${sor(tek, "SELECT count(*) n FROM kayit")}`);
console.log(`ayrilmis depolarin kayit sayisi = ${sor(ayri.gonderi, "SELECT count(*) n FROM gonderi")}` +
  ` / ${sor(ayri.olay, "SELECT count(*) n FROM olay")} / ${sor(ayri.donem, "SELECT count(*) n FROM donem")}\n`);

// S1 takip okumasi: bir takip numarasinin gonderi kaydi ve olaylari.
const NO = "TK15-100";
const s1Tek = sor(tek, "SELECT count(*) n FROM kayit WHERE anahtar=?", NO);
const s1Ayri = sor(ayri.gonderi, "SELECT count(*) n FROM gonderi WHERE anahtar=?", NO) +
  sor(ayri.olay, "SELECT count(*) n FROM olay WHERE anahtar=?", NO);

// S2 donem taramasi: 30 gunluk satici-gun kayitlari; paylasilan gun dizini butun siniflari gezer.
const s2Tek = sor(tek, "SELECT count(*) n FROM kayit WHERE gun BETWEEN 1 AND ?", GUN);
const s2Ayri = sor(ayri.donem, "SELECT count(*) n FROM donem WHERE gun BETWEEN 1 AND ?", GUN);

console.log("yol                     tek parca  ayrilmis  oran  gereken");
console.log("--------------------- ----------- --------- ----- --------");
const satir = (ad, a, b, gereken) => console.log(`${ad.padEnd(21)} ${String(a).padStart(11)} ${String(b).padStart(9)} ` +
  `${(a / b).toFixed(2).padStart(5)} ${String(gereken).padStart(8)}`);
satir("S1 takip okumasi", s1Tek, s1Ayri, 4);
satir("S2 donem taramasi", s2Tek, s2Ayri, SAYI.donem);

// S3 durum olayi yazmasi: bakilan dizin ve kendi sorgularina hizmet eden dizin.
const dTek = dizinSayisi(tek, "kayit");
const dAyri = dizinSayisi(ayri.olay, "olay");
console.log(`\nS3 durum olayi yazmasi: tek parcada ${dTek} dizin bakiliyor, ayrilmis depoda ${dAyri}`);
console.log(`bunlarin kendi siniflarinin sorgusuna hizmet edeni: 1 / ${dTek} = ${(1 / dTek).toFixed(2)}` +
  ` ve 1 / ${dAyri} = ${(1 / dAyri).toFixed(2)}`);
console.log(`K01 tepe yazma ${K01.tepeYazma} istek/s -> dizin bakimi ${(K01.tepeYazma * dTek).toFixed(2)}/s ve` +
  ` ${(K01.tepeYazma * dAyri).toFixed(2)}/s, fark ${(K01.tepeYazma * (dTek - dAyri)).toFixed(2)}/s`);

// Ayirmanin karsiligi: yazma yolu iki depoya uzuyor (olay kutugu + gonderinin son durumu).
const ayirmaYazma = 2, tekYazma = 1;
console.log(`\nayirmanin karsiligi: bir durum olayi ${tekYazma} depo yerine ${ayirmaYazma} depoya yaziliyor` +
  ` -> ${(K01.tepeYazma * ayirmaYazma).toFixed(2)} depo yazmasi/s (tek parcada ${(K01.tepeYazma * tekYazma).toFixed(2)})`);
console.log(`isletilen depo sayisi 1 -> 3; atomik olmayan yazma yolu ${K01.tepeYazma.toFixed(2)} istek/s`);

// Saklama kurali: tek kural butun siniflara uygulanir (K01 V12 = 730 gun, donem 30 gun yeter).
const [SAKLAMA, DONEM_GEREK] = [730, GUN];
const donemGunluk = SAYI.donem / GUN;
console.log(`\nsaklama: tek kural 730 gun -> donem kaydi ${SAKLAMA} gun tutuluyor, gereken ${DONEM_GEREK} gun`);
console.log(`olcekte tutulan donem kaydi ${donemGunluk * SAKLAMA} yerine ${donemGunluk * DONEM_GEREK}` +
  ` -> ${(SAKLAMA / DONEM_GEREK).toFixed(2)} kat fazla`);

// KK6'nin duyarliligi: sinif karisimi degisirse S2'nin gecis carpani ne olur.
console.log(`\nsinif karisimi (K01 V13 = fatura satirina kalem) -> S2 gecis carpani`);
for (const kalem of [100, 20, 5, 1]) {
  const d = (K01.gonderi / kalem / KK6) * GUN;
  const toplam = SAYI.gonderi + SAYI.olay + d;
  console.log(`  kalem ${String(kalem).padStart(3)} -> donem kaydi ${String(d).padStart(6)},` +
    ` donem payi ${(d / toplam).toFixed(4)}, carpan ${(toplam / d).toFixed(2)}`);
}
```

```
olcek 1/1000, donem 30 gun -> gonderi 12000, olay 84000, donem 120
tek parca deponun kayit sayisi = 96120
ayrilmis depolarin kayit sayisi = 12000 / 84000 / 120

yol                     tek parca  ayrilmis  oran  gereken
--------------------- ----------- --------- ----- --------
S1 takip okumasi                8         8  1.00        4
S2 donem taramasi           96120       120 801.00      120

S3 durum olayi yazmasi: tek parcada 3 dizin bakiliyor, ayrilmis depoda 1
bunlarin kendi siniflarinin sorgusuna hizmet edeni: 1 / 3 = 0.33 ve 1 / 1 = 1.00
K01 tepe yazma 97.22 istek/s -> dizin bakimi 291.66/s ve 97.22/s, fark 194.44/s

ayirmanin karsiligi: bir durum olayi 1 depo yerine 2 depoya yaziliyor -> 194.44 depo yazmasi/s (tek parcada 97.22)
isletilen depo sayisi 1 -> 3; atomik olmayan yazma yolu 97.22 istek/s

saklama: tek kural 730 gun -> donem kaydi 730 gun tutuluyor, gereken 30 gun
olcekte tutulan donem kaydi 2920 yerine 120 -> 24.33 kat fazla

sinif karisimi (K01 V13 = fatura satirina kalem) -> S2 gecis carpani
  kalem 100 -> donem kaydi    120, donem payi 0.0012, carpan 801.00
  kalem  20 -> donem kaydi    600, donem payi 0.0062, carpan 161.00
  kalem   5 -> donem kaydi   2400, donem payi 0.0244, carpan 41.00
  kalem   1 -> donem kaydi  12000, donem payi 0.1111, carpan 9.00
```

Kayıt ve dizin sayıları **ölçüm** sınıfındadır: depodan okunmuşlardır. K01'e çevrilen satırlar
**hesap**, ölçek ve sınıf karışımı **varsayım** sınıfındadır.

#### Okuma Yolu Hiçbir Şey Göstermiyor

Tablonun ilk satırı tanının en önemli parçasıdır. S1 takip okuması iki yerleşimde de 8 kayda
dokunuyor; oran 1,00. Tek parça kalıcılık bu yolda **görünmez**. Bir tanıyı yalnız takip
sorgusunun ölçümüyle kurmaya çalışan biri, deponun tek olmasının bir bedeli olmadığı sonucuna
varır ve yanılır. İki yerleşim de gerekenden fazlasını okuyor (8 kayıt, gereken 4) — ama bu, bu
dersin değil dördüncü dersin konusudur.

İkinci satır ayrımı veriyor. Dönem taraması ayrılmış depoda 120 kayda dokunuyor, yani tam olarak
gereken kadarına. Tek parça depoda paylaşılan `gun` dizini bütün sınıfların satırlarını aynı
sıraya dizdiği için tarama 96.120 kaydın üstünden geçiyor: kendi sınıfının **801 katı**. Bu sayı
deponun yavaş olmasından değil, taramanın kendi sınıfına ait olmayan kayıtları geçmek zorunda
kalmasından geliyor.

Üçüncü ölçüm belirtiyi doğrudan karşılıyor. Tek parça depoda bir durum olayı yazması üç dizin
bakar; ayrılmış depoda bir. Bu üç dizinin yalnız biri (`ix_anahtar`) olayın kendi sorgusuna hizmet
eder, ikisi dönem sınıfı için oradadır: hizmet oranı **0,33**. K01'in 97,22 istek/s tepe yazmasında
bu, saniyede 291,66 dizin bakımı demektir; ayrılmış yerleşimde 97,22. Aradaki 194,44 bakım, hiçbir
sorgunun kullanmadığı iştir.

Sayının işaret ettiği neden birincisi değil ikincisidir: kayıt hiç büyümemiştir, büyüyen yalnız
kendi sınıfına hizmet etmeyen paydır. Ayrımı yapan ölçüm budur.

#### Tek Deponun Doğru Olduğu Koşul

Tek depo her ölçekte yanlış değildir. Son tablo sınırı sayıyla veriyor: geçiş çarpanı sınıf
karışımına bağlıdır. Dönem kayıtları toplamın 0,0012'siyken çarpan 801, 0,0244'üyken 41,
0,1111'iyken 9'a iniyor. Yani tek depo, hiçbir sınıfın payı ötekilerin yanında kaybolmadığı sürece
savunulabilir bir seçimdir; çarpan bire yaklaştıkça ayırmanın kazandıracağı bir şey kalmaz.

İkinci koşul dizin tarafındadır. Üç dizinin hepsi aynı sınıfa hizmet etseydi hizmet oranı 1,00
olurdu ve yazma tarafında ödenen hiçbir bedel bulunmazdı. Bedel dizin sayısından değil, **başka
sınıf için konmuş** dizin sayısından doğar.

Üçüncü koşul saklamadır. K01'in V12 varsayımı bütün kayıtları 730 gün tutar, oysa dönem kaydının
işlevi otuz günlük dönem kapandığında biter. Tek depo tek bir saklama kuralı taşıdığı için dönem
kayıtları 24,33 kat fazla tutuluyor. Sınıfların saklama süreleri eşitse bu koşul da düşer.

Üçü birden sağlanıyorsa — paylar birbirine yakın, dizinler ortak, saklama tek — tek depo yalnız
doğru değil, ucuzdur da.

#### Ayırmanın Karşılığında Büyüyen

Ayırma bedava değildir ve bedeli yazma yolundadır. Tek depoda bir durum olayı tek bir yazmadır:
olay kütüğüne eklenir ve gönderinin son durumu aynı işlemde güncellenir. Sınıflar ayrıldığında aynı
olay iki depoya yazılır; K01'in tepe yazmasında bu, saniyede 97,22 yerine 194,44 depo yazması
demektir. Daha ağır olan şey sayı değil: iki yazma artık **tek bir işlem değildir**. Aradaki
tutarlılık, Dayanıklılık ve Güvenilirlik kursunun telafi edici işlem ve etkisiz işlem derslerinde
kurulan düzene devredilir — saniyede 97,22 isteklik bir yazma yolu, atomikliğini kaybeder.

İkinci karşılık işletme yüzeyidir: bir depo yerine üç depo yedeklenir, sürümlenir, izlenir ve
devralınır. Bu, bir sonraki konunun sayacağı yükün doğrudan girdisidir.

#### Özet

- Belirti yazma başına işin büyümesidir; iki nedeni vardır — kayıt büyümüştür ya da depo başka bir
  sınıfla paylaşılmaktadır. Ayıran ölçüm, işin kendi sınıfına hizmet eden payıdır.
- Takip okuması iki yerleşimde de 8 kayda dokunuyor (oran 1,00): tek parça kalıcılık okuma yolunda
  görünmez ve yalnız o yolla tanı konamaz.
- Dönem taraması ayrılmış depoda 120, tek parça depoda 96.120 kayıttan geçiyor — kendi sınıfının
  801 katı; çarpanın kaynağı sınıf karışımıdır.
- Bir durum olayı yazması tek parça depoda 3 dizin bakar ve yalnız 1'i kendi sorgusuna hizmet eder
  (0,33); K01'in 97,22 istek/s yazmasında saniyede 194,44 bakım hiçbir sorguya yaramaz.
- Tek depo üç koşul sağlandığında doğrudur: sınıf payları birbirine yakın (çarpan 801 değil 9),
  dizinler ortak (hizmet oranı 1,00) ve saklama tek (dönem kaydı 24,33 kat fazla tutulmuyor).
- Ayırmanın karşılığı yazma yolunun uzamasıdır: saniyede 97,22 yerine 194,44 depo yazması ve
  atomikliğini kaybeden bir yazma yolu; işletilen depo sayısı 1'den 3'e çıkar.

#### Sonraki Adım

Bu ders deponun **yapısını** sorguladı ve bir yazmanın kendi sınıfına hizmet etmeyen payını saydı.
Bir sonraki soru okuma tarafındadır ve önce yanıtlanmış gibi görünür: Veri Katmanı Ölçekleme
kursunun önbellek konusu, önbelleğin gerekmediği yeri ölçmüştü — tekrar oranı 1,00 olan bir
taramaya önbellek koymanın istek başına 2,00 ek dokunuşa mal olduğunu gösterdi. Sonraki ders o
ölçümün aynadaki yüzüdür: önbelleğin gerektiği hâlde konmadığı yer. Ama ölçülecek şey aynı değildir.
Orada yinelenen bir **okuma** sayıldı; burada yinelenen bir **hesap** sayılır ve bu ikisi aynı
akışta ayrışabilir: okuma tekrarı düşük bir akışın hesap tekrarı çok yüksek olabilir. Sonraki ders
bu ayrımı ölçer, hesabın tekrar oranını hesap anahtarı üzerinden sayar ve anahtarı eksik seçmenin
neye mal olduğunu gösterir.

### Önbelleksizlik

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/onbelleksizlik

Yinelenen pahalı hesabın önbelleksiz bırakılmasının ölçülmesi: okuma tekrarı 1,00 olan dönem taramasında hesap tekrarının 125,00 çıkması, bellemenin tavanını anahtar kurma maliyetinin belirlemesi (2,53 kat), başabaş tekrarın 1,63 olarak hesaplanması, sürekli ağırlıkta bellemenin 0,71 kata düşüp 119.365 girdi tutması ve anahtarın sürümü içermemesinin sonuçların yarısını yanlış yapması.

Bir önceki ders deponun yapısını sorguladı ve bir yazmanın kendi sınıfına hizmet etmeyen payını
saydı. Bu dersin sorusu okuma tarafındadır ve önce yanıtlanmış görünür. Veri Katmanı Ölçekleme
kursunun önbellek konusu, önbelleğin **gerekmediği** yeri ölçmüştü: tekrar oranı 1,00 olan dönem
taraması için önbellek istek başına 2,00 ek dokunuş getiriyor ve hiçbir şey kazandırmıyordu. Bu
ders o ölçümün aynadaki yüzüdür — önbelleğin gerektiği hâlde konmadığı yer. Ama ölçülen nicelik
aynı değildir. Orada yinelenen bir **okuma** sayıldı; burada yinelenen bir **hesap** sayılır.

Belirti şudur: uygulama katmanının işlemci doluluğu gün sonu penceresinde doyuma yaklaşıyor, oysa
depoya inen istek hızı değişmedi. K01'in hesabında `depoya ulasan istek/s` 138,89 ve önbellek
isabeti V9 varsayımıyla 0,90; iki sayı da yerinde duruyor. Yavaşlayan şey depo değil, uygulamanın
kendisi.

#### Aynı Belirti, İki Neden

**Birinci neden: istek başına iş gerçekten arttı.** Yanıta yeni alanlar girmiştir; seri hâle
getirme uzamıştır. Bu, dördüncü dersin ölçtüğü aşırı veri çekmenin işlemci tarafındaki karşılığıdır.

**İkinci neden: aynı hesap aynı girdiyle yineleniyor.** İstek başına iş hiç değişmemiştir; değişen,
aynı işin kaç kez tekrarlandığıdır. Buna **önbelleksizlik** (no caching) denir ve ders boyunca
ölçülen biçimi **belleme** (memoization) ile giderilir: hesabın sonucu, girdilerinden türetilen bir
**hesap anahtarına** karşı saklanır.

İkisini ayıran ölçüm, çağrıları okuma anahtarına göre değil **hesap anahtarına** göre
tekilleştirmektir. Hesap tekrarı bire yakınsa neden birincidir ve belleme bir şey kazandırmaz;
tekrar birden büyükse neden ikincidir. Bu iki tekrar oranı aynı akışta ayrışabilir ve bu dersin
asıl bulgusu odur.

#### Hesap ve Üç Akış

Ücretlendirme hesabı gerçek bir işlevdir: ağırlık dilimi taranır, sıralı tarife kural listesi
yürünür ve sözleşme çarpanı bulunur. Ölçülen nicelik süre değil, koşum sırasında **yürütülen adım
sayısıdır**; süre bu makineye bağlı olurdu, adım sayısı değildir.

**KK8 — hesabın biçimi.** Sekiz bölge, beş ağırlık dilimi, üç sözleşme sınıfı ve on yedi sıralı
tarife kuralı. Gerekçe: M18'in gönderi kitaplığındaki `bolge`, `tarife` ve `sozlesme` alanları bu
biçimi verir; kural listesi iç bölge, kaynak bölge ve yakalayıcı olmak üzere üç katmandır.
Duyarlılığı üçüncü akışla verilir: ağırlık dilimlenmeden anahtara girerse tekrar çöker. Bu varsayım
K01'in tablosuna eklenmez.

```js
// hesap/tarife.mjs — ucretlendirme hesabi ve uc akis. Sayaclar gercek kosum sirasinda artar;
// olculen nicelik sure degil, yurutulen adim sayisidir.
export const BOLGE = 8;
export const DILIM = [1, 5, 20, 50, Infinity];          // agirlik dilimi ust sinirlari
export const SOZLESME = ["standart", "sozlesmeli", "toplu"];

export const KURAL = [];
for (let k = 1; k <= BOLGE; k += 1) KURAL.push({ k: `B${k}`, h: `B${k}`, carpan: 1.0 });
for (let k = 1; k <= BOLGE; k += 1) KURAL.push({ k: `B${k}`, h: "*", carpan: 1.0 + k / 10 });
KURAL.push({ k: "*", h: "*", carpan: 2.5 });            // yakalayici kural

export const sayac = { hesap: 0, anahtar: 0, arama: 0 };
const sifirla = () => { sayac.hesap = 0; sayac.anahtar = 0; sayac.arama = 0; };

// Hesabin kendisi: dilim taranir, kural listesi sirayla yurunur, sozlesme carpani bulunur.
export function ucret(g) {
  let d = 0;
  while (DILIM[d] < g.agirlik) { sayac.hesap += 1; d += 1; }
  let carpan = 0;
  for (const kural of KURAL) {
    sayac.hesap += 1;
    if ((kural.k === "*" || kural.k === g.kaynak) && (kural.h === "*" || kural.h === g.hedef)) {
      carpan = kural.carpan; break;
    }
  }
  let s = 0;
  while (SOZLESME[s] !== g.sozlesme) { sayac.hesap += 1; s += 1; }
  sayac.hesap += 1;
  return carpan * (d + 1) * (1 + s / 4);
}

// Hesap anahtari yalniz belleme icin kurulur; dort alan okunur, dilim ayrica taranir.
export function anahtar(g) {
  let d = 0;
  while (DILIM[d] < g.agirlik) { sayac.anahtar += 1; d += 1; }
  sayac.anahtar += 3;
  return g.surekli ? `${g.kaynak}>${g.hedef}|${g.agirlik}|${g.sozlesme}`
                   : `${g.kaynak}>${g.hedef}|${d}|${g.sozlesme}`;
}

// 32 bit tam sayi aritmetigiyle tohumlu uretec; Math.imul kesinlik kaybini onler.
export function rastgeleUretec(tohum) {
  let s = tohum >>> 0;
  return () => ((s = (Math.imul(s, 1664525) + 1013904223) >>> 0) / 4294967296);
}

// Donem taramasi: her satici-gun kaydi bir kez okunur (okuma tekrari 1,00), ama hesabin
// girdileri kucuk bir kumeden gelir. `surekli` acikken agirlik dilimlenmeden anahtara girer.
export function donemAkisi(kayit, { surekli = false, tohum = 20260730 } = {}) {
  const rast = rastgeleUretec(tohum);
  return Array.from({ length: kayit }, (_, i) => ({
    okuma: `donem:${i + 1}`, surekli,
    kaynak: `B${Math.floor(rast() * BOLGE) + 1}`,
    hedef: `B${Math.floor(rast() * BOLGE) + 1}`,
    agirlik: surekli ? Math.round(rast() * 60000) / 1000 : [0.5, 3, 12, 35, 80][Math.floor(rast() * 5)],
    sozlesme: SOZLESME[Math.floor(rast() * SOZLESME.length)],
  }));
}

// Takip sorgusu: ayni gonderi obek obek sorulur; hesap girdileri gonderinin kendisinden gelir.
export function takipAkisi(istek, calismaKumesi = 1000, obek = 10, tohum = 4242) {
  const rast = rastgeleUretec(tohum);
  const gonderi = (no) => {
    const r = rastgeleUretec(no * 7919 + 13);
    return { okuma: `takip:${no}`, surekli: false, kaynak: `B${Math.floor(r() * BOLGE) + 1}`,
      hedef: `B${Math.floor(r() * BOLGE) + 1}`, agirlik: [0.5, 3, 12, 35, 80][Math.floor(r() * 5)],
      sozlesme: SOZLESME[Math.floor(r() * SOZLESME.length)] };
  };
  let sonraki = 1;
  const etkin = Array.from({ length: calismaKumesi }, () => ({ no: sonraki++, kalan: obek }));
  return Array.from({ length: istek }, () => {
    const j = Math.floor(rast() * etkin.length);
    const g = gonderi(etkin[j].no);
    if ((etkin[j].kalan -= 1) === 0) etkin[j] = { no: sonraki++, kalan: obek };
    return g;
  });
}

export function onbelleksiz(akis) {
  sifirla();
  for (const g of akis) ucret(g);
  return { ...sayac, girdi: 0 };
}

export function bellemeli(akis) {
  sifirla();
  const m = new Map();
  for (const g of akis) {
    const a = anahtar(g);
    sayac.arama += 1;
    if (m.has(a)) continue;
    m.set(a, ucret(g));
  }
  return { ...sayac, girdi: m.size };
}
```

```js
// hesap/olcum.mjs — okuma tekrari ile hesap tekrarinin ayrilmasi ve bellemenin karsiligi
import { ucret, anahtar, donemAkisi, takipAkisi, onbelleksiz, bellemeli } from "./tarife.mjs";

const KAYIT = 120_000;            // K01: donem satici-gun
const ISTEK = 200_000;            // K01: 480 s x 416,67 istek/s tepe okuma

// Sutun genisligi tek yerden gelir; baslik ile satir hep hizali kalir.
const EN = [22, 8, 13, 15, 13, 15];
const yaz = (h) => console.log(h.map((x, i) => (i ? String(x).padStart(EN[i]) : String(x).padEnd(EN[i]))).join(" "));
const cizgi = () => console.log(EN.map((n) => "-".repeat(n)).join(" "));

const AKIS = [
  ["donem taramasi", donemAkisi(KAYIT)],
  ["takip sorgusu", takipAkisi(ISTEK)],
  ["donem, surekli agirlik", donemAkisi(KAYIT, { surekli: true })],
];

yaz(["akis", "cagri", "tekil okuma", "okuma tekrari", "tekil hesap", "hesap tekrari"]);
cizgi();
const olcum = [];
for (const [ad, akis] of AKIS) {
  const o = onbelleksiz(akis);
  const b = bellemeli(akis);
  const tekilOkuma = new Set(akis.map((g) => g.okuma)).size;
  olcum.push([ad, akis.length, o, b]);
  yaz([ad, akis.length, tekilOkuma, (akis.length / tekilOkuma).toFixed(2), b.girdi,
    (akis.length / b.girdi).toFixed(2)]);
}

console.log();
yaz(["akis", "adim yok", "adim belleme", "oran", "adim/cagri", "belleme girdisi"]);
cizgi();
for (const [ad, n, o, b] of olcum) {
  const bt = b.hesap + b.anahtar + b.arama;
  yaz([ad, o.hesap, bt, (o.hesap / bt).toFixed(2), (o.hesap / n).toFixed(2), b.girdi]);
}

// Basabas noktasi olculen ortalamalardan cikar: C adim hesap, K adim anahtar, L adim arama.
console.log();
yaz(["akis", "C", "K", "L", "basabas tekrar", "olculen tekrar"]);
cizgi();
for (const [ad, n, o, b] of olcum) {
  const [C, K, L] = [o.hesap / n, b.anahtar / n, b.arama / n];
  yaz([ad, C.toFixed(2), K.toFixed(2), L.toFixed(2),
    (C / (C - K - L)).toFixed(2), (n / b.girdi).toFixed(2)]);
}

// KK9: tarife revizyonu donemin ortasinda yururluge giriyor. Anahtar surumu icermezse
// bellemeden donen deger revizyondan sonraki kayitlar icin yanlistir; yanlislar sayilir.
const REVIZYON = 1.05;
const surumlu = donemAkisi(KAYIT).map((g, i) => ({ ...g, surum: i < KAYIT / 2 ? 1 : 2 }));
const dogruDeger = (g) => ucret(g) * (g.surum === 2 ? REVIZYON : 1);
console.log();
for (const eksik of [true, false]) {
  const m = new Map();
  let yanlis = 0;
  for (const g of surumlu) {
    const a = eksik ? anahtar(g) : `${anahtar(g)}|s${g.surum}`;
    if (!m.has(a)) m.set(a, dogruDeger(g));
    if (Math.abs(m.get(a) - dogruDeger(g)) > 1e-9) yanlis += 1;
  }
  console.log(`anahtar surumu ${eksik ? "icermiyor" : "iceriyor "}: belleme girdisi ${String(m.size).padStart(6)},` +
    ` yanlis sonuc ${String(yanlis).padStart(6)}, yanlis orani ${(yanlis / KAYIT).toFixed(4)}`);
}
```

```
akis                      cagri   tekil okuma   okuma tekrari   tekil hesap   hesap tekrari
---------------------- -------- ------------- --------------- ------------- ---------------
donem taramasi           120000        120000            1.00           960          125.00
takip sorgusu            200000         20444            9.78           960          208.33
donem, surekli agirlik   120000        120000            1.00        119365            1.01

akis                   adim yok  adim belleme            oran    adim/cagri belleme girdisi
---------------------- -------- ------------- --------------- ------------- ---------------
donem taramasi          1860493        734534            2.53         15.50             960
takip sorgusu           3102366       1214858            2.55         15.51             960
donem, surekli agirlik  1948593       2745984            0.71         16.24          119365

akis                          C             K               L basabas tekrar  olculen tekrar
---------------------- -------- ------------- --------------- ------------- ---------------
donem taramasi            15.50          5.00            1.00          1.63          125.00
takip sorgusu             15.51          5.00            1.00          1.63          208.33
donem, surekli agirlik    16.24          5.73            1.00          1.71            1.01

anahtar surumu icermiyor: belleme girdisi    960, yanlis sonuc  60000, yanlis orani 0.5000
anahtar surumu iceriyor : belleme girdisi   1920, yanlis sonuc      0, yanlis orani 0.0000
```

Adım sayıları ve tekil anahtar sayıları **ölçüm** sınıfındadır: koşum sırasında sayılmışlardır.
Çağrı sayıları K01'den gelen **hesap**, hesabın biçimi **varsayım** sınıfındadır.

#### Okuma Tekrarı Hesap Tekrarını Söylemiyor

İlk tablonun ilk satırı bu dersin gerekçesidir. Dönem taramasında okuma tekrarı **1,00**: her
satıcı-gün kaydı bir kez okunur ve bir daha okunmaz. Veri Katmanı Ölçekleme kursu bu satır için
doğru kararı vermişti — bu akışa okuma önbelleği konmaz. Aynı akışın hesap tekrarı ise **125,00**:
120.000 kayıt yalnız 960 farklı hesap anahtarına düşüyor, çünkü ücret gönderinin kendisine değil
bölge çiftine, ağırlık dilimine ve sözleşme sınıfına bağlıdır. Bir akış hem okuma önbelleğini hak
etmeyebilir hem de her kaydında yinelenen bir hesap taşıyabilir; iki ölçü aynı akışta ayrışır.

Takip sorgusunda ikisi de büyük: okuma tekrarı 9,78, hesap tekrarı 208,33. Bu akışta okuma
önbelleği hesabın bir bölümünü zaten önler; önbelleksizlik burada ikinci bir kat olarak durur.

Üçüncü satır sınırı gösteriyor. Ağırlık dilimlenmeden anahtara girdiğinde 120.000 kayıt 119.365
farklı anahtar üretiyor ve hesap tekrarı **1,01**'e iniyor. Hesabın kendisi değişmedi; değişen,
anahtarın ayırt ettiği şeyin inceliği.

#### Bellemenin Tavanı

İkinci tablo kazancı adım cinsinden veriyor. Dönem taramasında önbelleksiz koşum 1.860.493 adım
yürütüyor, bellemeli koşum 734.534: oran **2,53**. Kazanç 125 katlık tekrarla orantılı değil ve
nedeni üçüncü tabloda: hesap çağrı başına ortalama **15,50** adım, ama hesap anahtarını kurmak her
çağrıda **5,00** adım ve aramanın kendisi **1,00** adım. Belleme tekrarları tümüyle ortadan
kaldırsa bile çağrı başına 6 adım ödenmeye devam eder. Kazancın tavanı bu yüzden 15,50 / 6,00,
yani 2,58'dir; ölçülen 2,53 o tavanın hemen altındadır.

Bu, bellemenin ölçüsünü tersine çevirir. Kazancı belirleyen şey tekrar oranı değil, **hesabın
anahtarına oranıdır**. Tekrar oranı yalnız tavana ne kadar yaklaşıldığını söyler.

Üçüncü tablo başabaş noktasını veriyor. Ölçülen üç maliyetten çıkan eşik $C / (C - K - L)$
biçimindedir ve dönem taraması için **1,63**: hesap tekrarı bu değerin üstündeyse belleme öder.
Ölçülen tekrar 125,00 olduğuna göre fazlasıyla öder. Sürekli ağırlık akışında eşik 1,71 ve ölçülen
tekrar 1,01; belleme orada **zarardadır** — oran 0,71, yani bellemeli koşum önbelleksizden 1,41 kat
daha çok adım yürütüyor.

#### Karşılığında Büyüyen

Belleme iki şeyi büyütür ve ikisi de sayılabilir.

**Bellek.** Dönem taramasında 960 girdi tutulur; sürekli ağırlık akışında 119.365. İkinci sayı,
kazancın olmadığı yerde bedelin nasıl patladığını gösterir: kayıt başına neredeyse bir girdi. Veri
Katmanı Ölçekleme kursunun ölçtüğü 2,00 ek dokunuşun buradaki karşılığı budur.

**Yanlışlık yüzeyi.** Son iki satır bunu ölçüyor. Tarife dönemin ortasında revize ediliyor
(**KK9**; gerekçe: ücretlendirme aylık dönem üzerinden mutabık kalınır, revizyon dönem içinde
yürürlüğe girebilir). Hesap anahtarı sürümü içermediğinde belleme 960 girdi tutuyor ve sonuçların
**0,5000**'i yanlış çıkıyor — revizyondan sonraki bütün kayıtlar, revizyondan önce hesaplanmış
değeri alıyor. Sürüm anahtara eklendiğinde girdi 1920'ye çıkıyor ve yanlış oranı 0,0000 oluyor.
Bedel iki katına çıkan bellektir; kazanılan şey doğruluktur.

Önbelleksiz bir hesap yanlış olamaz. Belleme, doğruluğu anahtarın eksiksizliğine bağlar ve bu bağ
ölçülmezse sessizce kopar.

#### Önbelleksizliğin Doğru Olduğu Koşul

Önbelleksizlik iki koşulda karşıt kalıp değil, doğru tasarımdır ve ikisi de sayıyla verilir.

**Hesap tekrarı başabaş eşiğinin altındaysa.** Sürekli ağırlık akışında tekrar 1,01, eşik 1,71;
belleme adımı 1,41 kat artırır ve 119.365 girdi tutar. Bu akışta hesabı her seferinde yapmak
doğrudur.

**Hesap anahtardan ucuzsa.** Eşik $C / (C - K - L)$ ifadesinde $C \le K + L$ olduğunda tanımsızdır:
anahtarı kurmak hesabı yapmaktan pahalıysa hiçbir tekrar oranı bellemeyi kurtarmaz. Bu ölçümde $C$
15,50 ve $K + L$ 6,00 olduğu için koşul sağlanıyor, ama ucuz bir hesapta sağlanmaz.

#### Özet

- Belirti uygulama katmanının doyuma yaklaşmasıdır; iki nedeni vardır — istek başına iş büyümüştür
  ya da aynı hesap yinelenmektedir. Ayıran ölçüm, çağrıların hesap anahtarına göre tekilleştirilmesidir.
- Dönem taramasında okuma tekrarı 1,00 iken hesap tekrarı 125,00: bir akış okuma önbelleğini hak
  etmezken her kaydında yinelenen bir hesap taşıyabilir. Veri Katmanı Ölçekleme kursunun ölçtüğü
  örüntünün aynadaki yüzü budur.
- Bellemenin kazancı 2,53 kat ve tavanı 15,50 / 6,00 = 2,58; tavanı belirleyen şey tekrar oranı
  değil, hesabın anahtar kurma maliyetine oranıdır.
- Başabaş tekrar ölçülen maliyetlerden çıkar ve 1,63'tür; sürekli ağırlık akışında tekrar 1,01,
  eşik 1,71 ve belleme 0,71 kata düşer — orada önbelleksizlik doğru tasarımdır.
- Bellemenin karşılığı bellek ve yanlışlık yüzeyidir: 960 girdiye karşı 119.365 girdi ve hesap
  anahtarı tarife sürümünü içermediğinde sonuçların 0,5000'i yanlış çıkıyor.

#### Sonraki Adım

Buraya kadarki yedi kalıbın hepsi tek bir sahibin işini ölçtü: sistemin yaptığı fazla iş, kendi
akışları arasında paylaştırıldı. Bir varsayım hiç sorulmadı — paylaşılan kaynağı kullanan herkesin
aynı taraf olduğu. Gönderi takip ve ücretlendirme servisi tek bir müşteriye hizmet etmiyor;
satıcılar aynı uçtan, aynı depodan ve aynı işçi havuzundan geçiyor. Sonraki ders belirtiyi buradan
alır: toplam istek hızı hiç değişmediği hâlde bir satıcı grubunun gördüğü gecikme büyüyor. Bunun da
iki nedeni vardır ve ikisini ayıran ölçü istek sayısı değildir — bir isteğin kaç birim iş doğurduğu
akıştan akışa altmış kat değişir ve payı istekle sayan bir ölçüm gürültüyü hiç göremez.

### Gürültülü Komşu

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/gurultulu-komsu

Paylaşılan bir kaynakta izolasyon eksikliğinin belirtiden tanınması: aynı gecikme artışını veren iki nedenin istek payı ile iş payının ayrıştırılmasıyla ayrılması, gürültü katsayısının 1,01'den 1,43'e çıkması, istek payı hiç değişmezken iş payının 0,1702'den 0,2415'e sıçraması ve pay ayırmanın karşılığında en büyük kiracının kendi gecikmesinin 0,47'den 2,96 tura çıkması.

Buraya kadarki yedi kalıp tek bir sahibin işini ölçtü. Bir varsayım hiç sorulmadı: paylaşılan
kaynağı kullanan herkesin aynı taraf olduğu. Gönderi takip ve ücretlendirme servisi tek bir
müşteriye hizmet etmiyor; satıcılar aynı uçtan, aynı depodan ve aynı işçi havuzundan geçiyor.

Belirti şudur: küçük satıcıların gördüğü ortalama bekleme dört kata çıktı, ama uçtaki toplam istek
hızı hiç değişmedi. K01'in tepe okuma hızı 416,67 istek/s yerinde; kapasite de aynı. Buna
**gürültülü komşu** (noisy neighbor) denir: paylaşılan bir kaynakta bir **kiracının** (tenant)
davranışı ötekilerin gördüğü başarımı değiştirir.

#### Aynı Belirti, İki Neden

**Birinci neden: küçük satıcıların kendi hızı arttı.** Kimse kimseyi rahatsız etmiyor; talep
gerçekten büyüdü ve kuyruk buna göre uzadı. Bu bir gürültü değil, bir kapasite sorusudur.

**İkinci neden: bir komşu payını büyüttü.** Küçük satıcıların hızı hiç değişmedi; en büyük satıcı
dönem taramasını sıklaştırdı ve paylaşılan havuzun kapasitesinin büyük bir bölümünü tüketmeye
başladı.

İkisi de aynı belirtiyi verir. Ayıran ölçüm, kiracı başına **istek payı** ile **iş payını** ayrı
ayrı saymaktır. Bir isteğin doğurduğu iş akıştan akışa yüzlerce kat değişir — altıncı ders bunu
ölçtü: bir dönem taraması, satıcının kendi kayıt sayısıyla orantılı iş yapar. Payı istekle sayan
bir ölçüm gürültüyü hiç göremez.

#### Paylaşılan Havuz

Aşağıdaki koşum bir **modeldir**: tur soyut bir adımdır (bir tur bir saniye; çevrim katsayısı
varsayımdır) ve gerçek küme ya da kiracı kurulmaz. İki düzen karşılaştırılır — geldiği sırayla
işleyen paylaşılan sıra ve iş koruyan dönüşümlü paylaştırma.

**KK10 — satıcı hacmi.** İki yüz satıcının gönderi hacmi sıraya göre çarpık dağılır ve toplamı
K01'in günde 400.000 gönderi varsayımıdır. Gerekçe: tek bir büyük satıcının hacmi kuyruğun tamamını
taşıyabilir. **KK11 — birim ve kapasite.** Bir takip sorgusu 1 birim, bir dönem taraması satıcının
gönderi sayısının yüzde biri kadar birim; havuz turda 540 birim işler. **KK12 — iki senaryo.** A:
beşinci sıradan sonraki satıcıların istek hızı 1,20 kat. B: en büyük satıcı taramasını beş kat
sıklaştırıyor. İkisi de olağan talebi benzer ölçüde büyütür. Bu varsayımlar K01'in tablosuna
eklenmez.

```js
// kiraci/paylasim.mjs — paylasilan bir is havuzunun MODELI. Tur soyut bir adimdir
// (bir tur = bir saniye; cevrim katsayisi varsayim). Gercek kume ya da kiraci kurulmaz.
export const KAPASITE = 540;                  // birim/tur
export const KIRACI = 200;
export const ARA = 60;                        // donem istegi araligi (tur)
export const K01 = { tepeOkuma: 416.67, gunlukGonderi: 400_000 };

// KK10: satici hacmi carpik dagilir (pay ~ 1/sira), toplami K01'in gunluk gonderisidir.
export function saticilar(n = KIRACI) {
  const h = Array.from({ length: n }, (_, i) => 1 / (i + 1));
  const t = h.reduce((a, b) => a + b, 0);
  return h.map((x, i) => ({ ad: `S${i + 1}`, pay: x / t, gonderi: Math.round((x / t) * K01.gunlukGonderi) }));
}
// Bir donem taramasinin birimi saticinin kendi kayit sayisiyla orantilidir (06. dersin olcusu).
export const tarama = (s) => Math.max(1, Math.round(s.gonderi / 100));

export function kosum({ tur = 600, ayirma = false, buyukSik = false, kucukArtti = false, parca = 0 }) {
  const S = saticilar();
  const birikim = S.map(() => 0);
  const kuyruk = S.map(() => []), bas = S.map(() => 0);   // kiraci kuyrugu ve bas isaretcisi
  const sira = []; let basSira = 0;                       // paylasilan duzende genel varis sirasi
  const s = S.map(() => ({ istek: 0, birim: 0, biten: 0, gecikme: 0, enBuyuk: 0 }));
  const koy = (i, birim, t) => {          // parca > 0 ise buyuk istek parcalara bolunur
    const n = parca > 0 ? Math.ceil(birim / parca) : 1;
    for (let c = 0; c < n; c += 1) {
      const o = { k: i, kalan: Math.ceil(birim / n), varis: t };
      if (ayirma) kuyruk[i].push(o); else sira.push(o);
      s[i].istek += 1; s[i].birim += o.kalan;
    }
  };
  const bitir = (o, t) => {
    const g = t - o.varis;
    s[o.k].biten += 1; s[o.k].gecikme += g;
    s[o.k].enBuyuk = Math.max(s[o.k].enBuyuk, g);
  };

  for (let t = 1; t <= tur; t += 1) {
    S.forEach((x, i) => {
      const carpan = i >= 5 && kucukArtti ? 1.20 : 1;     // A: kucuk saticilarin kendi hizi artiyor
      birikim[i] += K01.tepeOkuma * x.pay * carpan;
      while (birikim[i] >= 1) { birikim[i] -= 1; koy(i, 1, t); }
      const sik = i === 0 && buyukSik ? 5 : 1;            // B: en buyuk komsu taramasini siklastiriyor
      if ((t + i) % Math.round(ARA / sik) === 0) koy(i, tarama(x), t);
    });

    let butce = KAPASITE;
    if (ayirma) {                             // pay ayirma: is koruyan donusumlu paylastirma
      for (let gecis = 0; gecis < 12 && butce > 0; gecis += 1) {
        const aktif = [];
        for (let i = 0; i < KIRACI; i += 1) if (bas[i] < kuyruk[i].length) aktif.push(i);
        if (aktif.length === 0) break;
        const pay = Math.max(1, Math.floor(butce / aktif.length));
        const bastan = t % aktif.length;                  // baslangic donuyor, sira kimseye kalmiyor
        for (let n = 0; n < aktif.length && butce > 0; n += 1) {
          const i = aktif[(bastan + n) % aktif.length];
          let kalanPay = pay;
          while (kalanPay > 0 && butce > 0 && bas[i] < kuyruk[i].length) {
            const o = kuyruk[i][bas[i]];
            const al = Math.min(o.kalan, kalanPay, butce);
            o.kalan -= al; kalanPay -= al; butce -= al;
            if (o.kalan === 0) { bitir(o, t); bas[i] += 1; }
          }
        }
      }
    } else {                                  // paylasilan sira: geldigi sirayla
      while (butce > 0 && basSira < sira.length) {
        const o = sira[basSira];
        const al = Math.min(o.kalan, butce);
        o.kalan -= al; butce -= al;
        if (o.kalan === 0) { bitir(o, t); basSira += 1; }
      }
    }
  }
  return { S, s };
}
```

```js
// kiraci/olcum.mjs — istek payi ile is payinin ayrilmasi ve pay ayirmanin karsiligi
import { KAPASITE, KIRACI, ARA, K01, saticilar, tarama, kosum } from "./paylasim.mjs";

const EN = [10, 9, 11, 11, 10, 12, 12];
const yaz = (h) => console.log(h.map((x, i) => (i ? String(x).padStart(EN[i]) : String(x).padEnd(EN[i]))).join(" "));
const cizgi = () => console.log(EN.map((n) => "-".repeat(n)).join(" "));
const SEC = [0, 9, 49, 199];                          // S1, S10, S50, S200
const ort = (x) => (x.gecikme / x.biten).toFixed(2);
const kucukOrt = (r) => {                             // S6-S200 birlikte
  const k = r.s.slice(5);
  return (k.reduce((a, x) => a + x.gecikme, 0) / k.reduce((a, x) => a + x.biten, 0)).toFixed(2);
};

const S = saticilar();
const olagan = S.reduce((a, x) => a + K01.tepeOkuma * x.pay + tarama(x) / ARA, 0);
console.log(`kapasite ${KAPASITE} birim/tur, ${KIRACI} satici, olagan talep ${olagan.toFixed(2)} birim/tur` +
  ` (doluluk ${(olagan / KAPASITE).toFixed(4)}); en buyuk saticinin gunluk gonderisi ${S[0].gonderi},` +
  ` en kucugun ${S[KIRACI - 1].gonderi}; donem taramasi ${tarama(S[0])} ve ${tarama(S[KIRACI - 1])} birim\n`);

const GUN = [["arizasiz", {}], ["A: kucukler hizlandi", { kucukArtti: true }],
  ["B: en buyuk komsu taramayi siklastirdi", { buyukSik: true }]];
const PAYLASILAN = [], AYRILMIS = [];

for (const [ad, ek] of GUN) {
  const r = kosum(ek);
  PAYLASILAN.push(r); AYRILMIS.push(kosum({ ...ek, ayirma: true }));
  const ti = r.s.reduce((a, x) => a + x.istek, 0), tb = r.s.reduce((a, x) => a + x.birim, 0);
  console.log(`-- ${ad} --`);
  yaz(["satici", "istek", "istek payi", "is payi", "gurultu", "ort gecikme", "en buyuk"]);
  cizgi();
  for (const i of SEC) {
    const x = r.s[i];
    yaz([r.S[i].ad, x.istek, (x.istek / ti).toFixed(4), (x.birim / tb).toFixed(4),
      (x.birim / tb / (x.istek / ti)).toFixed(2), ort(x), x.enBuyuk]);
  }
  const k = r.s.slice(5);
  console.log(`S6-S200: ort gecikme ${kucukOrt(r)} tur, is payi ${(k.reduce((a, x) => a + x.birim, 0) / tb).toFixed(4)},` +
    ` istek payi ${(k.reduce((a, x) => a + x.istek, 0) / ti).toFixed(4)}\n`);
}

console.log("-- pay ayirma acikken: is koruyan donusumlu paylastirma --");
yaz(["gun", "S1 gec", "S10 gec", "S50 gec", "S200 gec", "S6-S200 ort", "S1 en buyuk"]);
cizgi();
GUN.forEach(([ad], g) => {
  const r = AYRILMIS[g];
  yaz([ad.split(":")[0], ...SEC.map((i) => ort(r.s[i])), kucukOrt(r), r.s[0].enBuyuk]);
});

console.log(`\nkarsiligi: B gununde S1'in ortalama gecikmesi ${ort(PAYLASILAN[2].s[0])} -> ${ort(AYRILMIS[2].s[0])} tur,` +
  ` en buyuk ${PAYLASILAN[2].s[0].enBuyuk} -> ${AYRILMIS[2].s[0].enBuyuk} tur`);
console.log(`arizasiz gunde bile S1 oder: ${ort(PAYLASILAN[0].s[0])} -> ${ort(AYRILMIS[0].s[0])} tur`);
console.log(`muhasebe: ${KIRACI} sayac, istek basina 1 dokunus; S1'in bolunemeyen istegi ${tarama(S[0])} birim,` +
  ` turdaki payi ${Math.floor(KAPASITE / KIRACI)} birim`);

console.log("\nayni B gunu, buyuk istek parcalanarak (pay ayirma yok):");
for (const parca of [681, 50]) {
  const r = kosum({ buyukSik: true, parca });
  console.log(`  en buyuk parca ${String(parca).padStart(3)} birim (turluk kapasitenin ${(parca / KAPASITE).toFixed(2)}'si)` +
    ` -> S6-S200 ort gecikme ${kucukOrt(r)} tur`);
}
```

```
kapasite 540 birim/tur, 200 satici, olagan talep 483.34 birim/tur (doluluk 0.8951); en buyuk saticinin gunluk gonderisi 68050, en kucugun 340; donem taramasi 681 ve 3 birim

-- arizasiz --
satici         istek  istek payi     is payi    gurultu  ort gecikme     en buyuk
---------- --------- ----------- ----------- ---------- ------------ ------------
S1             42541      0.1689      0.1702       1.01         0.10            2
S10             4263      0.0169      0.0170       1.01         0.20            2
S50              860      0.0034      0.0034       1.00         0.25            2
S200             222      0.0009      0.0008       0.95         0.33            2
S6-S200: ort gecikme 0.24 tur, is payi 0.6114, istek payi 0.6143

-- A: kucukler hizlandi --
satici         istek  istek payi     is payi    gurultu  ort gecikme     en buyuk
---------- --------- ----------- ----------- ---------- ------------ ------------
S1             42541      0.1506      0.1540       1.02         0.48            2
S10             5113      0.0181      0.0180       1.00         0.72            3
S50             1030      0.0036      0.0036       0.99         0.90            3
S200             265      0.0009      0.0009       0.95         1.12            3
S6-S200: ort gecikme 0.85 tur, is payi 0.6485, istek payi 0.6560

-- B: en buyuk komsu taramayi siklastirdi --
satici         istek  istek payi     is payi    gurultu  ort gecikme     en buyuk
---------- --------- ----------- ----------- ---------- ------------ ------------
S1             42581      0.1690      0.2415       1.43         0.47            3
S10             4263      0.0169      0.0156       0.92         0.85            3
S50              860      0.0034      0.0031       0.91         1.02            3
S200             222      0.0009      0.0008       0.87         1.23            3
S6-S200: ort gecikme 0.98 tur, is payi 0.5589, istek payi 0.6142

-- pay ayirma acikken: is koruyan donusumlu paylastirma --
gun           S1 gec     S10 gec     S50 gec   S200 gec  S6-S200 ort  S1 en buyuk
---------- --------- ----------- ----------- ---------- ------------ ------------
arizasiz        0.48        0.00        0.00       0.00         0.00            7
A               3.27        0.00        0.00       0.00         0.00           13
B               2.96        0.00        0.00       0.00         0.00            9

karsiligi: B gununde S1'in ortalama gecikmesi 0.47 -> 2.96 tur, en buyuk 3 -> 9 tur
arizasiz gunde bile S1 oder: 0.10 -> 0.48 tur
muhasebe: 200 sayac, istek basina 1 dokunus; S1'in bolunemeyen istegi 681 birim, turdaki payi 2 birim

ayni B gunu, buyuk istek parcalanarak (pay ayirma yok):
  en buyuk parca 681 birim (turluk kapasitenin 1.26'si) -> S6-S200 ort gecikme 0.98 tur
  en buyuk parca  50 birim (turluk kapasitenin 0.09'si) -> S6-S200 ort gecikme 1.00 tur
```

Gecikmeler ve paylar **ölçüm** sınıfındadır: belirlenimli bir varış dizisi üzerinde sayılmışlardır.
Kapasite, birim maliyetleri ve satıcı dağılımı **varsayım**, K01'den gelen hızlar **hesap**
sınıfındadır.

#### İstek Payı Gürültüyü Göstermiyor

İki senaryo aynı belirtiyi veriyor. Küçük satıcıların ortalama beklemesi arızasız günde 0,24 tur;
A gününde 0,85, B gününde 0,98. Yalnız bu sayıya bakan biri iki günü ayırt edemez.

Paylara bakıldığında ayrım keskinleşiyor. **A gününde** küçük satıcıların istek payı 0,6143'ten
0,6560'a, iş payı 0,6114'ten 0,6485'e çıkıyor; ikisi birlikte hareket ediyor. En büyük satıcının
gürültü katsayısı — iş payının istek payına oranı — 1,01'de kalıyor. Yükü büyüten, bekleyenlerin
kendisidir.

**B gününde** küçük satıcıların istek payı hiç değişmiyor: 0,6143 iken 0,6142. Buna karşılık iş
payları 0,6114'ten **0,5589**'a düşüyor. Kaybettikleri pay en büyük satıcıya geçiyor: onun iş payı
0,1702'den **0,2415**'e, gürültü katsayısı 1,01'den **1,43**'e çıkıyor. İstek payı ise 0,1689 ile
0,1690 arasında, yani sabittir.

Ayrımı yapan ölçüm budur. İstek sayısıyla tutulan bir muhasebede B günü hiçbir kiracıda kıpırtı
üretmez; en büyük satıcının fazladan gönderdiği şey birkaç istektir, ama o birkaç istek 681'er
birim taşır. Küçük bir satıcının aynı isteği 3 birimdir: **227 kat** fark. Payı istekle ölçen bir
sistem gürültülü komşuyu göremez, çünkü ölçtüğü nicelik kaynağın tükendiği nicelik değildir.

#### Pay Ayırma ve Karşılığı

Dönüşümlü paylaştırma açıldığında küçük satıcıların beklemesi üç günde de **0,00 tura** iniyor.
Sıra artık geliş anına göre değil kiracıya göre veriliyor ve kalan bütçe boşa gitmiyor — düzen iş
korur. Dayanıklılık ve Güvenilirlik kursundaki bölme kalıbı aynı sorunun sert çözümüdür: havuz
baştan bölünür, komşuluk ortadan kalkar ve bedeli orada atıl kalan kaynak olarak ölçülmüştü.
Buradaki pay ayırma kaynağı atıl bırakmaz; bedeli başka yerdedir.

Bedel en büyük kiracıdadır. B gününde S1'in kendi ortalama gecikmesi 0,47'den **2,96 tura**, en
büyük gecikmesi 3'ten 9 tura çıkıyor. Nedeni son satırda: bölünemeyen tek isteği 681 birim, turda
düşen payı ise 2 birim. Arızasız günde bile ödüyor — 0,10'dan 0,48 tura — hiçbir şey bozulmamışken.

İkinci bedel muhasebedir: iki yüz sayaç tutulur ve her istek bir kayıt dokunuşu ekler. Kiracı
sayısı arttıkça bu yük kiracı sayısıyla büyür.

Üçüncü ölçüm bir yanlış çözümü eliyor. Büyük isteği parçalara bölmek tek başına hiçbir şey
değiştirmiyor: en büyük parça 681'den 50 birime indiğinde küçük satıcıların beklemesi 0,98'den
1,00 tura çıkıyor. Parçalar da art arda aynı sıraya giriyor. Çözen şey parçanın küçüklüğü değil,
sıranın kiracılar arasında **dönüşümlü** verilmesidir.

Bir uyarı: pay ayırma belirtiyi giderir ama nedeni ayırt etmez. A gününde de küçük satıcıların
beklemesi sıfırlanıyor ve bedeli yine S1 ödüyor (3,27 tur), oysa o günkü yükü büyüten S1 değildi.
Ölçüm yapılmadan konan bir pay kuralı, yanlış kiracıyı cezalandırır.

#### Paylaşmanın Doğru Olduğu Koşul

Paylaşmak her ölçekte yanlış değildir ve sınırı iki sayıyla verilir.

**Gürültü katsayısı bire yakın kaldığı sürece.** Arızasız günde dört satıcının katsayısı 0,95 ile
1,01 arasında; havuz 0,8951 dolulukla çalışıyor ve ortalama bekleme 0,24 tur. Bu koşulda paylaşmak
yalnız zararsız değil, kârlıdır: hiçbir kiracıya ayrılmış atıl kapasite yoktur.

**Hiçbir tek istek turluk kapasiteyi aşmadığı sürece.** S1'in taraması 681 birim, turluk kapasite
540 — oran 1,26. Bölünemeyen bir istek bir turdan uzun sürüyorsa, arkasındaki her kiracı en az bir
tur bekler ve bu, hiçbir hız kuralıyla önlenemez.

#### Özet

- Belirti küçük kiracıların beklemesinin artmasıdır; iki nedeni vardır — kendi hızları artmıştır ya
  da bir komşu payını büyütmüştür. İkisi de beklemeyi 0,24'ten 0,85 ve 0,98 tura çıkarıyor.
- Ayıran ölçüm istek payı ile iş payının ayrıştırılmasıdır. A gününde ikisi birlikte büyüyor;
  B gününde istek payı 0,6143'ten 0,6142'ye, yani hiç değişmezken iş payı 0,5589'a düşüyor.
- Gürültü katsayısı en büyük satıcıda 1,01'den 1,43'e çıkıyor ve iş payı 0,1702'den 0,2415'e
  sıçrıyor; istek payı sabit kalıyor. İstekle tutulan bir muhasebe gürültüyü göremez.
- Pay ayırma küçük kiracıların beklemesini üç günde de 0,00 tura indiriyor; karşılığında S1'in
  gecikmesi 0,47'den 2,96 tura, arızasız günde 0,10'dan 0,48 tura çıkıyor ve 200 sayaç tutulur.
- Büyük isteği parçalamak tek başına yetmiyor (0,98 → 1,00 tur); çözen şey sıranın kiracılar
  arasında dönüşümlü verilmesidir.
- Paylaşmak, gürültü katsayısı bire yakın ve hiçbir tek istek turluk kapasiteyi aşmıyorken
  doğrudur; S1'in 681 birimlik taraması 540 birimlik turu 1,26 kat aşıyor.

#### Sonraki Adım

Bu dersin bütün ölçümleri bir sayıyı sabit tuttu: havuzun turda 540 birim işlediği. O sayının
nereden geldiği hiç sorulmadı. Bir sürecin taşıyabileceği iş, işlemcisinin hızıyla değil, aynı
anda kaç isteği ilerletebildiğiyle sınırlıdır ve bu sınır çoğu zaman işlemciden çok önce dolar.
Sonraki ders belirtiyi oradan alır: işçi eklendiği hâlde verim artmıyor ve işlemci doluluğu
düşük. Bunun da iki nedeni vardır — aşağı akış doymuştur ya da işçiler engelleyen çağrılarda
bekliyordur. İkisini ayıran ölçü işçilerde değil, bağımlılığın kendi doluluğundadır; işçilerin
bekleme payı iki nedende de neredeyse aynı çıktığı için ayırt edemez.

### Eşzamanlı Girdi/Çıktı

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/eszamanli-girdi-cikti

Engelleyen çağrıların kapasiteye etkisinin ölçülmesi: aynı verim tavanını veren iki nedenin bağımlılık doluluğuyla ayrılması (0,9999 ve 0,1819), engelleyen tasarımın verimi eşzamanlılık sınırının bekleme artı iş süresine bölümüyle sınırlaması, aynı verimi taşımak için işçi sayısının 21 kat büyümesi ve düzeltmenin karşılığında uçuştaki isteğin 8'den 46'ya çıkması.

Bir önceki dersin bütün ölçümleri bir sayıyı sabit tuttu: havuzun turda 540 birim işlediği. O
sayının nereden geldiği hiç sorulmadı. Bir sürecin taşıyabileceği iş, işlemcisinin hızıyla değil,
aynı anda kaç isteği ilerletebildiğiyle sınırlıdır.

Belirti şudur: verim turda 0,36 istekte tavan yaptı ve işçi eklemek bu sayıyı büyütmüyor. İşçilerin
doluluğu 0,95 — havuz doymuş görünüyor. Bu ders o görüntünün yanıltıcı olabildiğini gösterir.

#### Aynı Belirti, İki Neden

**Birinci neden: aşağı akış doymuş.** Bağımlılık turda ne kadar çağrı karşılayabiliyorsa verim
odur; işçi eklemek hiçbir şeyi değiştirmez, çünkü darboğaz çağıranda değildir.

**İkinci neden: işçiler engelleyen çağrıda bekliyor.** Bağımlılık boşta duruyor, ama her işçi bir
**engelleyen çağrının** (blocking call) yanıtını beklerken başka hiçbir isteği ilerletemez. Buna
**eşzamanlı girdi/çıktı** (synchronous I/O) denir.

İki durumda da işçi doluluğu 1'e yakın çıkar ve iki durumda da işçi eklemek çare gibi görünür.
Ayıran ölçüm işçilerde değil, **bağımlılıkta**dır: bağımlılığın kendi doluluğu 1'e yakınsa neden
birincidir, düşükse ikincidir. Bu ölçüm alınmadan yapılan her açıklama tahmindir.

#### Engelleyen Çağrı Gerçekte Ne Yapar

Aşağıdaki blok bir model değil, bu makinede koşan gerçek bir ölçümdür. İki yüz dosya okunur; süre
ölçülmez, çünkü süre bu makineye bağlıdır. Ölçülen nicelikler ikisi de belirlenimlidir: okuma
sürerken **ilerleyen başka iş sayısı** ve **eş zamanlı uçuştaki işlem sayısı**.

```js
// eszamanli/gercek.mjs — gercek node kosumu: engelleyen cagri sirasinda baska hicbir is
// ilerleyemez. Olculen nicelik sure degil, ilerleyen is sayisi ve es zamanli ucustaki islem.
import { mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

const N = 200;
const dizin = mkdtempSync(join(tmpdir(), "kk09-"));
for (let i = 0; i < N; i += 1) writeFileSync(join(dizin, `k${i}`), "x".repeat(1024));
const yol = (i) => join(dizin, `k${i}`);

// A: engelleyen cagri. Donguye girmeden once siraya konan is, dongu bitene kadar ilerleyemez.
let ilerleyen = 0, ucus = 0, enBuyukUcusA = 0;
const bekleyenIs = Promise.resolve().then(() => { ilerleyen += 1; });
for (let i = 0; i < N; i += 1) {
  ucus += 1; enBuyukUcusA = Math.max(enBuyukUcusA, ucus);
  readFileSync(yol(i));
  ucus -= 1;
}
console.log(`engelleyen: dongu boyunca ilerleyen baska is = ${ilerleyen},` +
  ` en buyuk es zamanli ucus = ${enBuyukUcusA}, okunan dosya = ${N}`);
await bekleyenIs;

// B: engellemeyen cagri. Ayni is, cagrilar surerken ilerler.
let ilerleyenB = 0, ucusB = 0, enBuyukUcusB = 0;
const bekleyenIsB = Promise.resolve().then(() => { ilerleyenB += 1; });
const sonuc = await Promise.all(Array.from({ length: N }, async (_, i) => {
  ucusB += 1; enBuyukUcusB = Math.max(enBuyukUcusB, ucusB);
  const b = await readFile(yol(i));
  ucusB -= 1;
  return b.length;
}));
await bekleyenIsB;
console.log(`engellemeyen: cagrilar surerken ilerleyen baska is = ${ilerleyenB},` +
  ` en buyuk es zamanli ucus = ${enBuyukUcusB}, okunan dosya = ${sonuc.length}`);
console.log(`es zamanli ucustaki islem orani = ${enBuyukUcusB / enBuyukUcusA}`);
rmSync(dizin, { recursive: true, force: true });
```

```
engelleyen: dongu boyunca ilerleyen baska is = 0, en buyuk es zamanli ucus = 1, okunan dosya = 200
engellemeyen: cagrilar surerken ilerleyen baska is = 1, en buyuk es zamanli ucus = 200, okunan dosya = 200
es zamanli ucustaki islem orani = 200
```

İki sayı bütün mekaniği veriyor. Engelleyen düzende, döngüye girmeden önce sıraya konmuş bir iş
iki yüz okuma bitene kadar **hiç ilerlemiyor** ve aynı anda uçuşta olan işlem sayısı 1'de kalıyor.
Engellemeyen düzende aynı iş çağrılar sürerken ilerliyor ve uçuştaki işlem 200'e çıkıyor: **200
kat**. Burada ölçülen şey koşutluk değildir — çalışma zamanının kaç işi gerçekten koşut yürüttüğü
ayrı bir sorudur. Ölçülen şey, işçinin bloke olup olmadığıdır.

Dil düzeyinde eşzamansız model Eşzamansız JavaScript ve Çalışma Zamanı kursunda kuruldu; burada
yeniden anlatılmaz. Buradaki soru, bu farkın **kapasiteye** ne yaptığıdır.

#### İki Nedeni Ayıran İki Ölçüm

Aşağıdaki koşum bir **modeldir**: tur soyut bir adımdır ve gerçek süreç ya da iş parçacığı
kurulmaz. **KK13 — çağrı profili.** Eşzamanlılık sınırı 8 işçi, yerel iş 1 tur, engelleyen
çağrının beklemesi 20 tur. Gerekçe: uzak bir bağımlılığın yanıtı yerel işten iki mertebe uzundur;
duyarlılığı son tabloda bekleme süresi değiştirilerek verilir. **KK14 — çevrim katsayısı.** Bir tur
1 ms sayılır; bu yalnız verimi K01'in istek/s ölçüsüne çevirmek içindir. Bu varsayımlar K01'in
tablosuna eklenmez.

```js
// eszamanli/kapasite.mjs — isci havuzunun MODELI. Tur soyut bir adimdir; KK14 ile bir tur
// 1 ms sayilir (cevrim katsayisi varsayim). Gercek surec ya da is parcacigi kurulmaz.
export const K01 = { tepeUc: 513.89 };

export function kosum({ tur = 20_000, sinir = 8, is = 1, bekleme = 20, bagimlilik = 2.0,
                        engelleyen = true, teklif = 3 }) {
  let bosIsci = sinir, kuyruk = 0, tBirikim = 0, jBirikim = 0;
  const aktif = [];                       // { asama: "is" | "jeton" | "bekle", kalan }
  const s = { tamam: 0, calisan: 0, bekleyen: 0, jeton: 0, enBuyukUcus: 0 };

  for (let t = 1; t <= tur; t += 1) {
    tBirikim += teklif;
    while (tBirikim >= 1) { tBirikim -= 1; kuyruk += 1; }
    jBirikim += bagimlilik;
    let jeton = Math.floor(jBirikim); jBirikim -= jeton;

    while (bosIsci > 0 && kuyruk > 0) { kuyruk -= 1; bosIsci -= 1; aktif.push({ asama: "is", kalan: is }); }

    for (let i = aktif.length - 1; i >= 0; i -= 1) {
      const r = aktif[i];
      if (r.asama === "is") {
        r.kalan -= 1; s.calisan += 1;
        if (r.kalan === 0) r.asama = "jeton";
      } else if (r.asama === "jeton") {
        if (jeton > 0) {
          jeton -= 1; s.jeton += 1; r.asama = "bekle"; r.kalan = bekleme;
          if (!engelleyen) bosIsci += 1;         // isci cagriyi baslatip serbest kalir
        } else if (engelleyen) s.bekleyen += 1;  // jeton yokken isci elde tutuluyor
      } else {
        r.kalan -= 1;
        if (engelleyen) s.bekleyen += 1;
        if (r.kalan === 0) {
          s.tamam += 1; if (engelleyen) bosIsci += 1;
          aktif.splice(i, 1);
        }
      }
    }
    s.enBuyukUcus = Math.max(s.enBuyukUcus, aktif.length);
  }

  const isciTur = s.calisan + s.bekleyen;
  return {
    verim: s.tamam / tur,
    isciDoluluk: isciTur / (sinir * tur),
    beklemePayi: isciTur === 0 ? 0 : s.bekleyen / isciTur,
    bagimlilikDoluluk: s.jeton / (bagimlilik * tur),
    ucus: s.enBuyukUcus,
  };
}
```

```js
// eszamanli/olcum.mjs — ayni belirtiyi veren iki neden, ayirt eden iki olcum ve karsiligi
import { K01, kosum } from "./kapasite.mjs";

const EN = [34, 8, 12, 13, 13, 8];
const yaz = (h) => console.log(h.map((x, i) => (i ? String(x).padStart(EN[i]) : String(x).padEnd(EN[i]))).join(" "));
const cizgi = () => console.log(EN.map((n) => "-".repeat(n)).join(" "));
const satir = (ad, r) => yaz([ad, r.verim.toFixed(4), r.isciDoluluk.toFixed(4),
  r.beklemePayi.toFixed(4), r.bagimlilikDoluluk.toFixed(4), r.ucus]);

const [SINIR, IS, BEKLEME] = [8, 1, 20];
console.log(`eszamanlilik siniri ${SINIR} isci, yerel is ${IS} tur, engelleyen cagrinin beklemesi ${BEKLEME} tur`);
console.log(`aritmetik ust sinir: engelleyen ${SINIR}/(${BEKLEME}+${IS}) = ${(SINIR / (BEKLEME + IS)).toFixed(4)} istek/tur,` +
  ` engellemeyen min(${SINIR}/${IS}, bagimlilik) istek/tur\n`);

yaz(["durum", "verim", "isci dol.", "bekleme payi", "bagim. dol.", "ucus"]);
cizgi();
satir("neden 1: bagimlilik doymus (D=0,35)", kosum({ bagimlilik: 0.35 }));
satir("neden 2: engelleyen cagri (D=2,0)", kosum({ bagimlilik: 2.0 }));
console.log();
satir("neden 1, engellemeyen", kosum({ bagimlilik: 0.35, engelleyen: false }));
satir("neden 2, engellemeyen", kosum({ bagimlilik: 2.0, engelleyen: false }));

const e = kosum({ bagimlilik: 2.0 });
const n = kosum({ bagimlilik: 2.0, engelleyen: false });
console.log(`\nneden 2'de duzeltmenin kazanci: ${e.verim.toFixed(4)} -> ${n.verim.toFixed(4)} istek/tur` +
  ` = ${(n.verim / e.verim).toFixed(2)} kat`);
console.log(`ayni verimi engelleyen tasarimla tasimak icin gereken isci = ${n.verim} x (${BEKLEME}+${IS})` +
  ` = ${(n.verim * (BEKLEME + IS)).toFixed(0)}, yani ${((BEKLEME + IS) / IS).toFixed(0)} kat`);
console.log(`karsiliginda buyuyen ucustaki istek: ${e.ucus} -> ${n.ucus} (${(n.ucus / e.ucus).toFixed(2)} kat)` +
  ` ve bagimlilik dolulugu ${e.bagimlilikDoluluk.toFixed(4)} -> ${n.bagimlilikDoluluk.toFixed(4)}`);

// KK14: bir tur 1 ms sayilir; verim istek/s'ye cevrilir ve K01'in tepe hiziyla karsilastirilir.
const TUR_MS = 1;
const saniye = (r) => (r.verim * 1000) / TUR_MS;
console.log(`\nbir tur ${TUR_MS} ms (KK14): tek surecin tasidigi istek engelleyen ${saniye(e).toFixed(2)} istek/s,` +
  ` engellemeyen ${saniye(n).toFixed(2)} istek/s`);
console.log(`K01 tepe ucta ${K01.tepeUc} istek/s -> gereken surec sayisi ` +
  `${Math.ceil(K01.tepeUc / saniye(e))} ve ${Math.ceil(K01.tepeUc / saniye(n))}`);

console.log(`\nisci eklemenin engelleyen tasarimdaki etkisi (D=2,0):`);
yaz(["durum", "verim", "isci dol.", "bekleme payi", "bagim. dol.", "ucus"]);
cizgi();
for (const sinir of [8, 16, 42, 84]) satir(`sinir ${sinir} isci`, kosum({ sinir, bagimlilik: 2.0 }));

console.log(`\nKK13'un duyarliligi: bekleme kisaldikca engelleyen tasarimin bedeli (D=2,0)`);
for (const bekleme of [20, 5, 2, 1]) {
  const a = kosum({ bekleme, bagimlilik: 2.0 }), b = kosum({ bekleme, bagimlilik: 2.0, engelleyen: false });
  console.log(`  bekleme ${String(bekleme).padStart(2)} tur -> engelleyen ${a.verim.toFixed(4)},` +
    ` engellemeyen ${b.verim.toFixed(4)}, oran ${(b.verim / a.verim).toFixed(2)}`);
}
```

```
eszamanlilik siniri 8 isci, yerel is 1 tur, engelleyen cagrinin beklemesi 20 tur
aritmetik ust sinir: engelleyen 8/(20+1) = 0.3810 istek/tur, engellemeyen min(8/1, bagimlilik) istek/tur

durum                                 verim    isci dol.  bekleme payi   bagim. dol.     ucus
---------------------------------- -------- ------------ ------------- ------------- --------
neden 1: bagimlilik doymus (D=0,35)   0.3496       0.9562        0.9542        0.9999        8
neden 2: engelleyen cagri (D=2,0)    0.3635       0.9545        0.9524        0.1819        8

neden 1, engellemeyen                0.3496       0.0438        0.0000        0.9999       15
neden 2, engellemeyen                1.9979       0.2500        0.0000        1.0000       46

neden 2'de duzeltmenin kazanci: 0.3635 -> 1.9979 istek/tur = 5.50 kat
ayni verimi engelleyen tasarimla tasimak icin gereken isci = 1.9979 x (20+1) = 42, yani 21 kat
karsiliginda buyuyen ucustaki istek: 8 -> 46 (5.75 kat) ve bagimlilik dolulugu 0.1819 -> 1.0000

bir tur 1 ms (KK14): tek surecin tasidigi istek engelleyen 363.50 istek/s, engellemeyen 1997.90 istek/s
K01 tepe ucta 513.89 istek/s -> gereken surec sayisi 2 ve 1

isci eklemenin engelleyen tasarimdaki etkisi (D=2,0):
durum                                 verim    isci dol.  bekleme payi   bagim. dol.     ucus
---------------------------------- -------- ------------ ------------- ------------- --------
sinir 8 isci                         0.3635       0.9545        0.9524        0.1819        8
sinir 16 isci                        0.7267       0.9544        0.9524        0.3636       16
sinir 42 isci                        1.9071       0.9542        0.9524        0.9545       42
sinir 84 isci                        1.9979       0.9755        0.9756        1.0000       82

KK13'un duyarliligi: bekleme kisaldikca engelleyen tasarimin bedeli (D=2,0)
  bekleme 20 tur -> engelleyen 0.3635, engellemeyen 1.9979, oran 5.50
  bekleme  5 tur -> engelleyen 1.1426, engellemeyen 1.9994, oran 1.75
  bekleme  2 tur -> engelleyen 1.9997, engellemeyen 1.9997, oran 1.00
  bekleme  1 tur -> engelleyen 1.9998, engellemeyen 1.9998, oran 1.00
```

Uçuş sayıları ve ilerleyen iş sayısı **ölçüm**, model çıktısındaki verim ve doluluklar belirlenimli
koşumdan çıkan **hesap**, çağrı profili ve çevrim katsayısı **varsayım** sınıfındadır.

#### Ölçüm Nedeni Söylüyor

İlk tablonun iki satırı aynı belirtiyi veriyor: verim 0,3496 ile 0,3635, işçi doluluğu 0,9562 ile
0,9545, bekleme payı 0,9542 ile 0,9524. İşçiler doymuş görünüyor ve iki durumda da vakitlerinin
yüzde doksan beşi bekleyerek geçiyor.

Ayrım son sütundan bir öncekinde. Birinci nedende bağımlılık doluluğu **0,9999** — bağımlılık
gerçekten doymuş. İkinci nedende **0,1819** — bağımlılık kapasitesinin beşte dördü boşta dururken
verim tavana çarpıyor. Tek başına işçi doluluğuna bakan bir tanı iki durumu ayıramaz; bağımlılığın
kendi doluluğu ayırır.

Doğrulaması ikinci tablodadır. Engelleyen çağrı kaldırıldığında birinci nedende hiçbir şey
değişmiyor (0,3496 → 0,3496), ikinci nedende verim 0,3635'ten **1,9979**'a, yani 5,50 kata
çıkıyor. Aynı düzeltme bir nedende işe yarar, ötekinde yaramaz — bu yüzden ölçüm düzeltmeden önce
gelir.

Tavanın kaynağı aritmetiktir ve ilk satırda yazılı: engelleyen tasarımda verim, eşzamanlılık
sınırının bekleme artı iş süresine bölümünü aşamaz — 8 / (20 + 1) = 0,3810. Model 0,3635 ölçüyor;
aradaki fark bağımlılığın jeton dağıtımının turlara yayılmasından gelir.

Üçüncü tablo işçi eklemenin ne yaptığını gösteriyor. Sınır 8'den 16'ya çıkınca verim 0,7267'ye,
42'ye çıkınca 1,9071'e ulaşıyor; yani engelleyen tasarım aynı verimi ancak **21 kat** işçiyle
taşıyor. İşçi doluluğu bu satırların hepsinde 0,95 civarında kalıyor — doluluk hiçbir zaman sorunu
göstermiyor, yalnız işçilerin meşgul olduğunu söylüyor.

#### Düzeltmenin Karşılığında Büyüyen

Engellemeyen tasarım bedava değildir ve bedeli sayılabilir.

**Uçuştaki istek.** Aynı anda sistemde duran istek sayısı 8'den 46'ya çıkıyor: 5,75 kat. Her
uçuştaki istek bellek tutar — tampon, bağlam, kısmi yanıt. Engelleyen tasarımda bu sayıyı
eşzamanlılık sınırı kendiliğinden bağlıyordu; engellemeyen tasarımda böyle bir sınır yoktur ve
kabul denetimi ayrıca konmalıdır. Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunun kabul
denetimi ve eşzamanlılık sınırı dersleri tam olarak bu boşluğu doldurur.

**Darboğazın yer değiştirmesi.** Bağımlılık doluluğu 0,1819'dan 1,0000'e çıkıyor. Çağıran
düzeldiğinde yük bağımlılığa iniyor ve bir sonraki tanı orada yapılacaktır.

**Taşınan istek.** KK14 ile bir tur 1 ms sayıldığında tek süreç engelleyen tasarımda 363,50
istek/s, engellemeyen tasarımda 1997,90 istek/s taşıyor. K01'in 513,89 istek/s tepe hızı için
gereken süreç sayısı 2 ile 1 arasında değişiyor: aynı donanım, iki kat fark.

#### Engelleyen Çağrının Doğru Olduğu Koşul

Son tablo sınırı veriyor. Bedel, beklemenin yerel işe oranından doğar. Bekleme 20 turken oran 5,50;
5 turken 1,75; 2 turken **1,00**. Bekleme yerel işle aynı mertebeye indiğinde engelleyen tasarım
hiçbir şey kaybettirmez, çünkü işçinin tuttuğu süre zaten çalışarak geçiyordur.

İkinci koşul birinci nedenden çıkar: bağımlılık doymuşsa engellememenin kazancı sıfırdır (0,3496 →
0,3496). Darboğaz aşağıdaysa çağıranı karmaşıklaştırmak yalnız uçuştaki isteği büyütür.

#### Özet

- Belirti verimin tavana çarpması ve işçi eklemenin işe yaramamasıdır; iki nedeni vardır — aşağı
  akış doymuştur ya da işçiler engelleyen çağrıda beklemektedir.
- İşçi doluluğu iki nedeni ayıramaz: 0,9562 ile 0,9545, bekleme payı 0,9542 ile 0,9524. Ayıran
  ölçüm bağımlılığın kendi doluluğudur — 0,9999 ile 0,1819.
- Engelleyen tasarımda verim, eşzamanlılık sınırının bekleme artı iş süresine bölümünü aşamaz:
  8 / (20 + 1) = 0,3810 istek/tur; aynı verimi taşımak için işçi sayısı 21 kat büyümelidir.
- Düzeltme ikinci nedende verimi 0,3635'ten 1,9979'a (5,50 kat) çıkarıyor, birinci nedende hiçbir
  şeyi değiştirmiyor; bir tur 1 ms sayıldığında tek süreç 363,50 yerine 1997,90 istek/s taşıyor.
- Karşılığında uçuştaki istek 8'den 46'ya (5,75 kat) çıkıyor ve bağımlılık doluluğu 0,1819'dan
  1,0000'e yükseliyor: sınır kalktığı için kabul denetimi ayrıca konmalıdır.
- Engelleyen çağrı, bekleme yerel işle aynı mertebedeyken doğrudur: bekleme 2 turda oran 1,00'e
  iniyor.

#### Sonraki Adım

Dokuz kalıbın hepsi bir varsayımın üstünde duruyor: bir isteğin bir kez yapıldığı. Uçtan gelen yük
dışarıdan belirlenir, sistem onu yalnız taşır. Bu varsayım bir şey bozulduğu anda düşer. Çağrılar
başarısız olmaya başladığında çağıran yeniden dener, denemeler zincirin her katmanında çarpılır ve
bağımlılığa varan yük artık dışarıdan değil, sistemin kendisinden gelir. Sonraki ders konunun son
tanısıdır ve sorusu şudur: böyle bir döngü dışarıdan nasıl görünür. Bağımlılığa varan çağrı hızının
iki katına çıkması hem gerçek bir talep artışının hem de bir yeniden deneme fırtınasının belirtisi
olabilir; ikisini ayıran ölçüt çağrı sayısı değildir.

### Yeniden Deneme Fırtınası

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/yeniden-deneme-firtinasi

Arızayı büyüten geri bildirim döngüsünün dışarıdan tanınması: aynı çağrı hızı artışını veren iki nedenin uç hızı, tekil istek kimliği ve başarı oranıyla ayrılması, fırtınada çağrı hızının 4,00'ten 25,00'e çıkarken tekil kimliğin 4,00'te kalması, çağrı hızı ile başarı oranının çarpımının kapasite sınırında sabit kalması ve deneme iznini kısmanın 180 düşen isteğe mal olması.

Dokuz kalıbın hepsi bir varsayımın üstünde durdu: bir isteğin bir kez yapıldığı. Uçtan gelen yük
dışarıdan belirlenir, sistem onu yalnız taşır. Bu varsayım bir şey bozulduğu anda düşer.

Belirti şudur: bağımlılığa varan çağrı hızı altı katına çıktı. Yeniden deneme kararı, üstel geri
çekilme, sarsıntı ve deneme bütçesi Uygulama Mimarisi, Önbellekleme, Kuyruklar ve Eşzamansız İşleme
ve Dayanıklılık ve Güvenilirlik kurslarında kuruldu ve ölçüldü; hiçbiri yeniden hesaplanmaz. Çizelge bir
**girdidir**. Buradaki soru tanıdır: böyle bir hız artışı dışarıdan nasıl görünür ve hangi ölçüt
onu gerçek bir talep artışından ayırır.

#### Aynı Belirti, İki Neden

**Birinci neden: dış talep gerçekten arttı.** Uçtan gelen istek hızı yükselmiştir; bağımlılığa
varan çağrı da onunla birlikte yükselir. Sistem sağlıklıdır, yalnız daha çok iş yapmaktadır.

**İkinci neden: dış talep sabit, zincir yeniden deniyor.** Bağımlılık bozulmuştur, çağrılar
başarısız olmaktadır ve zincirin her katmanı kendi başına yeniden dener. İzinler çarpılır ve
bağımlılığa varan yük artık dışarıdan değil, sistemin kendisinden gelir. Buna **yeniden deneme
fırtınası** (retry storm) denir.

İkisi de bağımlılıkta aynı sayıyı büyütür. Çağrı hızına bakan bir uyarı ikisini ayıramaz.

#### Zincir

Aşağıdaki koşum bir **modeldir**: tur soyut bir adımdır ve gerçek küme kurulmaz. **KK15 — zincir
ve çizelge.** Üç katman, katman başına üç deneme, denemeler arası dört tur, bağımlılığın olağan
kapasitesi turda 12 çağrı, dış talep turda 4 istek. Gerekçe: çizelge önceki kurslarda
kararlaştırıldı ve burada girdi olarak alınır; kapasite payı dış talebin üç katıdır. Arıza,
kapasitenin 101–160 turları arasında 1 çağrıya inmesidir. Duyarlılığı deneme izni üzerinden
verilir. Bu varsayımlar K01'in tablosuna eklenmez.

```js
// firtina/zincir.mjs — uc katmanli zincirin MODELI. Tur soyut bir adimdir. Geri cekilme
// cizelgesi ve deneme butcesi burada hesaplanmaz; onceki kurslarda kararlastirilmis bir
// GIRDI olarak alinir (sabit aralik, katman basina sabit izin).
export const KK15 = { katman: 3, izin: 3, aralik: 4, kapasite: 12, disTalep: 4 };

export function kosum({ tur = 400, disTalep = KK15.disTalep, izin = KK15.izin,
                        arizaBas = 101, arizaSon = 160, arizaKapasite = 1 }) {
  const { katman, aralik, kapasite } = KK15;
  const uctan = [], cagri = [], basarili = [], dusen = [], kimlik = [];
  const aktif = [];                       // { id, sayac: [k0,k1,k2], zaman }
  let siradaki = 1, birikim = 0;

  for (let t = 1; t <= tur; t += 1) {
    birikim += typeof disTalep === "function" ? disTalep(t) : disTalep;
    let gelen = 0;
    while (birikim >= 1) {
      birikim -= 1; gelen += 1;
      aktif.push({ id: siradaki++, sayac: new Array(katman).fill(0), zaman: t });
    }
    uctan.push(gelen);

    const buTur = aktif.filter((r) => r.zaman === t);
    let jeton = t >= arizaBas && t <= arizaSon ? arizaKapasite : kapasite;
    let b = 0, d = 0;
    const gorulen = new Set();
    for (const r of buTur) {
      gorulen.add(r.id);                  // bagimlilik cagriyi gorur (reddedilse de)
      if (jeton > 0) { jeton -= 1; b += 1; aktif.splice(aktif.indexOf(r), 1); continue; }
      let k = katman - 1;                 // sayac odometresi: en derin katmandan yukari tasar
      while (k >= 0) {
        r.sayac[k] += 1;
        if (r.sayac[k] < izin) break;
        r.sayac[k] = 0; k -= 1;
      }
      if (k < 0) { d += 1; aktif.splice(aktif.indexOf(r), 1); }   // butun izinler tukendi
      else r.zaman = t + aralik;
    }
    cagri.push(buTur.length); basarili.push(b); dusen.push(d); kimlik.push(gorulen);
  }
  return { uctan, cagri, basarili, dusen, kimlik };
}

// Pencere ozeti: tekil kimlik pencerenin tamaminda sayilir, tur icinde degil.
export function pencere(r, bas, son) {
  const topla = (a) => a.slice(bas - 1, son).reduce((x, y) => x + y, 0);
  const n = son - bas + 1;
  const tekil = new Set();
  for (const s of r.kimlik.slice(bas - 1, son)) for (const id of s) tekil.add(id);
  const c = topla(r.cagri);
  return {
    uc: topla(r.uctan) / n, cagri: c / n, tekil: tekil.size / n,
    carpan: tekil.size === 0 ? 0 : c / tekil.size,
    basari: c === 0 ? 0 : topla(r.basarili) / c, dusen: topla(r.dusen),
  };
}
```

```js
// firtina/olcum.mjs — ayni belirtiyi veren iki neden ve firtinayi ayirt eden olcutler
import { KK15, kosum, pencere } from "./zincir.mjs";

const EN = [24, 8, 10, 10, 8, 8];
const yaz = (h) => console.log(h.map((x, i) => (i ? String(x).padStart(EN[i]) : String(x).padEnd(EN[i]))).join(" "));
const cizgi = () => console.log(EN.map((n) => "-".repeat(n)).join(" "));
const satir = (ad, p) => yaz([ad, p.uc.toFixed(2), p.cagri.toFixed(2), p.tekil.toFixed(2),
  p.carpan.toFixed(2), p.basari.toFixed(4)]);

console.log(`zincir ${KK15.katman} katman, katman basina ${KK15.izin} deneme, aralik ${KK15.aralik} tur,` +
  ` bagimliligin olagan kapasitesi ${KK15.kapasite} cagri/tur, dis talep ${KK15.disTalep} istek/tur`);
console.log(`aritmetik ust sinir: ${KK15.izin}^${KK15.katman} = ${KK15.izin ** KK15.katman} cagri/istek;` +
  ` pencere 101-160 turlari\n`);

// Neden 1: dis talep gercekten artiyor, ariza yok. Neden 2: talep sabit, bagimlilik bozuluyor.
const organik = kosum({ disTalep: (t) => (t >= 101 && t <= 160 ? 8 : 4), arizaKapasite: KK15.kapasite });
const firtina = kosum({});
const denemesiz = kosum({ izin: 1 });

yaz(["durum", "uc/tur", "cagri/tur", "tekil/tur", "carpan", "basari"]);
cizgi();
for (const [ad, r] of [["talep artti", organik], ["firtina", firtina], ["denemesiz", denemesiz]]) {
  satir(`${ad}: once`, pencere(r, 1, 100));
  satir(`${ad}: sirasinda`, pencere(r, 101, 160));
  satir(`${ad}: sonra`, pencere(r, 161, 400));
}

// Geri donus: ariza bittikten sonra cagri hizinin olagan seviyeye inmesi kac tur suruyor.
const olagan = pencere(firtina, 1, 100).cagri;
const donus = (r) => {
  for (let t = 161; t <= 400; t += 1) if (r.cagri[t - 1] <= olagan * 1.1) return t - 160;
  return -1;
};
// Karsilanan cagri/tur = cagri hizi x basari orani; kapasite sinirinda bu carpim sabittir.
const carp = (r) => { const p = pencere(r, 101, 160); return (p.cagri * p.basari).toFixed(2); };
console.log(`\ncagri hizi x basari orani (karsilanan cagri/tur): talep artti ${carp(organik)},` +
  ` firtina ${carp(firtina)}, denemesiz ${carp(denemesiz)}`);
console.log(`olagan cagri hizi ${olagan.toFixed(2)}/tur; ariza 160. turda bitiyor`);
console.log(`geri donus ${donus(firtina)} tur (denemesiz duzende ${donus(denemesiz)} tur);` +
  ` firtinanin tepe cagri hizi ${Math.max(...firtina.cagri)}/tur = olaganin` +
  ` ${(Math.max(...firtina.cagri) / olagan).toFixed(2)} kati`);

// Duzeltmenin karsiligi: izin kisildiginda ayni arizada ne kazanilip ne kaybediliyor.
console.log(`\nizni kismanin karsiligi (ayni ariza, 101-400 turlari):`);
yaz(["izin", "cagri/tur", "carpan", "dusen istek", "donus", "basari"]);
cizgi();
for (const izin of [3, 2, 1]) {
  const r = kosum({ izin });
  const p = pencere(r, 101, 160), o = pencere(r, 1, 100).cagri;
  let g = -1;
  for (let t = 161; t <= 400; t += 1) if (r.cagri[t - 1] <= o * 1.1) { g = t - 160; break; }
  yaz([`katman basina ${izin}`, p.cagri.toFixed(2), p.carpan.toFixed(2),
    pencere(r, 101, 400).dusen, g, p.basari.toFixed(4)]);
}
```

```
zincir 3 katman, katman basina 3 deneme, aralik 4 tur, bagimliligin olagan kapasitesi 12 cagri/tur, dis talep 4 istek/tur
aritmetik ust sinir: 3^3 = 27 cagri/istek; pencere 101-160 turlari

durum                      uc/tur  cagri/tur  tekil/tur   carpan   basari
------------------------ -------- ---------- ---------- -------- --------
talep artti: once            4.00       4.00       4.00     1.00   1.0000
talep artti: sirasinda       8.00       8.00       8.00     1.00   1.0000
talep artti: sonra           4.00       4.00       4.00     1.00   1.0000
firtina: once                4.00       4.00       4.00     1.00   1.0000
firtina: sirasinda           4.00      25.00       4.00     6.25   0.0400
firtina: sonra               4.00       6.50       4.75     1.37   0.7308
denemesiz: once              4.00       4.00       4.00     1.00   1.0000
denemesiz: sirasinda         4.00       4.00       4.00     1.00   0.2500
denemesiz: sonra             4.00       4.00       4.00     1.00   1.0000

cagri hizi x basari orani (karsilanan cagri/tur): talep artti 8.00, firtina 1.00, denemesiz 1.00
olagan cagri hizi 4.00/tur; ariza 160. turda bitiyor
geri donus 25 tur (denemesiz duzende 1 tur); firtinanin tepe cagri hizi 49/tur = olaganin 12.25 kati

izni kismanin karsiligi (ayni ariza, 101-400 turlari):
izin                     cagri/tur     carpan dusen istek    donus   basari
------------------------ -------- ---------- ---------- -------- --------
katman basina 3             25.00       6.25          0       25   0.0400
katman basina 2             22.33       5.58         68       17   0.0448
katman basina 1              4.00       1.00        180        1   0.2500
```

Bütün sayılar belirlenimli bir koşumdan çıkan **hesap** sınıfındadır; zincir, çizelge ve arıza
penceresi **varsayım** sınıfındadır.

#### Fırtına Dışarıdan Nasıl Görünür

İki senaryonun ortak yanı ilk bakışta ayırt edilemez: bağımlılığa varan çağrı hızı iki durumda da
büyüyor. Ayrımı üç ölçüt yapıyor ve üçü de bağımlılıkta toplanabilir.

**Uçtaki hız.** Talep arttığında uç 4,00'ten 8,00'e çıkıyor; fırtınada 4,00'te kalıyor. Bir çağrı
hızı artışı uçta karşılığı olmadan gerçekleşiyorsa, artan yükü sistemin kendisi üretiyordur.

**Tekil istek kimliği.** Bu, ölçütlerin en keskinidir. Talep arttığında bağımlılığın gördüğü tekil
kimlik 4,00'ten 8,00'e çıkıyor — yeni istekler var. Fırtınada tekil kimlik **4,00'te sabit**
kalırken çağrı 25,00'e çıkıyor: aynı istekler yeniden ve yeniden geliyor. Çağrı sayısının tekil
kimliğe oranı, gözlenen **çağrı çarpanıdır** ve burada 1,00'e karşı **6,25**. Aritmetik üst sınır
27; gözlenen ondan küçüktür, çünkü denemeler zamana yayılmıştır.

**Başarı oranı.** Talep arttığında başarı 1,0000'de kalıyor. Fırtınada çağrı hızı altı katına
çıkarken başarı 1,0000'den **0,0400**'e düşüyor. Hız ve başarı ters yönde hareket ediyorsa,
büyüyen şey iş değil, aynı işin tekrarıdır.

Dördüncü işaret zamandadır. Arıza 160. turda bitiyor, ama çağrı hızı olağan düzeye ancak **25 tur**
sonra iniyor; yeniden deneme yapmayan düzende bu süre 1 turdur. Fırtına, arızadan uzun yaşar.

#### Kapasite Sınırının İmzası

Bir sayı fırtınanın niteliğini de veriyor. Çağrı hızı ile başarı oranının çarpımı, bağımlılığın
gerçekten karşıladığı çağrı sayısıdır. Fırtınada bu çarpım 1,00; yeniden deneme yapmayan düzende
de 1,00. Yani yirmi beş kat daha çok çağrı, karşılanan çağrı sayısını **hiç değiştirmiyor** —
bağımlılığın kapasitesi neyse odur ve denemeler yalnız kendi paylarını küçültüyor. Talep arttığı
senaryoda aynı çarpım 8,00, yani uçtaki hızın kendisi.

Bu, denemenin ne zaman zararlı olduğunu söyleyen ayrımdır. Bağımlılık geçici bir hatayla tek tek
çağrıları düşürüyorsa denemeler çarpımı büyütür ve iş biter. Bağımlılık kapasite sınırındaysa
çarpım sabittir: her yeni deneme, başka bir isteğin denemesini düşürür. Dayanıklılık ve Güvenilirlik
kursunun ölçtüğü çarpan bu ikinci durumda doğrudan bir arıza uzatıcısına dönüşür.

#### Düzeltmenin Karşılığında Büyüyen

Son tablo düzeltmenin bedelini sayıyor. Deneme izni katman başına 3'ten 2'ye indiğinde çağrı hızı
25,00'ten 22,33'e, geri dönüş 25 turdan 17 tura iniyor — ve **68 istek düşüyor**. İzin 1'e
indiğinde fırtına tümüyle kayboluyor (çağrı 4,00, çarpan 1,00, geri dönüş 1 tur), ama düşen istek
**180**'e çıkıyor.

Karşılık nettir: fırtınayı kısmak, arıza penceresinde hiç yanıtlanmayan istek demektir. Üç izinle
tek bir istek bile düşmüyor; bedeli, bağımlılığın altı kat fazla çağrı görmesi ve arızanın yirmi
beş tur uzamasıdır. Bu değiş tokuş bir ölçüm olmadan yapılamaz, çünkü iki ucu da sayıdır.

#### Yeniden Denemenin Doğru Olduğu Koşul

Yeniden deneme, çağrı çarpanı bağımlılığın kapasite payının altında kaldığı sürece doğrudur.
Buradaki sayılarla: olağan çağrı hızı 4,00 ve kapasite 12 çağrı/tur, yani pay 3,00 kat. Gözlenen
çarpan 6,25 bu payı aşıyor ve fırtına tam da bu yüzden oluşuyor. Çarpan payın altında kalsaydı
denemeler bedelsiz olurdu.

İkinci koşul arızanın türüdür. Çağrı hızı ile başarı oranının çarpımı çağrı hızından bağımsız
sabit kalıyorsa bağımlılık kapasite sınırındadır ve deneme hakkını artırmak hiçbir isteği daha
kurtarmaz. Çarpım çağrı hızıyla birlikte büyüyorsa hata geçicidir ve deneme ucuzdur.

#### Özet

- Belirti bağımlılığa varan çağrı hızının büyümesidir; iki nedeni vardır — dış talep artmıştır ya
  da zincir yeniden denemektedir. Çağrı hızına bakan bir ölçüm ikisini ayıramaz.
- Uçtaki hız birinci nedende 4,00'ten 8,00'e çıkar, fırtınada 4,00'te kalır. Bağımlılığın gördüğü
  tekil istek kimliği birinci nedende 8,00'e çıkar, fırtınada 4,00'te sabit kalır.
- Gözlenen çağrı çarpanı 1,00'e karşı 6,25; başarı oranı 1,0000'e karşı 0,0400. Hız ve başarı ters
  yönde hareket ediyorsa büyüyen şey iş değil, aynı işin tekrarıdır.
- Fırtına arızadan uzun yaşar: kapasite 160. turda düzeldiği hâlde çağrı hızı 25 tur sonra olağana
  iniyor; yeniden deneme yapmayan düzende bu süre 1 tur.
- Çağrı hızı ile başarı oranının çarpımı fırtınada da denemesiz düzende de 1,00; yirmi beş kat çağrı
  karşılanan çağrı sayısını değiştirmiyor. Bu, bağımlılığın kapasite sınırında olduğunun imzasıdır.
- İzni kısmanın karşılığı düşen istektir: izin 3'te 0, 2'de 68, 1'de 180 istek düşüyor; buna
  karşılık geri dönüş 25 turdan 1 tura iniyor.

#### Sonraki Adım

Bu konu on belirtiyi on nedene bağladı ve her birinde ayrımı yapan şey aynı türden bir şey oldu:
bir sayı. Hesabın hangi katmanda yapıldığı taranan kayıtla, ana iş parçacığındaki iş kaynak
ayrımıyla, çağrı sayısı ile taşınan bayt arasındaki ödünleşim istek başına dokunuşla, gereksiz
alanlar uçtan uca bayt sayısıyla, pahalı istemci kurulum işiyle, tek parça kalıcılık kendi sınıfına
hizmet eden dizin payıyla, önbelleksizlik hesap tekrarıyla, gürültülü komşu iş payıyla, engelleyen
çağrı bağımlılık doluluğuyla ve yeniden deneme fırtınası tekil istek kimliğiyle ayrıldı.

Bu on tanının hepsi tek bir koşula bağlıdır: **o ölçüm var olmalıdır.** Oysa hangi ölçütün
toplanacağı bu konuda hiç tasarlanmadı. Tekil istek kimliği bağımlılıkta sayılıyor mu; iş payı
kiracıya göre tutuluyor mu; bağımlılığın kendi doluluğu görülüyor mu; hesap çağrıları anahtarıyla
birlikte kaydediliyor mu. Bunların hiçbiri kendiliğinden var olmaz; her biri bir tasarım kararıdır
ve her birinin bir bedeli vardır. Sonraki konu o katmanı kurar: hangi ölçüt kategorileri eksiksiz
bir izleme yüzeyi oluşturur, neyi ölçmeye değer, hangi eşik bir uyarı doğurur, yük nasıl sınanır
ve ölçülen sayı kaç düğüme çevrilir.

## İzleme ve Kapasite

Arızanın ve yavaşlığın görünür kılınması: izleme kategorilerinin erişilebilirlik, sağlık, başarım, kullanım ve güvenlik olarak eksiksiz kurulması, hangi ölçütün toplanmaya değdiğinin enstrümantasyon kararına çevrilmesi, karar verdiren görselleştirme ile uyarı eşiğinin ayrılması, yük, dayanıklılık ve ani yük testlerinin ayrı sorular sorması, ölçümden kaynak tahminine geçiş ve tasarım kararlarının işletme maliyetine kaynak birimiyle bağlanması.

### İzleme Kategorileri

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/izleme-kategorileri

Toplanan ölçüt kümesinin eksiksizliğinin bir kapsama ölçüsüyle sınanması: izleme ile gözlemlenebilirliğin ayrılması, hizmet erişilebilirliği, sağlık, başarım, kullanım ve güvenlik kategorilerinin sordukları soruya göre tanımlanması, bir kategori hiç toplanmadığında önceki konunun hangi tanısının yapılamaz kaldığının sayılması ve tanının tek bir ölçütle değil ölçüt bileşimiyle kurulduğunun gösterilmesi.

Önceki konu on tanı kurdu ve hepsini aynı üç adımla yürüttü: belirti bir sayıyla yazıldı, aynı
belirtiyi veren ikinci bir neden kuruldu, ikisini ayıran bir ölçüm gösterildi. Konu kapanışı bu on
dersin ortak koşulunu bıraktı — hepsi **bir ölçüm varsa** yapılabilir. O ölçümün nereden geldiği
ise hiçbir derste sorulmadı: hangi ölçütün toplandığı, hangisinin toplanmadığı ve toplanmayanın
hangi tanıyı elden aldığı hep varsayıldı.

Bu ders o boşluğu doldurur ve tek bir soruyla başlar: bir sistemden toplanan ölçüt kümesi ne zaman
**eksiksiz** sayılır? Eksiksizlik burada bir liste uzunluğu değil, bir kapsama ölçüsüdür — hangi
kategori boşsa hangi tanı yapılamaz. Ölçü bu yüzden önceki konunun on dersi üzerinde tanımlanır.

#### İzleme ve Gözlemlenebilirlik

**İzleme** (monitoring) önceden belirlenmiş soruların yanıtlarını sürekli toplamaktır;
**gözlemlenebilirlik** (observability) ise önceden sorulmamış bir soruya toplanmış veriden yanıt
verilebilmesidir. Bu ders izlemeyi kurar, çünkü sorulmamış soruya yanıt verebilmenin ön koşulu
sorulmuş soruların hangi boşluğu bıraktığını bilmektir.

Bir **ölçüt** (metric) bir soruya karşılık gelir ve kategorisi o sorunun türüdür. Beş kategori
şunları sorar:

- **Hizmet erişilebilirliği** (availability): istek yanıtlanabildi mi? Terim arayüz
  erişilebilirliğiyle karıştırılmamalıdır ve kısaltılmaz; ayrım Sistem Tasarımına Giriş kursunda
  yapıldı. Ölçütleri istek başarı oranı ve kesinti dakikasıdır; ikincisi doğrudan o kursun aylık
  43,2 dakikalık kesinti bütçesini ve içindeki 28,2 dakikalık arıza payını harcar.
- **Sağlık**: hangi kopya iş alabilir durumda? Sağlık ucunun içeriği — sığ denetim, hazır olma
  denetimi, derin denetim — Dayanıklılık ve Güvenilirlik kursunda tasarlandı ve burada yeniden
  tasarlanmaz; ucun ürettiği evet-hayır burada bir izleme ölçütü olarak yerine konur.
- **Başarım**: iş ne kadar sürüyor? Yanıt süresi yüzdelikleri ve alt çağrıların süreleri.
- **Kullanım**: kaynağın ne kadarı harcandı? Doluluk, taşınan bayt, istek başına çağrı sayısı,
  bellekteki nesne sayısı, önbellek isabet oranı, taranan kayıt sayısı, müşteri başına iş payı.
- **Güvenlik**: isteği kim gönderdi ve gönderme hakkı var mıydı? Reddedilen yetkisiz istek ve hız
  sınırı tetiklenmesi.

Başarım kategorisinin tek başına okunamamasının nedeni aynı kursta ölçülmüştü: bir gecikme sayısı
hangi yükte alındığı söylenmeden okunamaz. Tepe uçtaki 513,89 istek/s ile ölçülen bir yüzdelik ile
gece yarısında ölçülen aynı yüzdelik aynı sayı değildir. Bu yüzden her başarım ölçütü yanında bir
kullanım ölçütü ister.

#### Eksiksizlik Bir Kapsama Ölçüsüdür

Aşağıdaki hesap on tanıyı ve on altı ölçütü eşler, sonra her kategoriyi tek tek boşaltıp kaç tanının
yapılamaz kaldığını sayar. Eşlemenin kendisi bu dersin varsayımıdır (**IZ1**): önceki konu her tanı
için bir ayırt eden ölçüm gösterdi, aşağıdaki tablo o ölçümlerin karşılığı olan ölçütleri seçer ve
kategorilere yerleştirir. Başka bir eşleme başka sayılar verir. Çıkan sayılar hesap sınıfındadır.

```js
// izleme/kapsam.mjs — bes kategorinin eksiksizlik olcusu: Karsit Kaliplar konusundaki on tani
// hangi olcutu gerektiriyor, bir kategori hic toplanmazsa hangi tanilar yapilamaz kaliyor.
const OLCUT = {
  "istek basari orani": "erisilebilirlik",
  "kesinti dakikasi": "erisilebilirlik",
  "kopya havuz durumu": "saglik",
  "hazir olma durumu": "saglik",
  "yanit suresi yuzdeligi": "basarim",
  "depo sorgu suresi": "basarim",
  "istek basina cagri sayisi": "kullanim",
  "tasinan govde bayti": "kullanim",
  "islemci dolulugu": "kullanim",
  "havuz dolulugu": "kullanim",
  "bellekteki nesne sayisi": "kullanim",
  "onbellek isabet orani": "kullanim",
  "taranan kayit sayisi": "kullanim",
  "musteri basina is payi": "kullanim",
  "reddedilen yetkisiz istek": "guvenlik",
  "hiz siniri tetiklenmesi": "guvenlik",
};

const TANI = {
  "mesgul veritabani": ["depo sorgu suresi", "islemci dolulugu", "yanit suresi yuzdeligi"],
  "mesgul on yuz": ["tasinan govde bayti", "yanit suresi yuzdeligi"],
  "gevezelik eden g/c": ["istek basina cagri sayisi", "yanit suresi yuzdeligi"],
  "asiri veri cekme": ["tasinan govde bayti", "onbellek isabet orani"],
  "hatali nesne yaratimi": ["bellekteki nesne sayisi", "islemci dolulugu"],
  "tek parca kalicilik": ["taranan kayit sayisi", "depo sorgu suresi", "islemci dolulugu"],
  "onbelleksizlik": ["onbellek isabet orani", "islemci dolulugu", "yanit suresi yuzdeligi"],
  "gurultulu komsu": ["musteri basina is payi", "yanit suresi yuzdeligi", "hiz siniri tetiklenmesi"],
  "eszamanli g/c": ["havuz dolulugu", "hazir olma durumu", "yanit suresi yuzdeligi"],
  "yeniden deneme firtinasi": ["istek basari orani", "istek basina cagri sayisi",
    "kopya havuz durumu", "hiz siniri tetiklenmesi"],
};

const KATEGORI = [...new Set(Object.values(OLCUT))];
const kategorisi = (o) => OLCUT[o];
const tanilar = Object.keys(TANI);

console.log(`${KATEGORI.length} kategori, ${Object.keys(OLCUT).length} olcut, ${tanilar.length} tani`);
console.log(`\n${"kategori".padEnd(18)}${"olcut".padStart(7)}${"besledigi tani".padStart(16)}` +
  `${"kategori bos: dusen tani".padStart(26)}`);
const dusen = {};
for (const k of KATEGORI) {
  const olcutler = Object.keys(OLCUT).filter((o) => kategorisi(o) === k);
  dusen[k] = tanilar.filter((t) => TANI[t].some((o) => kategorisi(o) === k));
  console.log(`${k.padEnd(18)}${String(olcutler.length).padStart(7)}` +
    `${String(dusen[k].length).padStart(16)}` +
    `${`${dusen[k].length}/${tanilar.length}`.padStart(26)}`);
}
console.log(`hicbir kategori bos birakilamaz: en az yuk tasiyan kategori ` +
  `${KATEGORI.reduce((a, b) => (dusen[a].length <= dusen[b].length ? a : b))} ve o da ` +
  `${Math.min(...KATEGORI.map((k) => dusen[k].length))} taniyi dusuruyor`);

const sayim = Object.keys(OLCUT)
  .map((o) => [o, tanilar.filter((t) => TANI[t].includes(o)).length])
  .sort((a, b) => b[1] - a[1]);
const tekil = sayim.filter(([, n]) => n === 1).map(([o]) => o);
const sifir = sayim.filter(([, n]) => n === 0).map(([o]) => o);
console.log(`\nen cok paylasilan olcut = ${sayim[0][0]} (${sayim[0][1]}/${tanilar.length} tani), ` +
  `ikincisi ${sayim[1][0]} (${sayim[1][1]}/${tanilar.length})`);
console.log(`tek bir tanida gecen olcut (${tekil.length}): ${tekil.join(", ")}`);
console.log(`hicbir tanida gecmeyen olcut (${sifir.length}): ${sifir.join(", ")}`);

console.log(`\n${"tani".padEnd(26)}${"olcut".padStart(6)}${"kategori".padStart(10)}` +
  `${"ayirt eden (tek tanida gecen)".padStart(31)}`);
for (const t of tanilar) {
  const kats = new Set(TANI[t].map(kategorisi));
  const ayirt = TANI[t].filter((o) => tekil.includes(o));
  console.log(`${t.padEnd(26)}${String(TANI[t].length).padStart(6)}${String(kats.size).padStart(10)}` +
    `${(ayirt[0] ?? "-").padStart(31)}`);
}

const cift = [];
for (let i = 0; i < tanilar.length; i += 1)
  for (let j = i + 1; j < tanilar.length; j += 1) {
    const [a, b] = [tanilar[i], tanilar[j]];
    const ortak = TANI[a].filter((o) => TANI[b].includes(o));
    cift.push({ a, b, ortak });
  }
const enCok = Math.max(...cift.map((c) => c.ortak.length));
console.log(`\nen cok olcut paylasan tani ciftleri (ortak ${enCok} olcut):`);
for (const { a, b, ortak } of cift.filter((c) => c.ortak.length === enCok))
  console.log(`  ${a} / ${b} -> ortak ${ortak.join(" + ")}; ayiran ` +
    `${TANI[a].filter((o) => !ortak.includes(o)).join(" + ")} / ` +
    `${TANI[b].filter((o) => !ortak.includes(o)).join(" + ")}`);
```

```
5 kategori, 16 olcut, 10 tani

kategori            olcut  besledigi tani  kategori bos: dusen tani
erisilebilirlik         2               1                      1/10
saglik                  2               2                      2/10
basarim                 2               7                      7/10
kullanim                8              10                     10/10
guvenlik                2               2                      2/10
hicbir kategori bos birakilamaz: en az yuk tasiyan kategori erisilebilirlik ve o da 1 taniyi dusuruyor

en cok paylasilan olcut = yanit suresi yuzdeligi (6/10 tani), ikincisi islemci dolulugu (4/10)
tek bir tanida gecen olcut (7): istek basari orani, kopya havuz durumu, hazir olma durumu, havuz dolulugu, bellekteki nesne sayisi, taranan kayit sayisi, musteri basina is payi
hicbir tanida gecmeyen olcut (2): kesinti dakikasi, reddedilen yetkisiz istek

tani                       olcut  kategori  ayirt eden (tek tanida gecen)
mesgul veritabani              3         2                              -
mesgul on yuz                  2         2                              -
gevezelik eden g/c             2         2                              -
asiri veri cekme               2         1                              -
hatali nesne yaratimi          2         1        bellekteki nesne sayisi
tek parca kalicilik            3         2           taranan kayit sayisi
onbelleksizlik                 3         2                              -
gurultulu komsu                3         3         musteri basina is payi
eszamanli g/c                  3         3                 havuz dolulugu
yeniden deneme firtinasi       4         4             istek basari orani

en cok olcut paylasan tani ciftleri (ortak 2 olcut):
  mesgul veritabani / tek parca kalicilik -> ortak depo sorgu suresi + islemci dolulugu; ayiran yanit suresi yuzdeligi / taranan kayit sayisi
  mesgul veritabani / onbelleksizlik -> ortak islemci dolulugu + yanit suresi yuzdeligi; ayiran depo sorgu suresi / onbellek isabet orani
```

#### Kategori Boş Kalırsa

Birinci tablo eksiksizlik ölçüsünü veriyor. **Kullanım kategorisi on tanının onunu birden
besliyor:** yalnız hizmet erişilebilirliği ve yanıt süresi toplayan bir sistemde on tanının hiçbiri
yapılamaz. Bu şaşırtıcı görünmez ama sonucu serttir — bir belirtinin nedenini bulmak kaynağın ne
kadarının harcandığını bilmeyi gerektirir, oysa kullanım ölçütleri toplanması en çok ihmal edilen
kategoridir, çünkü kimse onlara bakmadan da hizmetin ayakta olduğunu görebilir.

Başarım yedi tanıyı besliyor. Kalan üç kategori bir ya da iki tanı besliyor ve **buradaki sayının
küçük olması onları isteğe bağlı yapmaz**: erişilebilirlik hiç toplanmazsa yeniden deneme fırtınası
kendiliğinden artan yükten ayrılamaz, sağlık hiç toplanmazsa eşzamanlı girdi/çıktı ile fırtına
ayırt edilemez, güvenlik hiç toplanmazsa gürültülü komşu ile fırtına ayırt edilemez. Düşen tanılar
başka kategoriden telafi edilmiyor; bu yüzden ölçü bir toplam değil, bir kapsamadır.

İki ölçüt hiçbir tanıda geçmiyor: kesinti dakikası ve reddedilen yetkisiz istek. Bu bir eksiklik
değil, kategorinin tanımının sonucudur. **Bir kategori yanıtladığı soruyla tanımlanır, mümkün
kıldığı tanılarla değil.** Kesinti dakikası hiçbir karşıt kalıbı ayırt etmez ama aylık 43,2
dakikalık bütçeyi harcayan tek sayıdır ve o bütçe olmadan hiçbir uyarı eşiği savunulamaz. Tanı
listesine göre budanan bir ölçüt kümesi, tanı dışındaki soruları kaybeder.

Varsayımın duyarlılığı da tabloda okunuyor. IZ1'deki eşlemede işlemci doluluğu dört tanıya girdi
veriyor ve meşgul veritabanı tanısının tek kullanım ölçütüdür; o ölçüt toplanmazsa bu tanının
kullanım ayağı tümüyle kalkar. Eşlemeyi değiştirmek sütunun sayılarını değiştirir, ama hiçbir
değişiklik beş kategoriden birini tümüyle yüksüz bırakmaz.

#### Tanı Ölçüt Bileşimidir

İkinci blok dersin ikinci sonucunu veriyor. Yanıt süresi yüzdeliği on tanının altısında geçiyor,
işlemci doluluğu dördünde. **En çok toplanan ölçüt en az ayırt eden ölçüttür.** Bir yanıt süresi
grafiğinin yükselmesi altı ayrı nedenin ortak belirtisidir ve tek başına hiçbirini göstermez; önceki
konunun "yavaş bir tanı değildir" cümlesinin sayısal karşılığı budur.

Yedi ölçüt tek bir tanıda geçiyor ve bunlar imza ölçütleridir: bellekteki nesne sayısı, müşteri
başına iş payı, taranan kayıt sayısı, havuz doluluğu, hazır olma durumu, kopya havuz durumu ve istek
başarı oranı. Beş tanının imza ölçütü var, beşinin yok; ikinciler yalnız **bileşimle** ayrılır. Son
iki satır bunun nasıl olduğunu gösteriyor.
Meşgul veritabanı ile önbelleksizlik iki ölçütü paylaşıyor — işlemci doluluğu ve yanıt süresi
yüzdeliği — ve ayrımı üçüncü ölçüt yapıyor: depo sorgu süresi mi büyüdü, yoksa önbellek isabet
oranı mı düştü? Aynı biçimde meşgul veritabanı ile tek parça kalıcılık depo sorgu süresi ve işlemci
doluluğunu paylaşıyor; ayrımı taranan kayıt sayısı yapıyor, çünkü tek depoya yığılmanın belirtisi
bir sorgunun kendi veri sınıfının kat kat üstünde kayıt taramasıdır.

Bu, enstrümantasyon kararının biçimini de belirler: bir ölçüt tek başına değil, ayırt ettiği çift
için toplanır. Bir ölçütün değerini "kaç tanıda geçtiği" değil, "hangi iki nedeni birbirinden
ayırdığı" verir.

#### Özet

- İzleme önceden belirlenmiş soruların yanıtını toplar; gözlemlenebilirlik önceden sorulmamış bir
  soruya toplanmış veriden yanıt verebilmektir.
- Beş kategori sordukları soruyla tanımlanır: hizmet erişilebilirliği, sağlık, başarım, kullanım ve
  güvenlik; sağlık ucunun içeriği önceki kursta tasarlandı ve burada yalnız kategori olarak konur.
- Eksiksizlik bir kapsama ölçüsüdür: kullanım kategorisi on tanının onunu, başarım yedisini besler;
  erişilebilirlik bir, sağlık ve güvenlik ikişer tanı besler ve hiçbiri boş bırakılamaz.
- Kesinti dakikası ve reddedilen yetkisiz istek hiçbir tanıda geçmez ama toplanır; bir kategori
  mümkün kıldığı tanılarla değil, yanıtladığı soruyla tanımlanır.
- En çok paylaşılan ölçüt en az ayırt eden ölçüttür: yanıt süresi yüzdeliği altı tanıda geçer, bu
  yüzden tek başına hiçbirini göstermez.
- On altı ölçütten yedisi tek bir tanıda geçer ve o tanının imzasıdır; beş tanının imza ölçütü yoktur
  ve yalnız bileşimle ayrılır: meşgul veritabanı ile önbelleksizlik iki ölçütü paylaşır, ayrımı depo
  sorgu süresi ile önbellek isabet oranı yapar.

#### Sonraki Adım

Bu ders on altı ölçütü bir listeye koydu ve listenin eksiksizliğini kapsamayla sınadı, ama listenin
bir bedeli olduğunu hiç hesaba katmadı. Her ölçüt üretilir, taşınır, saklanır ve okunur; her biri
bir kod yoluna eklenir ve o kod yolu tepe uçtaki 513,89 isteğin her birinde çalışır. Ölçüt sayısı
arttıkça toplanan kayıt sayısı da artar ve bir noktada izleme sisteminin ürettiği yük, izlediği
sistemin yüküyle karşılaştırılabilir hâle gelir. Sonraki ders bu bedeli sayar: bir ölçütün istek
başına ürettiği kayıt, gün başına saklanan bayt ve eklediği kod yolu bir yanda, mümkün kıldığı tanı
öbür yanda tartılır; örnekleme ile toplulaştırmanın ucuzlatırken hangi bilgiyi kaybettiği ölçülür.

### Enstrümantasyon Tasarımı

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/enstrumantasyon-tasarimi

Bir ölçütün toplanmaya değip değmediğinin iki yanlı tartılması: üretilen kayıt, saklanan bayt ve eklenen kod yolu cinsinden bedelin hesaplanması, örnekleme ile toplulaştırmanın kazandırdığı hacmin ve kaybettirdiği bilginin aynı nüfus üzerinde ölçülmesi, yakın bedelli iki biçimin yüzdelik hatasının karşılaştırılması ve toplama biçiminin mümkün kıldığı tanıya göre seçilmesi.

Önceki ders on altı ölçütü beş kategoriye yerleştirdi ve listenin eksiksizliğini kapsamayla sınadı.
Sınama tek yanlıydı: her ölçüt yalnız mümkün kıldığı tanıyla değerlendirildi, hiçbirinin bedeli
sayılmadı. Oysa her ölçüt üretilir, taşınır, saklanır ve bir kod yoluna eklenir; eklendiği kod yolu
tepe uçtaki 513,89 isteğin her birinde çalışır.

Bu ders kararı iki yanlı hâle getirir. Bir ölçüt toplanmaya değer mi sorusunun yanıtı, bedeli ile
mümkün kıldığı tanının karşılaştırılmasından çıkar. İkisi de sayıya çevrilir.

#### Ölçütün Üç Maliyet Kalemi

**Enstrümantasyon** (instrumentation), bir ölçütü üretmek için koda eklenen çağrılardır. Bedeli üç
kalemdir. **Üretilen kayıt**: birim zamanda kaç ölçüt kaydı doğuyor. **Saklanan bayt**: o kayıtlar
ne kadar süre ne kadar yer tutuyor. **Eklenen kod yolu**: her istekte kaç enstrümantasyon çağrısı
çalışıyor.

Üçüncü kalem ilk ikisinden farklıdır ve doğrudan sayılamaz — bir sayaç artırmakla bir olay kaydı
oluşturup kuyruğa koymak aynı iş değildir. Bu derste kod yolu, **istek başına üretilen kayıt
sayısıyla** temsil edilir: bir istekte bir kayıt üreten biçim, saniyede bir kayıt üreten biçimden
daha çok kod yolu çalıştırır. Aşağıdaki hesabın ilk sütunu bu yüzden hem kayıt hem kod yolu
kalemidir.

Aylık hacim **GB-ay** ile yazılır: bir ay boyunca tutulan bir gigabayt. Para birimi kullanılmaz,
çünkü bir fiyat sağlayıcıya ve tarihe bağlıdır; kaynak birimi bağlı değildir. Bu kural kursun
kalanında da geçerlidir.

```js
// izleme/maliyet.mjs — bes toplama biciminin bedeli: uretilen kayit, saklanan bayt ve
// sistemin kendi yazma hizina orani. Girdiler K01'in hesaplari ve bu dersin varsayimlaridir.
const TEPE_UC = 513.89;      // K01: tepe uctaki istek/s
const TEPE_YAZMA = 97.22;    // K01: tepe yazma istek/s (sistemin kendi kalici kaydi)
const OLAY_BAYT = 180;       // IZ2 (varsayim): bir ham olcut kaydi (ad, deger, zaman, uc etiket)
const KOVA_BAYT = 40;        // IZ3 (varsayim): bir toplulastirilmis kova kaydi
const KOVA = 20;             // IZ4 (varsayim): histogram kova sayisi
const SAKLAMA = 30;          // IZ5 (varsayim): ham kaydin saklanma suresi (gun)
const b = (x, n = 2) => x.toFixed(n);

const BICIM = [
  ["ham olay", TEPE_UC, OLAY_BAYT],
  ["1/10 orneklem", TEPE_UC / 10, OLAY_BAYT],
  ["1/100 orneklem", TEPE_UC / 100, OLAY_BAYT],
  ["saniyelik histogram", KOVA, KOVA_BAYT],
  ["saniyelik sayac+toplam", 2, KOVA_BAYT],
];

console.log(`${"toplama bicimi".padEnd(23)}${"kayit/s".padStart(10)}${"bayt/s".padStart(10)}` +
  `${"gunluk GB".padStart(11)}${"GB-ay (30 gun)".padStart(16)}${"sistem yazmasina oran".padStart(23)}`);
for (const [ad, hiz, bayt] of BICIM) {
  const gunlukGB = (hiz * bayt * 86_400) / 1e9;
  console.log(`${ad.padEnd(23)}${b(hiz).padStart(10)}${b(hiz * bayt, 0).padStart(10)}` +
    `${b(gunlukGB, 3).padStart(11)}${b(gunlukGB * SAKLAMA, 2).padStart(16)}` +
    `${(b(hiz / TEPE_YAZMA) + " kat").padStart(23)}`);
}

const ham = TEPE_UC * OLAY_BAYT * 86_400 / 1e9;
const hist = KOVA * KOVA_BAYT * 86_400 / 1e9;
console.log(`\nham olay / saniyelik histogram = ${b(ham / hist, 0)} kat hacim`);
console.log(`ornekleme istek hiziyla buyur, toplulastirma buyumez: istek hizi 2 katina ciksa ` +
  `ham ${b(2 * ham, 3)} GB/gun, histogram ${b(hist, 3)} GB/gun`);
console.log(`10 olcut ayni bicimde toplanirsa hacim 10 katina cikar: ham ${b(10 * ham, 2)} GB/gun`);
```

```
toplama bicimi            kayit/s    bayt/s  gunluk GB  GB-ay (30 gun)  sistem yazmasina oran
ham olay                   513.89     92500      7.992          239.76               5.29 kat
1/10 orneklem               51.39      9250      0.799           23.98               0.53 kat
1/100 orneklem               5.14       925      0.080            2.40               0.05 kat
saniyelik histogram         20.00       800      0.069            2.07               0.21 kat
saniyelik sayac+toplam       2.00        80      0.007            0.21               0.02 kat

ham olay / saniyelik histogram = 116 kat hacim
ornekleme istek hiziyla buyur, toplulastirma buyumez: istek hizi 2 katina ciksa ham 15.984 GB/gun, histogram 0.069 GB/gun
10 olcut ayni bicimde toplanirsa hacim 10 katina cikar: ham 79.92 GB/gun
```

Son sütun ölçünün en sert hâlidir. Her isteğe bir ölçüt kaydı düşürmek, tek bir ölçüt için sistemin
kendi kalıcı yazmasının **5,29 katı** kayıt üretir: gönderi takip servisi saniyede 97,22 durum
olayı yazarken izleme 513,89 kayıt yazar. Önceki dersin listesinde on altı ölçüt vardı; hepsi bu
biçimde toplansa izleme yükü sistemin yükünün yanında küçük bir kalem olmaktan çıkar. Son satır
bunu doğrudan söylüyor: on ölçüt ham olayla günde 79,92 GB üretir.

İki biçim iki ayrı büyümeye bağlıdır ve fark tasarım kararını belirler. **Örnekleme istek hızıyla
büyür**, çünkü örneklenen şey isteklerdir; istek hızı iki katına çıktığında ham hacim 7,992'den
15,984 GB'a çıkar. **Toplulaştırma istek hızıyla büyümez**, çünkü üretilen kayıt sayısı kova
sayısıyla ve toplama aralığıyla belirlenir; aynı ikiye katlamada histogram 0,069 GB'da kalır.
Ölçeklenen bir sistemde bu, izleme bedelinin ölçekle birlikte artıp artmayacağı sorusudur.

#### Kazanç Kayıpla Ölçülür

Bedeli düşüren her biçim bir şey kaybettirir ve kaybın adı konmadan karar verilemez. Kaybı ölçmek
için tek bir nüfus üzerinde beş biçim birden uygulanır. Aşağıdaki düzenek bir **modeldir**:
gecikmeler tam sayı aritmetiğiyle, görünür bir tohumla üretilir, bu yüzden koşum makineden bağımsız
olarak tekrarlanır. Model gerçek bir dağılımı öngörmez; gösterdiği şey biçimlerin aynı veriye ne
yaptığıdır.

Ölçülen büyüklük **kuyruk gecikmesi** (tail latency), yani dağılımın en yavaş ucudur — bekleyen
işlerin dizisi anlamındaki kuyrukla ilgisi yoktur, buradaki kuyruk dağılımın kuyruğudur. Yüzdelikler
Sistem Tasarımına Giriş kursunda tanımlandı ve burada yeniden tanımlanmaz.

```js
// izleme/kayip.mjs — orneklemenin ve toplulastirmanin bilgi kaybi MODELI. Gecikmeler tam sayi
// aritmetigiyle uretilir (tohum gorunur), boylece kosum makineden bagimsiz tekrarlanir.
const N = 200_000;              // model nufusu: 400 saniye x saniyede 500 istek
const SANIYE = 500;             // saniye basina istek (K01'in 513,89 tepe hizina yakin)
const TOHUM = 20260730;
let s = TOHUM;
const rast = (n) => Math.floor((((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32)) * n);

// Gecikmeler 0,1 ms biriminde tam sayidir: govde, orta kuyruk, uzak kuyruk, en uzak kuyruk.
const KATMAN = [[900, 30, 30], [90, 60, 140], [9, 200, 700], [1, 900, 3100]];
const veri = [];
for (const [pay, taban, genislik] of KATMAN)
  for (let i = 0; i < (N * pay) / 1000; i += 1) veri.push(taban + rast(genislik));
for (let i = veri.length - 1; i > 0; i -= 1) {          // tohumlu karistirma
  const j = rast(i + 1);
  [veri[i], veri[j]] = [veri[j], veri[i]];
}

const yuzdelik = (dizi, p) => {
  const d = [...dizi].sort((a, b) => a - b);
  return d[Math.min(d.length - 1, Math.floor((p / 100) * d.length))];
};
const ms = (x) => (x / 10).toFixed(1);
const enBuyuk = (d) => d.reduce((a, c) => (c > a ? c : a), 0);

const KENAR = [30, 40, 50, 60, 80, 100, 130, 170, 220, 290, 380, 500, 650, 850, 1100, 1500,
  2000, 2600, 3400, Infinity];
const kova = new Array(KENAR.length).fill(0);
for (const v of veri) kova[KENAR.findIndex((k) => v < k)] += 1;
const kovaYuzdelik = (p) => {
  let toplam = 0;
  const hedef = (p / 100) * veri.length;
  for (let i = 0; i < kova.length; i += 1) {
    toplam += kova[i];
    if (toplam >= hedef) return KENAR[i];
  }
  return Infinity;
};
const kovaEnBuyuk = KENAR[kova.findLastIndex((k) => k > 0)];

const ortalamalar = [];
for (let i = 0; i < veri.length; i += SANIYE)
  ortalamalar.push(veri.slice(i, i + SANIYE).reduce((a, c) => a + c, 0) / SANIYE);

const her = (k) => veri.filter((_, i) => i % k === 0);
const tam = { p50: yuzdelik(veri, 50), p99: yuzdelik(veri, 99), p999: yuzdelik(veri, 99.9),
  enB: enBuyuk(veri) };

console.log(`tohum ${TOHUM}, ${N} istek, ${veri.length / SANIYE} saniye, ${KENAR.length} kova`);
console.log(`gercek deger: p50 ${ms(tam.p50)} ms, p99 ${ms(tam.p99)} ms, ` +
  `p99,9 ${ms(tam.p999)} ms, en buyuk ${ms(tam.enB)} ms`);
console.log(`\n${"toplama bicimi".padEnd(23)}${"saklanan kayit".padStart(15)}${"p50".padStart(8)}` +
  `${"p99".padStart(8)}${"p99,9".padStart(9)}${"en buyuk".padStart(10)}${"p99 hatasi".padStart(12)}`);

const yaz = (ad, kayit, p50, p99, p999, enB) =>
  console.log(`${ad.padEnd(23)}${kayit.toLocaleString("tr-TR").padStart(15)}${ms(p50).padStart(8)}` +
    `${ms(p99).padStart(8)}${ms(p999).padStart(9)}${ms(enB).padStart(10)}` +
    `${(((p99 - tam.p99) / tam.p99) * 100).toFixed(1).padStart(11)}%`);

yaz("ham olay", veri.length, tam.p50, tam.p99, tam.p999, tam.enB);
for (const k of [10, 100]) {
  const o = her(k);
  yaz(`1/${k} orneklem`, o.length, yuzdelik(o, 50), yuzdelik(o, 99), yuzdelik(o, 99.9),
    enBuyuk(o));
}
yaz("saniyelik histogram", (veri.length / SANIYE) * KENAR.length, kovaYuzdelik(50),
  kovaYuzdelik(99), kovaYuzdelik(99.9), kovaEnBuyuk);
yaz("saniyelik sayac+toplam", (veri.length / SANIYE) * 2, yuzdelik(ortalamalar, 50),
  yuzdelik(ortalamalar, 99), yuzdelik(ortalamalar, 99.9), enBuyuk(ortalamalar));

const genelOrt = veri.reduce((a, c) => a + c, 0) / veri.length;
const ortOrt = ortalamalar.reduce((a, c) => a + c, 0) / ortalamalar.length;
console.log(`\nortalama iki bicimde de tam korunuyor: ham ${ms(genelOrt)} ms, ` +
  `saniyelik ortalamalarin ortalamasi ${ms(ortOrt)} ms`);
const yavas = veri.filter((v) => v >= 200).length;
console.log(`200 ms ustu istek: ham veride ${yavas}, 1/100 orneklemde ` +
  `${her(100).filter((v) => v >= 200).length} (beklenen ${(yavas / 100).toFixed(1)})`);
```

```
tohum 20260730, 200000 istek, 400 saniye, 20 kova
gercek deger: p50 4.6 ms, p99 20.0 ms, p99,9 90.3 ms, en buyuk 399.5 ms

toplama bicimi          saklanan kayit     p50     p99    p99,9  en buyuk  p99 hatasi
ham olay                       200.000     4.6    20.0     90.3     399.5        0.0%
1/10 orneklem                   20.000     4.6    20.4     91.3     397.6        2.0%
1/100 orneklem                   2.000     4.6    38.6    351.8     397.6       93.0%
saniyelik histogram              8.000     5.0    22.0    110.0  Infinity       10.0%
saniyelik sayac+toplam             800     5.8     7.9      8.8       8.8      -60.5%

ortalama iki bicimde de tam korunuyor: ham 5.9 ms, saniyelik ortalamalarin ortalamasi 5.9 ms
200 ms ustu istek: ham veride 2000, 1/100 orneklemde 22 (beklenen 20.0)
```

#### Aynı Bedel, Farklı Kayıp

İki tablo yan yana okunduğunda dersin ana sonucu çıkıyor. **1/100 örneklem 2,40 GB-ay tutuyor,
saniyelik histogram 2,07 GB-ay** — bedelleri birbirine yakın. Kayıpları yakın değil: örneklemin
p99 hatası yüzde 93,0, histogramınki yüzde 10,0. Örneklemde p99,9 tümüyle anlamsızlaşıyor, çünkü
2.000 kayıtta o yüzdelik yalnız iki kayda dayanıyor; gerçek 90,3 ms yerine 351,8 ms okunuyor. **Aynı
bedelle çok daha az bilgi kaybeden bir biçim varken kaybeden biçimi seçmek bir karar değil, bir
gözden kaçırmadır.**

Örneklemenin ne kaybettiği son satırda açıkça duruyor: ham veride 200 ms üstü 2.000 istek var,
1/100 örneklemde 22 tanesi görülüyor. Sayı beklenen 20,0'a yakın, yani örnekleme yanlı değil —
kaybolan şey kesinlik. Bir yüzdelik ne kadar uçtaysa onu taşıyan kayıt sayısı o kadar azdır ve
örnekleme önce en uçtaki bilgiyi siler. 1/10 örneklem hâlâ 20.000 kayıt tutuyor ve p99 hatası
yüzde 2,0; kaybın örnekleme oranıyla doğrusal büyümediği buradan görünür.

Toplulaştırmanın kaybı başka biçimdedir. Histogram bütün istekleri sayar, hiçbirini atmaz, ama
değeri kova sınırına yuvarlar: p99 20,0 yerine 22,0 okunuyor ve hata kova genişliğinden gelir. En
büyük değer sütununda ise `Infinity` yazıyor — en üstteki kovanın üst sınırı olmadığı için histogram
en büyük gecikmeyi hiç sınırlayamıyor. Bunu geri getirmenin yolu saniye başına bir sayı daha
saklamaktır: en büyük değer. Bir sayının bedeli 40 bayttır.

Saniyelik sayaç ve toplam ise dağılımı tümüyle siliyor. Ortalama her iki biçimde de birebir
korunuyor (5,9 ms), ama saniyelik ortalamaların p99'u 7,9 ms ve gördüğü en büyük değer 8,8 ms;
gerçek en büyük 399,5 ms. Beş yüz isteğin ortalaması alındığında bir yavaş istek dört yüz
doksan dokuz hızlı istek tarafından yutulur. **Ortalama gecikme bildiren bir ölçüt, kuyruk
gecikmesini ölçmez; bu biçimin ucuzluğu tam olarak o bilgiyi atmasından gelir.**

#### Karar Kuralı

Üç sonuç, ölçüt başına verilecek kararın kuralını verir. Birincisi: bir toplama biçimi bedeliyle
değil, **bedel başına koruduğu bilgiyle** seçilir. İkincisi: biçim, ölçütün mümkün kılacağı tanıya
göre seçilir. Histogram "ne kadar yavaş" sorusunu ucuza yanıtlar ama "hangi istek yavaştı"
sorusunu yanıtlayamaz, çünkü kayıt kimliği yoktur; örneklem tersini yapar — az sayıda ama tam
kayıt tutar, dolayısıyla etiketleriyle birlikte bir isteğin izini verir. Önceki dersin on tanısından
ölçüt bileşimiyle ayrılanlar tam kayıt ister, eşik aşımını görmek isteyenler histogramla yetinir.

Üçüncüsü ölçekleme kuralıdır: ham olay ve örneklem istek hızıyla büyür, toplulaştırma büyümez.
Bugün 513,89 istek/s'de 2,40 GB-ay tutan bir örneklem, trafik iki katına çıktığında 4,80 GB-ay
tutar; aynı koşulda histogram 2,07 GB-ay'da kalır. Bir izleme tasarımı sistemin ölçekleme
planından bağımsız yapılamaz.

#### Özet

- Bir ölçütün bedeli üç kalemdir: üretilen kayıt, saklanan bayt ve eklenen kod yolu; üçüncüsü istek
  başına üretilen kayıt sayısıyla temsil edilir. Hacim GB-ay ile yazılır, para birimiyle değil.
- Her isteğe bir kayıt düşüren tek bir ölçüt, sistemin kendi kalıcı yazmasının 5,29 katı kayıt
  üretir (513,89'a karşı 97,22 kayıt/s); on ölçüt aynı biçimde günde 79,92 GB eder.
- Örnekleme istek hızıyla büyür, toplulaştırma büyümez: istek hızı ikiye katlandığında ham hacim
  7,992'den 15,984 GB/güne çıkar, histogram 0,069 GB/günde kalır.
- 1/100 örneklem ile saniyelik histogram yakın bedeldedir (2,40 ve 2,07 GB-ay) ama p99 hataları
  yüzde 93,0 ve yüzde 10,0'dır; aynı bedelde daha az kaybeden biçim seçilir.
- Örnekleme önce en uçtaki bilgiyi siler (2.000 yavaş istekten 22'si görünür); toplulaştırma değeri
  kova sınırına yuvarlar ve üst kova sınırsız olduğu için en büyük değeri hiç sınırlayamaz.
- Ortalama saklamak dağılımı siler: saniyelik ortalamaların gördüğü en büyük değer 8,8 ms iken
  gerçek en büyük 399,5 ms'dir; ortalama her iki biçimde de 5,9 ms olarak korunur.

#### Sonraki Adım

Bu iki ders neyin toplanacağını ve hangi biçimde toplanacağını kararlaştırdı. Toplanan sayı henüz
kimseye bir şey söylemiyor: bir yerde çizilecek ya da birini uyandıracak. İkisi aynı şey değildir
ve aynı eşikle çalışmazlar. Bir çizim, ona zaten bakmakta olan birinin sorusunu yanıtlar; bir uyarı,
bakmayan birini bakmaya çağırır ve yanlış çağırdığında bir maliyeti vardır. Sonraki ders ikisini
ayırır, uyarı eşiğinin nereden geldiğini Sistem Tasarımına Giriş kursunun aylık 43,2 dakikalık
kesinti bütçesine bağlar ve eşik seçimini iki sayıyla sınar: kaç yanlış uyarı üretiyor ve kaç
gerçek olayı kaçırıyor.

### Gösterge Tabloları ve Uyarılar

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/gosterge-tablolari-ve-uyarilar

Toplanan ölçütün karara çevrilmesi: gösterge tablosu ile uyarının ayrı sorular yanıtladığının gösterilmesi, bir ay boyunca eşik ve pencere çiftlerinin taranarak yanlış uyarı ile kaçırılan olayın sayılması, eşiği yükseltmenin olay kaçırdığının ve pencereyi uzatmanın yalnız kısa gürültüyü kestiğinin ölçülmesi ve eşiğin K01'in aylık kesinti bütçesinden tüketim hızı olarak türetilmesi.

Önceki iki ders neyin, hangi biçimde ve ne bedele toplanacağını kararlaştırdı. Toplanan sayı henüz
kimseye bir şey söylemiyor. İki yoldan biriyle karara dönüşür: bir yerde çizilir ve ona bakan biri
okur, ya da bir eşiği aşınca bakmayan birini çağırır. İki yol aynı sayıyı kullanır ve **aynı eşikle
çalışmaz**.

Bu ders ikisini ayırır, sonra çağıran yolun tek parametresini — eşiği — bir tarama ile sınar ve
nereden geldiğini Sistem Tasarımına Giriş kursunun aylık kesinti bütçesine bağlar.

#### İki Ayrı Soru

Bir **gösterge tablosu** (dashboard) zaten bakmakta olan birinin sorusunu yanıtlar. Sorusu şudur:
şu an olağan mı ve olağan değilse hangi yönde. Bu yüzden her panelin yanında **beklenen aralık**
yazılır; aralıksız bir çizgi grafiği yalnız bir şeklin yukarı ya da aşağı gittiğini gösterir ve
karar taşımaz.

Bir **uyarı** (alert) bakmayan birini çağırır. Sorusu başkadır: şimdi bir şey yapılmalı mı. Buradan
tek kural çıkar — **her uyarı bir eyleme bağlıdır.** Karşılığında yapılacak bir iş yoksa toplanan
ölçüt gösterge tablosuna girer, uyarıya değil. Bu ayrım araç seçimiyle ilgili değildir; iki ayrı
soruya iki ayrı yanıt üretmekle ilgilidir.

İlk ekranın içeriği önceki dersin kategori listesinden çıkar: beş kategoriden birer ölçüt, ölçülen
yükle birlikte. Bir başarım paneli yanında bir kullanım paneli olmadan okunamaz, çünkü aynı yanıt
süresi 513,89 istek/s ile gece yarısı arasında aynı şeyi anlatmaz. **Panelin beklenen aralığı ile
uyarı eşiği aynı sayı değildir:** aralık "olağan mı" sorusunu, eşik "birini uyandırmalı mıyım"
sorusunu yanıtlar ve ikincisi her zaman daha geniştir.

#### Eşik Bir Tarama İşidir

Bir eşik iki hatayla ölçülür. **Yanlış uyarı**, gerçek bir olay yokken tetiklenen uyarıdır ve
bedeli doğrudandır: her biri bir kişinin dikkatini alır, biriktiğinde uyarıların hepsinin
güvenilirliğini düşürür. **Kaçırılan uyarı**, gerçek bir olayda tetiklenmeyen uyarıdır ve bedeli
bütçeden ödenir.

Aşağıdaki düzenek bir **modeldir**: bir ayın 43.200 dakikasının her biri için hata sayısı tohumlu
tam sayı aritmetiğiyle üretilir, altı gerçek olay bilinen bir takvimle yerleştirilir (IZ6) ve
gürültü ayrı bir varsayımla tanımlanır (IZ7). Model gerçek bir arıza dağılımını öngörmez; sınadığı
şey, aynı veri üzerinde farklı eşik ve pencere çiftlerinin ne yaptığıdır.

```js
// uyari/tarama.mjs — uyari esigi taramasi MODELI. Bir ayin her dakikasi icin hata sayisi
// tohumlu tam sayi aritmetigiyle uretilir; her esik-pencere cifti icin yanlis uyari,
// kacirilan olay ve algilama gecikmesi sayilir.
const DAKIKA = 43_200;            // K01: 30 gunluk ay
const ISTEK_DK = 30_833;          // K01: 513,89 istek/s x 60
const TOHUM = 20260731;
let s = TOHUM;
const rast = (n) => Math.floor((((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32)) * n);

// IZ6 (varsayim): ayda alti gercek olay [baslangic dakikasi, sure, siddet (binde)]
const OLAY = [[3000, 240, 8], [8000, 30, 15], [15000, 3, 30], [21000, 20, 120],
  [30000, 4, 800], [38000, 45, 400]];

// IZ7 (varsayim): gurultu modeli — taban hata ve 240 dakikada bir tek dakikalik zirve
const hata = new Array(DAKIKA);
for (let t = 0; t < DAKIKA; t += 1) {
  let h = rast(40);
  if (rast(240) === 0) h += rast(1000);
  hata[t] = h;
}
for (const [bas, sure, siddet] of OLAY)
  for (let t = bas; t < bas + sure; t += 1) hata[t] += Math.round((ISTEK_DK * siddet) / 1000);

const oran = hata.map((h) => h / ISTEK_DK);
const olayIcinde = (t) => OLAY.findIndex(([bas, sure]) => t >= bas && t < bas + sure);
const kayipDk = OLAY.reduce((a, [, sure, siddet]) => a + (sure * siddet) / 1000, 0);

console.log(`tohum ${TOHUM}, ${DAKIKA} dakika, ${OLAY.length} gercek olay, ` +
  `toplam ${OLAY.reduce((a, o) => a + o[1], 0)} dakika bozulma`);
console.log(`olaylarin butceden yedigi: ${kayipDk.toFixed(2)} dakika-esdegeri ` +
  `(K01'in ariza payi 28,2 dakika)`);
const gurultu = oran.filter((_, t) => olayIcinde(t) < 0);
console.log(`gurultu: en yuksek dakika %${(gurultu.reduce((a, c) => (c > a ? c : a), 0) * 100).toFixed(2)}, ` +
  `%0,5 esigini asan ${gurultu.filter((o) => o >= 0.005).length} dakika, ` +
  `%2 esigini asan ${gurultu.filter((o) => o >= 0.02).length} dakika`);

function tara(esik, pencere) {
  const r = { uyari: 0, yanlis: 0, yakalanan: new Set(), gecikme: [] };
  let etkin = false;
  for (let t = pencere - 1; t < DAKIKA; t += 1) {
    let hepsi = true;
    for (let k = 0; k < pencere; k += 1) if (oran[t - k] < esik) hepsi = false;
    if (hepsi === false) { etkin = false; continue; }
    if (etkin) continue;
    etkin = true;
    r.uyari += 1;
    const i = olayIcinde(t);
    if (i < 0) r.yanlis += 1;
    else if (r.yakalanan.has(i) === false) {
      r.yakalanan.add(i);
      r.gecikme.push(t - OLAY[i][0]);
    }
  }
  return r;
}

const ESIK = [0.005, 0.01, 0.02, 0.05];
const PENCERE = [1, 3, 5];
console.log(`\n${"esik".padStart(7)}${"pencere".padStart(9)}${"uyari".padStart(7)}` +
  `${"yanlis uyari".padStart(14)}${"yakalanan olay".padStart(16)}${"kacirilan".padStart(11)}` +
  `${"ort. gecikme".padStart(14)}${"kacan butce dk".padStart(16)}`);
for (const e of ESIK)
  for (const p of PENCERE) {
    const r = tara(e, p);
    const kacan = OLAY.filter((_, i) => r.yakalanan.has(i) === false)
      .reduce((a, [, sure, siddet]) => a + (sure * siddet) / 1000, 0);
    const ort = r.gecikme.length ? r.gecikme.reduce((a, c) => a + c, 0) / r.gecikme.length : 0;
    console.log(`${("%" + (e * 100).toFixed(1)).padStart(7)}${(p + " dk").padStart(9)}` +
      `${String(r.uyari).padStart(7)}${String(r.yanlis).padStart(14)}` +
      `${`${r.yakalanan.size}/${OLAY.length}`.padStart(16)}` +
      `${String(OLAY.length - r.yakalanan.size).padStart(11)}` +
      `${(ort.toFixed(1) + " dk").padStart(14)}${kacan.toFixed(2).padStart(16)}`);
  }
```

```
tohum 20260731, 43200 dakika, 6 gercek olay, toplam 342 dakika bozulma
olaylarin butceden yedigi: 26.06 dakika-esdegeri (K01'in ariza payi 28,2 dakika)
gurultu: en yuksek dakika %3.34, %0,5 esigini asan 149 dakika, %2 esigini asan 69 dakika

   esik  pencere  uyari  yanlis uyari  yakalanan olay  kacirilan  ort. gecikme  kacan butce dk
   %0.5     1 dk    155           149             6/6          0        0.0 dk            0.00
   %0.5     3 dk      6             0             6/6          0        2.0 dk            0.00
   %0.5     5 dk      4             0             4/6          2        4.0 dk            3.29
   %1.0     1 dk    131           125             6/6          0        2.0 dk            0.00
   %1.0     3 dk      5             0             5/6          1        2.0 dk            1.92
   %1.0     5 dk      3             0             3/6          3        4.0 dk            5.21
   %2.0     1 dk     74            69             5/6          1        2.4 dk            0.45
   %2.0     3 dk      4             0             4/6          2        2.0 dk            2.37
   %2.0     5 dk      2             0             2/6          4        4.0 dk            5.66
   %5.0     1 dk      3             0             3/6          3        0.0 dk            2.46
   %5.0     3 dk      3             0             3/6          3        2.0 dk            2.46
   %5.0     5 dk      2             0             2/6          4        4.0 dk            5.66
```

#### İki Hatayı Aynı Anda Küçültmenin Yolu

Tablo iki eksende okunur ve ikisi aynı şey değildir.

**Eşiği yükseltmek yanlış uyarıyı da gerçek olayı da keser.** Yüzde 0,5 eşiği ve tek dakikalık
pencerede ayda 155 uyarı tetikleniyor, 149'u yanlış — günde beş kez, hiçbir şey olmadan. Eşik yüzde
5'e çıkarıldığında yanlış uyarı sıfırlanıyor, ama yakalanan olay 6'dan 3'e düşüyor ve kaçan olaylar
bütçeden 2,46 dakika yiyor. Yanlış uyarıyı eşikle kesmenin bedeli budur: gürültünün üstüne çıkan
eşik, hafif olayların da üstündedir.

**Pencereyi uzatmak yalnız kısa süreli gürültüyü keser.** Aynı yüzde 0,5 eşiğinde pencere üç
dakikaya çıkarıldığında yanlış uyarı 149'dan 0'a iniyor, altı olayın altısı da yakalanmayı
sürdürüyor ve kaçan bütçe 0,00 dakika kalıyor. Bedeli yalnızca iki dakikalık algılama gecikmesidir.
Bunun nedeni modelde yazılıdır: gürültü tek dakikalıktır, gerçek olaylar ise en az üç dakika sürer.
**Pencere, gürültünün süresinden uzun ve en kısa gerçek olaydan kısa seçilir.**

Sınırı beş dakikalık pencere gösteriyor: aynı eşikte yanlış uyarı yine 0, ama üç ve dört dakikalık
iki olay artık görünmüyor ve bütçeden 3,29 dakika kaçıyor. Pencere en kısa olayı geçtiği anda
kaçırma başlar.

Bir satır da yanıltıcı bir başarıyı gösteriyor. Yüzde 1,0 eşiği ve tek dakikalık pencerede altı
olayın altısı yakalanmış görünüyor, oysa birinci olayın şiddeti yüzde 0,8'dir ve eşiğin altında
kalır. Yakalanmasının nedeni, o olayın 240 dakikası içine bir gürültü zirvesinin denk gelmesidir.
Bu bir tanı değil rastlantıdır ve tarama tablosunda ancak nedeni bilinerek okunur.

#### Eşik Nereden Gelir

Tarama en iyi çifti bulur ama eşiğin **hangi sayı** olması gerektiğini söylemez; yüzde 0,5 de yüzde
2 de savunulabilir görünür. Sayının kaynağı ölçütün kendisinde değil, hizmetin taahhüdündedir.
Sistem Tasarımına Giriş kursu aylık bütçeyi kurdu: yüzde 99,9 hedefi 43,2 dakika, bunun 15,0
dakikası planlı işlere, 28,2 dakikası arızaya. Yüzde aritmetiği burada tekrarlanmaz; bu üç sayı
girdidir. Arıza payı bir aya bölündüğünde **sürekli olarak izin verilen bir hata oranı** çıkar ve
gözlenen oranın bu orana bölümü **tüketim hızıdır**.

```js
// uyari/butce.mjs — uyari esiginin K01'in kesinti butcesinden turetilmesi: tuketim hizi,
// esigin hata orani karsiligi ve uyari tetiklendiginde harcanmis butce. Hepsi aritmetiktir.
const AY_DK = 43_200;         // K01: 30 gunluk ay (dakika)
const AYLIK = 43.2;           // K01: %99,9 icin aylik kesinti butcesi (dakika)
const ARIZA_PAYI = 28.2;      // K01: butcenin ariza payi (dakika)
const izinli = ARIZA_PAYI / AY_DK;                 // istek tabanli izin verilen hata orani
const b = (x, n = 2) => x.toFixed(n);

console.log(`aylik butce ${AYLIK} dk, ariza payi ${ARIZA_PAYI} dk, ay ${AY_DK} dk`);
console.log(`izin verilen surekli hata orani = %${b(izinli * 100, 4)} (tuketim hizi 1,0)`);

const PENCERE = [["5 dk", 5], ["1 sa", 60], ["6 sa", 360]];
console.log(`\n${"tuketim hizi".padStart(13)}${"hata orani esigi".padStart(18)}` +
  `${"ariza payi biter".padStart(18)}` +
  `${PENCERE.map(([a]) => `${a} penceresi`.padStart(16)).join("")}`);
for (const hiz of [1, 3, 6, 14.4]) {
  const oran = izinli * hiz;
  const satir = PENCERE.map(([, dk]) =>
    `%${b((100 * dk * oran) / ARIZA_PAYI)}`.padStart(16)).join("");
  console.log(`${b(hiz, 1).padStart(13)}${("%" + b(oran * 100, 3)).padStart(18)}` +
    `${(b(30 / hiz, 2) + " gun").padStart(18)}${satir}`);
}
console.log(`pencere sutunu: uyari tetiklendiginde ariza payinin ne kadari harcanmistir`);

const TARAMA = [0.005, 0.01, 0.02, 0.05];
console.log(`\nonceki taramanin esikleri butce diliyle:`);
for (const e of TARAMA)
  console.log(`  %${b(e * 100, 1)} esigi -> tuketim hizi ${b(e / izinli, 1)}, ` +
    `bu hizda ariza payi ${b(ARIZA_PAYI / (e * 1440), 2)} gunde biter`);

// IZ6 (varsayim): onceki blogun alti olayi [sure (dk), siddet (binde)]
const OLAY = [[240, 8], [30, 15], [3, 30], [20, 120], [4, 800], [45, 400]];
console.log(`\n${"olay".padStart(5)}${"sure".padStart(8)}${"siddet".padStart(9)}` +
  `${"tuketim hizi".padStart(14)}${"yedigi dk".padStart(12)}${"ariza payinin".padStart(15)}` +
  `${"bu hizda payin bitisi".padStart(23)}`);
let toplam = 0;
OLAY.forEach(([sure, siddet], i) => {
  const oran = siddet / 1000, yedi = sure * oran;
  toplam += yedi;
  console.log(`${String(i + 1).padStart(5)}${(sure + " dk").padStart(8)}` +
    `${("%" + b(oran * 100, 1)).padStart(9)}${b(oran / izinli, 1).padStart(14)}` +
    `${b(yedi).padStart(12)}${("%" + b((100 * yedi) / ARIZA_PAYI, 1)).padStart(15)}` +
    `${(b(ARIZA_PAYI / (oran * 1440), 2) + " gun").padStart(23)}`);
});
console.log(`toplam ${b(toplam)} dk, ariza payinin %${b((100 * toplam) / ARIZA_PAYI, 1)}'i; ` +
  `kalan pay ${b(ARIZA_PAYI - toplam)} dk`);
```

```
aylik butce 43.2 dk, ariza payi 28.2 dk, ay 43200 dk
izin verilen surekli hata orani = %0.0653 (tuketim hizi 1,0)

 tuketim hizi  hata orani esigi  ariza payi biter  5 dk penceresi  1 sa penceresi  6 sa penceresi
          1.0            %0.065         30.00 gun           %0.01           %0.14           %0.83
          3.0            %0.196         10.00 gun           %0.03           %0.42           %2.50
          6.0            %0.392          5.00 gun           %0.07           %0.83           %5.00
         14.4            %0.940          2.08 gun           %0.17           %2.00          %12.00
pencere sutunu: uyari tetiklendiginde ariza payinin ne kadari harcanmistir

onceki taramanin esikleri butce diliyle:
  %0.5 esigi -> tuketim hizi 7.7, bu hizda ariza payi 3.92 gunde biter
  %1.0 esigi -> tuketim hizi 15.3, bu hizda ariza payi 1.96 gunde biter
  %2.0 esigi -> tuketim hizi 30.6, bu hizda ariza payi 0.98 gunde biter
  %5.0 esigi -> tuketim hizi 76.6, bu hizda ariza payi 0.39 gunde biter

 olay    sure   siddet  tuketim hizi   yedigi dk  ariza payinin  bu hizda payin bitisi
    1  240 dk     %0.8          12.3        1.92           %6.8               2.45 gun
    2   30 dk     %1.5          23.0        0.45           %1.6               1.31 gun
    3    3 dk     %3.0          46.0        0.09           %0.3               0.65 gun
    4   20 dk    %12.0         183.8        2.40           %8.5               0.16 gun
    5    4 dk    %80.0        1225.5        3.20          %11.3               0.02 gun
    6   45 dk    %40.0         612.8       18.00          %63.8               0.05 gun
toplam 26.06 dk, ariza payinin %92.4'i; kalan pay 2.14 dk
```

Birinci tablo eşiği bir zaman ölçüsüne çeviriyor. Tüketim hızı 1,0 bütçeyi tam ay sonunda bitirir;
14,4 iki gün içinde bitirir. Eşik seçmek, "hangi hızda uyandırılmak istiyorum" sorusuna yanıt
vermektir ve pencere sütunu bedelini gösterir: 14,4 hızında altı saatlik bir pencereyle beklemek,
uyarı geldiğinde arıza payının yüzde 12'sinin çoktan harcanmış olması demektir. Kısa pencere erken
uyandırır ve gürültüye açıktır; uzun pencere sessizdir ve pahalıdır. **Bu, taramada ölçülen
ödünleşimin bütçe cinsinden yazılmış hâlidir.**

İkinci blok taramanın dört eşiğini aynı dile çeviriyor ve keyfiliği ortadan kaldırıyor: yüzde 0,5
eşiği 7,7 tüketim hızı demektir, yani o oran sürerse arıza payı 3,92 günde biter. Yüzde 5 eşiği
76,6 hız ve 0,39 gündür — yani bu eşiğe kadar beklemek, ayın bütçesinin dokuz saatte tükenmesine
izin vermektir. Eşik böyle yazıldığında tartışma bir tercih tartışması olmaktan çıkar.

Üçüncü tablo kaçırmanın bedelini kalem kalem veriyor. Altı olay arıza payının yüzde 92,4'ünü yedi
ve geriye 2,14 dakika kaldı. En pahalı olay en şiddetli olan değil, şiddeti ile süresinin çarpımı
en büyük olandır: kırk beş dakika süren yüzde 40'lık olay tek başına payın yüzde 63,8'ini aldı.
Buna karşılık yüzde 0,8 şiddetindeki olay — taramada yüzde 1 ve üstü eşiklerin kaçırdığı olay —
payın yüzde 6,8'ini yedi. **Bir olayın uyarı üretmemesi onun ucuz olduğu anlamına gelmez;** hafif
ve uzun bir bozulma, kısa ve sert bir olaydan daha fazla bütçe yiyebilir. Bu yüzden bütçe tabanlı
uyarı düzeni tek bir eşik değil, biri kısa pencereli yüksek hız, öteki uzun pencereli düşük hız
olmak üzere en az iki eşik ister.

#### Özet

- Gösterge tablosu bakmakta olanın "olağan mı" sorusunu, uyarı bakmayanın "şimdi bir şey yapmalı
  mıyım" sorusunu yanıtlar; panelin beklenen aralığı ile uyarı eşiği aynı sayı değildir.
- Her uyarı bir eyleme bağlıdır; karşılığında yapılacak iş yoksa ölçüt gösterge tablosuna girer.
- Eşiği yükseltmek iki hatayı birden değiştirir: yüzde 0,5 ve 1 dakikada ayda 155 uyarının 149'u
  yanlış; yüzde 5'te yanlış uyarı 0'a iner ama üç olay kaçar ve bütçeden 2,46 dakika gider.
- Pencereyi uzatmak yalnız kısa gürültüyü keser: yüzde 0,5 ve 3 dakikada yanlış uyarı 149'dan 0'a
  iner, altı olayın altısı yakalanır, bedeli 2,0 dakikalık algılama gecikmesidir. Pencere en kısa
  gerçek olayı geçerse (5 dakika) iki olay kaçar ve 3,29 dakika bütçe gider.
- Eşik bütçeden türetilir: 28,2 dakikalık arıza payı sürekli yüzde 0,0653 hata oranına denktir ve
  gözlenen oranın buna bölümü tüketim hızıdır; yüzde 0,5 eşiği 7,7 hız, yani 3,92 günde biten pay
  demektir.
- Kaçırılan olayın bedeli süre ile şiddetin çarpımıdır: 45 dakikalık yüzde 40'lık olay payın yüzde
  63,8'ini, 240 dakikalık yüzde 0,8'lik olay yüzde 6,8'ini yer.

#### Sonraki Adım

Buraya kadar ölçülen her şey, sistemin gerçek trafiği altında kendiliğinden oluşan sayılardı. Bu
sayılar yalnız **olmuş olanı** anlatır: bugünkü yükte ne olduğunu söyler, yarınki yükte ne olacağını
söylemez. Uyarı eşiği bugünün trafiğine göre ayarlanmış bir eşiktir; trafik iki katına çıktığında
hangi ölçütün önce eşiği aşacağı, sistemin hangi noktada doyuma varacağı ve ani bir sıçramadan
sonra kendine gelip gelemeyeceği toplanan veriden okunamaz, çünkü o durum hiç yaşanmamıştır. Sonraki
ders bu boşluğu doldurur: yükü beklemek yerine üretir. Üç ayrı test üç ayrı soru sorar ve her biri
başka bir belirtiyi ortaya çıkarır.

### Yük Testi

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/yuk-testi

Yükün beklenmek yerine üretilmesi: yük, dayanıklılık ve ani yük testlerinin üç ayrı soru sorduğunun ve üç ayrı belirtiyi ortaya çıkardığının ölçülmesi, açık döngülü bir yük üretecinin yazılması, aynı yükte uzun koşumun ortancada görünmeyen bozulmayı kuyruk gecikmesinde göstermesi ve bir sıçramanın bedelinin sıçramanın kendisinden büyük olduğunun sayılması.

Önceki üç ders sistemin kendiliğinden ürettiği sayıları topladı, biçimlendirdi ve eşiğe bağladı.
Toplanan her sayı **olmuş olanı** anlatır. Bugünkü yükte ne olduğunu söyler; yarınki yükte ne
olacağını, trafik üç katına çıktığında hangi ölçütün önce eşiği aşacağını ve sistem saatlerce aynı
yükte çalıştığında bugünkü sayının aynı kalıp kalmayacağını söylemez. Bu üç soru hiç yaşanmadıkları
için ölçülmemiştir.

Bu ders yükü beklemek yerine üretir. Üç ayrı test kurulur ve dersin savı şudur: **üçü aynı testin
uzun ve kısa hâlleri değildir, üç ayrı soru sorar ve üç ayrı belirtiyi ortaya çıkarır.**

#### Üç Soru

**Yük testi**, hedeflenen yükte eşiklerin karşılanıp karşılanmadığını sorar. Süresi kısadır, yükü
sabittir ve yanıtı iki değerlidir: karşılandı ya da karşılanmadı.

**Dayanıklılık testi** aynı yükü uzun süre uygular ve zamanla bozulma olup olmadığını sorar. Yükü
sabit tutması özellikle önemlidir; değişen tek şey süredir, böylece bulunan her bozulma yükün
değil, sistemde **biriken** bir şeyin sonucudur — tutulup bırakılmayan bellek, büyüyen bir dizi,
temizlenmeyen bir defter.

**Ani yük testi** yükü kısa süreliğine katlar ve iki şey sorar: sıçrama sırasında ne oluyor ve
sıçrama bittikten sonra sistem ne kadar sürede kendine geliyor. İkinci soru birincisinden önemlidir,
çünkü bir sıçrama geçicidir ama toparlanma süresi boyunca sıçramayla hiç ilgisi olmayan istekler de
bedel öder.

Aşağıdaki düzenek yük üretecini kendi yazar. Üreteç **açık döngülüdür**: istekleri hedef hızda
gönderir ve sunucunun yetişmesini beklemez. Kapalı döngülü bir üreteç, yani her yanıtı bekleyip
sonra göndereni, sunucu yavaşladığında yükü kendiliğinden azaltır ve sıçramayı hiç üretemez.
Sunucu tarafında takip ucu, her yüzüncü isteğin biriken kayıtları taradığı bir **bakım geçişi**
içerir; bu geçiş dayanıklılık testinin arayacağı birikmenin kaynağıdır. Ölçülen süreler bu makineye
bağlıdır; dersin savları oranlara ve sayaçlara dayanır.

```js
// yuk/sunucu.mjs — takip ucu: her istek bir bekleme ve bir islemci payi harcar, her yuzuncu
// istek birikmis kayitlari tarayan bir bakim gecisi yapar. /olcut ucu sunucunun sayaclarini verir.
import { createServer } from "node:http";

const BEKLEME = 3;        // duzenek parametresi: kayit aramasinin yerine gecen bekleme (ms)
const ISLEMCI = 0.85;     // duzenek parametresi: istek basina islemci payi (ms)
const BAKIM_ARALIK = 100; // duzenek parametresi: kac istekte bir bakim gecisi
const BAKIM_KAT = 600;    // duzenek parametresi: bakim gecisinin birikmis kaydi tarama sayisi

const kayitlar = [];
let istek = 0, bakim = 0, bakimAdim = 0;

function isle(ms) {
  const bit = performance.now() + ms;
  let t = 0;
  while (performance.now() < bit) t += 1;
  return t;
}

const port = Number(process.argv[2]);
if (Number.isInteger(port) === false) console.log("kullanim: node yuk/sunucu.mjs <baglanti-noktasi>");
else createServer(async (gelen, yanit) => {
  const yol = new URL(gelen.url, "http://yerel");
  if (yol.pathname === "/olcut") {
    yanit.writeHead(200, { "content-type": "application/json" });
    yanit.end(JSON.stringify({ istek, kayit: kayitlar.length, bakim, bakimAdim, kat: BAKIM_KAT }));
    return;
  }
  istek += 1;
  const sira = istek;                        // sirasi girerken saptanir, cikarken degil
  kayitlar.push({ no: yol.searchParams.get("no") ?? "-", adim: sira % 7 });
  await new Promise((c) => setTimeout(c, BEKLEME));
  isle(ISLEMCI);
  if (sira % BAKIM_ARALIK === 0) {           // bakim gecisi: isi birikmis kayitla buyur
    bakim += 1;
    let t = 0;
    for (let k = 0; k < BAKIM_KAT; k += 1) for (const kayit of kayitlar) t += kayit.adim;
    bakimAdim += BAKIM_KAT * kayitlar.length;
  }
  yanit.writeHead(200, { "content-type": "application/json" });
  yanit.end(JSON.stringify({ durum: "aktarma merkezinde", adim: sira % 7 }));
}).listen(port);
```

```js
// yuk/uretec.mjs — acik dongulu yuk ureteci: istekleri hedef hizda gonderir, sunucunun
// yetismesini beklemez. Uc test ayni uretecle, her biri yeni baslatilmis bir sunucuyla kosar.
import { Agent, request } from "node:http";

const port = Number(process.argv[2]);
const test = process.argv[3];
const TABAN = 514;        // hedef hiz (istek/s) — K01: tepe ucta 513,89 istek/s
const SICRAMA = 3;        // K01: tepe carpani 3
const ESIK = 25;          // YT1 (varsayim): takip yanitinin basarim esigi (ms)
const agent = new Agent({ keepAlive: true, maxSockets: 8192 });

const yuz = (d, p) => {
  const s = [...d].sort((a, b) => a - b);
  return s.length === 0 ? 0 : s[Math.min(s.length - 1, Math.floor((p / 100) * s.length))];
};
const b2 = (x) => x.toFixed(2);
const say = (x, n) => String(x).padStart(n);
const dilim = (k, a, b) => k.filter((r) => r.sn >= a && r.sn < b).map((r) => r.ms);

function birIstek(no, kayit, sn) {
  const bas = performance.now();
  return new Promise((coz) => {
    const r = request({ port, path: `/takip?no=${no}`, agent }, (y) => {
      y.resume();
      y.on("end", () => { kayit.push({ ms: performance.now() - bas, sn }); coz(); });
    });
    r.on("error", () => { kayit.push({ ms: performance.now() - bas, sn, hata: 1 }); coz(); });
    r.end();
  });
}

const olcut = () => new Promise((coz) => {
  request({ port, path: "/olcut", agent }, (y) => {
    let g = "";
    y.on("data", (p) => { g += p; });
    y.on("end", () => coz(JSON.parse(g)));
  }).end();
});

async function kosum(sure, hiz) {           // hiz(t): saniye cinsinden zamana gore hedef hiz
  const kayit = [], ucus = [];
  let borc = 0, i = 0, sonT = 0;
  const bas = performance.now();
  while (performance.now() - bas < sure * 1000) {
    const t = (performance.now() - bas) / 1000;
    borc += hiz(t) * (t - sonT);            // gecen gercek sureye gore: zamanlayici sapmasini yutar
    sonT = t;
    while (borc >= 1) { borc -= 1; ucus.push(birIstek(`G${i++}`, kayit, Math.floor(t))); }
    await new Promise((c) => setTimeout(c, 5));
  }
  await Promise.all(ucus);
  return { kayit, gecen: (performance.now() - bas) / 1000 };
}

if (Number.isInteger(port) === false || ["yuk", "dayaniklilik", "ani"].includes(test) === false) {
  console.log("kullanim: node yuk/uretec.mjs <baglanti-noktasi> yuk|dayaniklilik|ani");
} else if (test === "yuk") {
  await birIstek("isinma", [], 0);
  const r = await kosum(3, () => TABAN);
  const m = r.kayit.map((k) => k.ms);
  const asan = m.filter((x) => x > ESIK).length;
  console.log(`YUK TESTI: hedef ${TABAN} istek/s, 3 s, esik ${ESIK} ms`);
  console.log(`  tamamlanan ${m.length}, ulasilan hiz ${b2(m.length / r.gecen)} istek/s ` +
    `(hedefin %${b2((100 * m.length) / (TABAN * r.gecen))}'i)`);
  console.log(`  ortanca ${b2(yuz(m, 50))} ms, p95 ${b2(yuz(m, 95))} ms, p99 ${b2(yuz(m, 99))} ms`);
  console.log(`  esigi asan ${asan} istek (%${b2((100 * asan) / m.length)})`);
} else if (test === "dayaniklilik") {
  await birIstek("isinma", [], 0);
  const o1 = await olcut();
  const r = await kosum(12, () => TABAN);
  const o2 = await olcut();
  console.log(`DAYANIKLILIK TESTI: ayni ${TABAN} istek/s, 12 s`);
  console.log(`  ceyrek   istek   ortanca(ms)   p95(ms)   p99(ms)   esigi asan`);
  const c = [];
  for (let i = 0; i < 4; i += 1) {
    const d = dilim(r.kayit, i * 3, (i + 1) * 3);
    c.push(d);
    console.log(`  ${say(i + 1, 6)}${say(d.length, 8)}${say(b2(yuz(d, 50)), 14)}` +
      `${say(b2(yuz(d, 95)), 10)}${say(b2(yuz(d, 99)), 10)}${say(d.filter((x) => x > ESIK).length, 13)}`);
  }
  console.log(`  son/ilk ceyrek: ortanca ${b2(yuz(c[3], 50) / yuz(c[0], 50))} kat, ` +
    `p95 ${b2(yuz(c[3], 95) / yuz(c[0], 95))} kat, p99 ${b2(yuz(c[3], 99) / yuz(c[0], 99))} kat`);
  const gecis = o2.bakim - o1.bakim;
  console.log(`  sunucu sayaci: biriken kayit ${o1.kayit} -> ${o2.kayit}, bakim gecisi ${gecis}`);
  console.log(`  bakim gecisi basina adim: ilk ${(o1.kat * 100).toLocaleString("tr-TR")}, ` +
    `son ${(o2.kat * 100 * gecis).toLocaleString("tr-TR")} (${gecis} kat)`);
} else {
  await birIstek("isinma", [], 0);
  const r = await kosum(9, (t) => (t >= 3 && t < 4.5 ? TABAN * SICRAMA : TABAN));
  console.log(`ANI YUK TESTI: taban ${TABAN} istek/s, 3,0-4,5 s arasi ${TABAN * SICRAMA} istek/s, 9 s`);
  console.log(`  saniye   tamamlanan   ortanca(ms)   p95(ms)   esigi asan`);
  const taban = [];
  for (let sn = 0; sn < 9; sn += 1) {
    const d = dilim(r.kayit, sn, sn + 1);
    if (sn < 3) taban.push(yuz(d, 95));
    console.log(`  ${say(sn, 6)}${say(d.length, 13)}${say(b2(yuz(d, 50)), 14)}` +
      `${say(b2(yuz(d, 95)), 10)}${say(d.filter((x) => x > ESIK).length, 13)}`);
  }
  let donus = null;
  for (let sn = 5; sn < 9; sn += 1)
    if (donus === null && yuz(dilim(r.kayit, sn, sn + 1), 95) <= ESIK) donus = sn - 4.5;
  const fazla = Math.round(TABAN * (SICRAMA - 1) * 1.5);
  const asan = r.kayit.filter((k) => k.ms > ESIK).length;
  console.log(`  taban p95 ${b2(yuz(taban, 100))} ms; sicramada gonderilen fazla istek ${fazla}, ` +
    `hata ile biten ${r.kayit.filter((k) => k.hata).length}`);
  console.log(`  p95 esigin altina donusu: sicrama bittikten ${donus === null ? "sonra donmedi" : b2(donus) + " s sonra"}`);
  console.log(`  esigi asan toplam istek ${asan} = fazla istegin ${b2(asan / fazla)} kati`);
}
agent.destroy();
```

```bash
# olc.sh — uc test, her biri yeni baslatilmis bir sunucuyla kosar
for T in yuk dayaniklilik ani; do
  node yuk/sunucu.mjs 8851 & SUNUCU=$!
  sleep 1
  node yuk/uretec.mjs 8851 $T
  kill $SUNUCU
  wait $SUNUCU 2>/dev/null
  echo
done
```

```
YUK TESTI: hedef 514 istek/s, 3 s, esik 25 ms
  tamamlanan 1539, ulasilan hiz 512.72 istek/s (hedefin %99.75'i)
  ortanca 7.24 ms, p95 9.92 ms, p99 13.05 ms
  esigi asan 0 istek (%0.00)

DAYANIKLILIK TESTI: ayni 514 istek/s, 12 s
  ceyrek   istek   ortanca(ms)   p95(ms)   p99(ms)   esigi asan
       1    1538          7.06      9.04      9.65            0
       2    1544          6.97      9.00     10.12            0
       3    1542          7.30     15.95     22.37            4
       4    1541          7.33     20.04     25.99           22
  son/ilk ceyrek: ortanca 1.04 kat, p95 2.22 kat, p99 2.69 kat
  sunucu sayaci: biriken kayit 1 -> 6166, bakim gecisi 61
  bakim gecisi basina adim: ilk 60.000, son 3.660.000 (61 kat)

ANI YUK TESTI: taban 514 istek/s, 3,0-4,5 s arasi 1542 istek/s, 9 s
  saniye   tamamlanan   ortanca(ms)   p95(ms)   esigi asan
       0          512          7.22     10.01            0
       1          514          7.35     12.71            0
       2          514          7.21     11.43            0
       3         1545        101.74   1508.92         1400
       4         1024        111.74   1028.35          976
       5          516         14.07    108.66          224
       6          514          7.31     16.52            4
       7          512          7.61     20.21           12
       8          516          7.32     22.93           15
  taban p95 12.71 ms; sicramada gonderilen fazla istek 1542, hata ile biten 0
  p95 esigin altina donusu: sicrama bittikten 1.50 s sonra
  esigi asan toplam istek 2631 = fazla istegin 1.71 kati
```

Çıktının **süre sütunları** — ortanca, p95, p99 ve bunlardan çıkan katlar — bu makineye ve o andaki
yüküne bağlıdır; başka bir makinede başka, aynı makinede tekrarlandığında yine başka çıkar.
**Sayaç sütunları** ise makinenin hızına bağlı değildir: gönderilen istek sayısı, biriken kayıt,
bakım geçişi sayısı ve bakım geçişi başına adım, hedef hız ile sürenin çarpımından belirlenir.
Aşağıdaki üç bölümün savları bu ikinci kümeye ve süre sayılarının **yönüne** dayanır; tek bir
koşumdan okunan değerler *bu koşumda* diye işaretlenir.

#### Yük Testi Geçti

Birinci test hedefe ulaştı: üreteç istenen hızın yüzde birlik bandında kaldı (bu koşumda 512,72
istek/s, hedefin yüzde 99,75'i). Ulaşılan hızın raporlanması gereksiz görünür ve değildir — üreteç
hedefine ulaşamadıysa ölçülen gecikme başka bir yükün gecikmesidir ve tablo okunamaz. Yüzdelikler
eşiğin belirgin biçimde altında kaldı ve bu koşumda 25 milisaniyelik eşiği aşan istek olmadı. Yanıt
iki değerlidir ve olumludur.

Bu testin tek başına verdiği güvence dardır. Söylediği şey şudur: bu yükte, bu süre boyunca, bu
durumda olan bir sistem eşiği karşılar. Üç koşuldan ikisini kaldıran iki test daha vardır.

#### Dayanıklılık Testi: Ortanca Yalan Söyler

İkinci test aynı yükü dört katı süre uyguladı ve tablo ortada ikiye ayrılıyor. **Ortanca dört
çeyrekte de yerinde duruyor** (bu koşumda son çeyrek ilkin 1,04 katı). Bu sayıya bakan biri sistemin
değişmediğini söyler. Oysa aynı çeyreklerde p95 ile p99 iki katına yakın büyümüş (bu koşumda 2,22 ve
2,69 kat) ve eşiği aşan istek ilk çeyrekte sıfırken son çeyrekte onlarca isteğe çıkmış (bu koşumda
22). **Aynı yükte, aynı kodda, yalnız süre uzadığı için bir eşik ihlali doğdu.**

Katların tam değeri koşumdan koşuma oynar, yönü oynamaz; iddiayı taşıyan sayı da onlar değil,
sunucunun kendi sayaçlarıdır. Bozulmanın nedeni tahmin değil, orada yazılı: biriken kayıt 6.166'ya çıktı,
altmış bir bakım geçişi yapıldı ve bir bakım geçişinin adım sayısı 60.000'den 3.660.000'e, yani tam
**61 katına** çıktı. Bu oran koşumdan bağımsızdır — makine ne kadar hızlı olursa olsun, altmış
birinci geçiş birincinin altmış bir katı iş yapar, çünkü taranan dizi her yüz istekte yüz kayıt
büyüyor. Ortancanın kıpırdamaması da aynı sayıdan çıkıyor: bakım geçişi yüz istekten yalnız birini
yavaşlatır, yani isteklerin yüzde birini. Ortanca yüzde ellinci istektir ve orada hiçbir şey olmaz.
**Birikme belirtisi ortancada değil, kuyruk gecikmesinde görünür.**

#### Ani Yük Testi: Bedel Sıçramadan Büyüktür

Üçüncü test yükü bir buçuk saniye boyunca üç katına çıkardı — Sistem Tasarımına Giriş kursunun tepe
çarpanı da üçtü. Sıçrama saniyesinde p95 **iki mertebe** büyüdü (bu koşumda 12,71 milisaniyeden
1.508,92 milisaniyeye). Sıçrama bittikten sonra p95'in eşiğin altına dönmesi bir buçuk saniye sürdü;
ölçü saniye çözünürlüğündedir ve bu sürede biriken iş eritildi.

Testin asıl sayısı son satırdadır. Sıçrama penceresinde fazladan 1.542 istek gönderildi — bu sayı
hesaptır, üretecin parametrelerinden çıkar ve koşuma bağlı değildir. Eşiği aşan istek sayısı ise
ondan **büyüktür** (bu koşumda 2.631, yani 1,71 katı). Oranın tam değeri koşumdan koşuma oynar ama
birden büyük olması oynamaz, çünkü nedeni yapısaldır: kuyruğa giren fazla iş arkadan gelen olağan
istekleri de bekletir, dolayısıyla gecikmesi bozulan istek kümesi sıçrayan istek kümesinden geniştir.
Bir sıçramanın maliyeti sıçrayan istek sayısıyla değil, gecikmesi bozulan **toplam** istek sayısıyla
ölçülür.

Bir sütun da boş kalıyor: hata ile biten istek sayısı sıfır. Sistem fazla yükü reddetmedi, kuyruğa
aldı. Dayanıklılık ve Güvenilirlik kursunda kurulan geri basınç ve yük boşaltma kalıpları tam olarak
bu davranışı değiştirir; burada ikisi de yok, bu yüzden kapasite fazlası gecikmeyle ödendi. Ani yük
testinin ortaya çıkardığı belirti budur ve yük testi ile dayanıklılık testi onu göremez.

#### Özet

- Üç test üç ayrı soru sorar: yük testi bugünün eşiğini, dayanıklılık testi zamanla birikmeyi, ani
  yük testi sıçrama sırasındaki bozulmayı ve sonrasındaki toparlanmayı ölçer.
- Yük üreteci açık döngülü olmalıdır: her yanıtı bekleyen bir üreteç sunucu yavaşladığında yükü
  kendiliğinden azaltır ve sıçramayı hiç üretemez. Ulaşılan hız her koşumda raporlanır (bu koşumda
  hedefin yüzde 99,75'i).
- Yük testi eşiği karşıladı: hedef hızın yüzde birlik bandında p99 eşiğin çok altında kaldı ve eşiği
  aşan istek olmadı (bu koşumda 13,05 ms ve 0 istek).
- Dayanıklılık testi aynı yükte bozulma buldu: ortanca yerinde kalırken p95 ile p99 iki katına yakın
  büyüdü ve eşiği aşan istek sıfırdan onlarca isteğe çıktı (bu koşumda 1,04 / 2,22 / 2,69 kat, 22
  istek).
- Bozulmanın kaynağı sunucunun sayaçlarında: bir bakım geçişinin adım sayısı 61 katına çıktı;
  geçiş yüz istekten birini yavaşlattığı için belirti ortancada değil kuyruk gecikmesinde görünür.
- Ani yük testinde 1.542 fazla istek, kendisinden daha çok isteğin eşiği aşmasına yol açtı (bu
  koşumda 2.631, 1,71 kat); oranın birden büyük olması yapısaldır. p95'in eşiğin altına dönmesi bir
  buçuk saniye sürdü ve hiçbir istek reddedilmedi, fazlası gecikmeyle ödendi.

#### Sonraki Adım

Üç test bir sistemin bugünkü tek süreçle nereye kadar gittiğini gösterdi, ama hiçbiri "kaç tane
gerekiyor" sorusunu sormadı. Yük testinin verdiği sayı bir sınır değil bir doğrulamadır: 514 istek/s
karşılanıyor. Sınırın kendisi Sistem Tasarımına Giriş kursunda ölçülmüştü — bir sürecin doyum
verimi — ve orada doluluk arttıkça gecikmenin doğrusal büyümediği de gösterilmişti. Sonraki ders bu
iki girdiyi alır ve tahmine çevirir: ölçülen doluluktan kaç düğüm gerektiğini çıkarır, bir düğüm
kaybında kalan kapasiteyi sayar, dayanıklılık testinin gösterdiği birikmenin düğüm sayısını nasıl
değiştirdiğini ve büyüyen bir talebin bu sayıyı kaç ayda bir artırdığını hesaplar. Aradaki tek
karar bir orandır ve adı vardır: kapasite payı.

### Kapasite Planlama

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/kapasite-planlama

Ölçülen doluluğun kaynak tahminine çevrilmesi: kapasite payının bir doluluk tavanı olarak tanımlanması, düğüm sayısının yük kısıtı ile arıza kısıtının büyüğü olarak hesaplanması, yedeklilik priminin ölçekle küçülmesinin sayılması, birikmenin etkin kapasiteyi düşürmesinin düğüm sayısına etkisi ve başarımı iyileştirmenin düğüm sayısını neden düşürmediğinin gösterilmesi.

Önceki ders üç testle bir sürecin bugünkü yükte ne yaptığını ölçtü ve tek bir soruyu hiç sormadı:
kaç tane gerekiyor. Yük testinin verdiği sayı bir sınır değil, bir doğrulamadır — 514 istek/s
karşılanıyor. Sınırın kendisi bu derste yeniden ölçülmez; Sistem Tasarımına Giriş kursu bir sürecin
**doyum verimini** ölçtü ve doluluk arttıkça gecikmenin doğrusal büyümediğini gösteren kuyruk
modelini kurdu. İkisi burada girdidir.

Bu ders ölçümü tahmine çevirir. Girdi bir hız ve bir üst sınır, çıktı bir sayıdır: kaç düğüm.
Aradaki tek karar bir orandır.

#### Kapasite Payı ve İki Kısıt

**Kapasite payı** (headroom), bir kaynağın bilerek kullanılmadan bırakılan bölümüdür ve bir
**doluluk tavanı** olarak yazılır: yüzde 30 pay, doluluğun yüzde 70'i geçmemesi demektir. Payın
gerekçesi K01'in kuyruk modelindedir — doluluk yükseldikçe gecikme doğrusaldan hızlı büyür, bu
yüzden pay bir israf değil bir gecikme kararıdır.

Düğüm sayısını iki ayrı kısıt belirler ve **büyüğü kazanır**. **Yük kısıtı**: tepe hızın doluluk
tavanının altında karşılanması için gereken düğüm sayısı. **Arıza kısıtı**: bir düğüm düştüğünde
kalanların aynı tavanın altında kalması için gereken sayı — bu, kalan düğümlerin yük kısıtını
karşılaması demektir, yani her zaman bir düğüm fazlası.

Aşağıdaki hesabın girdileri şunlardır: tepe hız 513,89 istek/s (K01, hesap); düğüm başına doyum
verimi 1.176,47 istek/s — önceki dersin sunucusu istek başına 0,85 ms işlemci payı harcadığına göre
tek iş parçacığında bu aritmetikle çıkar, ölçüm değildir; K01'in çıplak uçta ölçtüğü 4.268 istek/s
duyarlılık satırında kullanılır. Aylık talep büyümesi (IZ8) ve birikmenin etkin kapasiteye etkisi
(IZ9) varsayımdır.

```js
// kapasite/dugum.mjs — olculen doyum veriminden dugum sayisina. Kapasite payi bir doluluk
// tavanidir; dugum sayisi yuk kisiti ile ariza kisitinin buyugudur. Hepsi aritmetiktir.
const TEPE = 513.89;        // K01 (hesap): tepe ucta istek/s
const TOPLU = 833.33;       // K01 (hesap): gun sonu isinin tarama hizi (kayit/s)
const KAYIT_MS = 1.20;      // K01 (hesap): toplu iste kayit basina sure, tek isci
const MU = 1 / 0.00085;     // hesap: onceki dersin sunucusu istek basina 0,85 ms islemci harciyor
const MU_CIPLAK = 4268;     // K01 (olcum): ciplak takip ucunun doyum verimi, o makinede
const BOZULMA = 0.7;        // IZ9 (varsayim): birikme yuzunden dugumun etkin kapasitesi %30 duser
const BUYUME = 0.06;        // IZ8 (varsayim): aylik talep buyumesi
const b = (x, n = 2) => x.toFixed(n);

const yukKisiti = (tepe, mu, tavan) => Math.ceil(tepe / (mu * tavan));
const arizaKisiti = (tepe, mu, tavan) => yukKisiti(tepe, mu, tavan) + 1;
const gecikmeMs = (mu, lambda) => 1000 / (mu - lambda);   // K01'in kuyruk MODELI, girdi olarak

console.log(`dugum basina doyum verimi ${b(MU)} istek/s (hesap)`);
console.log(`${"tepe istek/s".padStart(13)}${"doluluk tavani".padStart(16)}${"yuk kisiti".padStart(12)}` +
  `${"ariza kisiti".padStart(14)}${"secilen n".padStart(11)}${"doluluk".padStart(9)}` +
  `${"kayipta doluluk".padStart(17)}${"kayipta gecikme".padStart(17)}`);
for (const tepe of [TEPE, TEPE * 3])
  for (const tavan of [0.5, 0.6, 0.7, 0.8, 0.9]) {
    const n = Math.max(yukKisiti(tepe, MU, tavan), arizaKisiti(tepe, MU, tavan));
    const kayipta = tepe / (n - 1);
    console.log(`${b(tepe).padStart(13)}${("%" + b(tavan * 100, 0)).padStart(16)}` +
      `${String(yukKisiti(tepe, MU, tavan)).padStart(12)}${String(arizaKisiti(tepe, MU, tavan)).padStart(14)}` +
      `${String(n).padStart(11)}${("%" + b((tepe / (n * MU)) * 100, 1)).padStart(9)}` +
      `${("%" + b((kayipta / MU) * 100, 1)).padStart(17)}${(b(gecikmeMs(MU, kayipta)) + " ms").padStart(17)}`);
  }

console.log(`\nyedeklilik primi olcekle kuculur (doluluk tavani %70):`);
console.log(`${"olcek".padStart(6)}${"tepe istek/s".padStart(14)}${"yuk kisiti".padStart(12)}` +
  `${"secilen n".padStart(11)}${"yedeklilik primi".padStart(18)}${"aylik dugum-saat".padStart(18)}`);
for (const k of [1, 2, 3, 4, 6, 10]) {
  const tepe = TEPE * k;
  const y = yukKisiti(tepe, MU, 0.7);
  const n = Math.max(y, arizaKisiti(tepe, MU, 0.7));
  console.log(`${(k + "x").padStart(6)}${b(tepe).padStart(14)}${String(y).padStart(12)}` +
    `${String(n).padStart(11)}${("%" + b((100 * (n - y)) / y, 1)).padStart(18)}` +
    `${(n * 720).toLocaleString("tr-TR").padStart(18)}`);
}

console.log(`\ntalep buyumesi (aylik %${b(BUYUME * 100, 0)}, doluluk tavani %70):`);
console.log(`${"ay".padStart(4)}${"tepe istek/s".padStart(14)}${"secilen n".padStart(11)}` +
  `${"doluluk".padStart(9)}${"bozulmus dugumle n".padStart(20)}`);
for (const ay of [0, 6, 12, 18, 24]) {
  const tepe = TEPE * (1 + BUYUME) ** ay;
  const n = Math.max(yukKisiti(tepe, MU, 0.7), arizaKisiti(tepe, MU, 0.7));
  const nb = Math.max(yukKisiti(tepe, MU * BOZULMA, 0.7), arizaKisiti(tepe, MU * BOZULMA, 0.7));
  console.log(`${String(ay).padStart(4)}${b(tepe).padStart(14)}${String(n).padStart(11)}` +
    `${("%" + b((tepe / (n * MU)) * 100, 1)).padStart(9)}${String(nb).padStart(20)}`);
}

console.log(`\nduyarlilik: dugum basina doyum verimi ${MU_CIPLAK} istek/s olsaydi (K01'in ciplak ucu)`);
console.log(`  bugunun tepesinde n = ${Math.max(yukKisiti(TEPE, MU_CIPLAK, 0.7), arizaKisiti(TEPE, MU_CIPLAK, 0.7))}` +
  `, ${b(MU)} istek/s ile n = ${Math.max(yukKisiti(TEPE, MU, 0.7), arizaKisiti(TEPE, MU, 0.7))}` +
  ` (${b(MU_CIPLAK / MU)} kat basarim, ayni dugum sayisi)`);
const isci = Math.max(yukKisiti(TOPLU, 1000 / KAYIT_MS, 0.7), 1);
console.log(`toplu is ayri bir kapasite sorusu: ${TOPLU} kayit/s gerekiyor, bir isci ` +
  `${b(1000 / KAYIT_MS)} kayit/s -> %70 tavanla ${isci} isci, pencere ` +
  `${b((TOPLU * 4) / (isci * (1000 / KAYIT_MS)), 2)} saate iner`);
```

```
dugum basina doyum verimi 1176.47 istek/s (hesap)
 tepe istek/s  doluluk tavani  yuk kisiti  ariza kisiti  secilen n  doluluk  kayipta doluluk  kayipta gecikme
       513.89             %50           1             2          2    %21.8            %43.7          1.51 ms
       513.89             %60           1             2          2    %21.8            %43.7          1.51 ms
       513.89             %70           1             2          2    %21.8            %43.7          1.51 ms
       513.89             %80           1             2          2    %21.8            %43.7          1.51 ms
       513.89             %90           1             2          2    %21.8            %43.7          1.51 ms
      1541.67             %50           3             4          4    %32.8            %43.7          1.51 ms
      1541.67             %60           3             4          4    %32.8            %43.7          1.51 ms
      1541.67             %70           2             3          3    %43.7            %65.5          2.47 ms
      1541.67             %80           2             3          3    %43.7            %65.5          2.47 ms
      1541.67             %90           2             3          3    %43.7            %65.5          2.47 ms

yedeklilik primi olcekle kuculur (doluluk tavani %70):
 olcek  tepe istek/s  yuk kisiti  secilen n  yedeklilik primi  aylik dugum-saat
    1x        513.89           1          2            %100.0             1.440
    2x       1027.78           2          3             %50.0             2.160
    3x       1541.67           2          3             %50.0             2.160
    4x       2055.56           3          4             %33.3             2.880
    6x       3083.34           4          5             %25.0             3.600
   10x       5138.90           7          8             %14.3             5.760

talep buyumesi (aylik %6, doluluk tavani %70):
  ay  tepe istek/s  secilen n  doluluk  bozulmus dugumle n
   0        513.89          2    %21.8                   2
   6        728.96          2    %31.0                   3
  12       1034.05          3    %29.3                   3
  18       1466.82          3    %41.6                   4
  24       2080.71          4    %44.2                   5

duyarlilik: dugum basina doyum verimi 4268 istek/s olsaydi (K01'in ciplak ucu)
  bugunun tepesinde n = 2, 1176.47 istek/s ile n = 2 (3.63 kat basarim, ayni dugum sayisi)
toplu is ayri bir kapasite sorusu: 833.33 kayit/s gerekiyor, bir isci 833.33 kayit/s -> %70 tavanla 2 isci, pencere 2.00 saate iner
```

#### Bugünün Yükünde Payın Hiç Etkisi Yok

Birinci tablonun ilk beş satırı beklenmedik bir sonuç veriyor. Doluluk tavanı yüzde 50'den yüzde
90'a çıkarılsa da düğüm sayısı 2'de kalıyor, doluluk yüzde 21,8'de, bir düğüm kaybındaki doluluk
yüzde 43,7'de. **Bugünkü tepe hızda kapasite payı seçimi hiçbir şeyi değiştirmiyor**, çünkü yük
kısıtı bütün tavanlarda 1 düğüm veriyor ve belirleyen kısıt arıza kısıtı oluyor.

Bu, kapasite planlamanın en sık atlanan yanıdır. "Kaç düğüm gerekiyor" sorusunun iki bağımsız yanıtı
vardır; biri yükten, öteki arıza davranışından gelir. Tek düğümle bugünkü yük fazlasıyla
karşılanır, ama tek düğüm düştüğünde kapasite sıfırdır ve K01'in kesinti bütçesi o düğümün
erişilebilirliğinin üstüne çıkamaz. Düğüm sayısını iki yapan şey yük değil, bu kısıttır.

Sonraki beş satır payın ne zaman bağlayıcı olduğunu gösteriyor. Tepe hız üç katına çıktığında —
K01'in tepe çarpanı da üçtü — tavan yüzde 70 ve üstünde 3 düğüm, yüzde 50 ile 60'ta 4 düğüm
gerekiyor. Dördüncü düğümün karşılığı son iki sütunda: bir düğüm kaybında doluluk yüzde 65,5 yerine
yüzde 43,7 oluyor ve kuyruk modelinin verdiği gecikme 2,47 ms yerine 1,51 ms. **Kapasite payı bir
düğüm sayısı kararı gibi görünür ve bir arıza günü gecikmesi kararıdır.**

#### Yedeklilik Primi, Büyüme ve Birikme

İkinci tablo arıza kısıtının faturasını ölçekle birlikte veriyor. Bugünkü ölçekte fazladan düğüm,
filoyu iki katına çıkarıyor: yedeklilik primi yüzde 100. On kat ölçekte aynı kural yalnız yüzde
14,3 ekliyor. **Yedeklilik küçük sistemlerde orantısız pahalıdır** ve bu, ölçeğin savunulabilir tek
avantajlarından biridir. Sağ sütun kaynağı ay cinsinden yazıyor: bugün 1.440 düğüm-saat, on katta
5.760 düğüm-saat. Kaynak birimi bu kursta para birimi yerine kullanılır.

Üçüncü tablo kapasitenin bir sayı değil bir **zaman serisi** olduğunu gösteriyor. Aylık yüzde 6
büyümeyle düğüm sayısı on ikinci ayda 3'e, yirmi dördüncü ayda 4'e çıkıyor; doluluk her eklemede
düşüp yeniden tırmanıyor. Son sütun önceki dersin dayanıklılık testinin bulgusunu hesaba katıyor:
düğümün etkin kapasitesi birikme yüzünden yüzde 30 düşerse (IZ9), altıncı ayda değil hemen üçüncü
düğüm gerekir ve yirmi dördüncü ayda beş düğüme çıkılır. **Bir dayanıklılık testinin bulunmayan
bulgusu kapasite planına fazladan bir düğüm olarak yazılır.**

#### Başarım Düğüm Sayısını Düşürmüyor

Duyarlılık satırı dersin en sert sonucudur. Düğüm başına doyum verimi 1.176,47 yerine K01'in çıplak
uçta ölçtüğü 4.268 istek/s olsaydı, yani **3,63 kat daha hızlı bir uç**, gereken düğüm sayısı yine
2 olurdu. Bugünkü ölçekte başarım iyileştirmesinin kaynak karşılığı sıfırdır, çünkü belirleyen kısıt
başarım değil arızadır. Bir başarım çalışmasının kapasiteye çevrilmesi ancak yük kısıtı bağlayıcı
olduğunda anlamlıdır; bu tabloda o durum ancak dördüncü satırdan sonra doğuyor.

Son satır kapasitenin akış başına sorulduğunu hatırlatıyor. Gün sonu ücretlendirmesi saniyede
833,33 kayıt istiyor ve tek işçinin hızı da tam 833,33 kayıt/s — yani pay sıfır. Yüzde 70 tavanıyla
iki işçi gerekir ve karşılığında dört saatlik pencere iki saate iner. Takip sorgusunun düğüm sayısı
bu hesabı hiç etkilemez: iki akış aynı sistemin parçasıdır ve ayrı kapasite soruları sorar.

#### Özet

- Kapasite payı bir doluluk tavanıdır; gerekçesi K01'in kuyruk modelidir ve bir israf değil bir
  gecikme kararıdır.
- Düğüm sayısı yük kısıtı ile arıza kısıtının büyüğüdür; arıza kısıtı her zaman bir düğüm fazlası
  ister, çünkü kalan düğümler de aynı tavanı karşılamalıdır.
- Bugünkü 513,89 istek/s tepede pay seçiminin hiç etkisi yok: her tavanda 2 düğüm, doluluk yüzde
  21,8. Tepe üç katına çıktığında yüzde 50 tavan 4, yüzde 70 tavan 3 düğüm veriyor ve fark bir arıza
  günü gecikmesidir (1,51 ms yerine 2,47 ms).
- Yedeklilik primi ölçekle küçülür: bugünkü ölçekte fazladan düğüm filoyu yüzde 100 büyütür, on kat
  ölçekte yüzde 14,3; kaynak ayda 1.440 düğüm-saatten 5.760 düğüm-saate çıkar.
- Kapasite bir zaman serisidir: aylık yüzde 6 büyümeyle düğüm sayısı 12. ayda 3, 24. ayda 4 olur;
  birikme etkin kapasiteyi yüzde 30 düşürürse aynı takvim 3 ve 5 olur.
- Başarımı 3,63 kat iyileştirmek bugünkü ölçekte düğüm sayısını değiştirmiyor; başarımın kapasiteye
  çevrilmesi ancak yük kısıtı bağlayıcı olduğunda anlamlıdır. Toplu iş ayrı bir sorudur: yüzde 70
  tavanla iki işçi, pencere dört saatten iki saate iner.

#### Sonraki Adım

Bu ders bir sayı üretti: kaç düğüm. Sayının yanına yazılan kaynak birimi de var — bugün ayda 1.440
düğüm-saat, on kat ölçekte 5.760. Ama o düğümlerin **neye mal olduğu** hiç sorulmadı. Düğüm-saat tek
kalem değildir; bir istek ağ üzerinden veri taşır, bir kayıt saklanır, bir ölçüt üretilir ve
saklanır, bir önbellek bellekte durur. Daha önemlisi, buraya kadarki bütün kurslar boyunca verilen
tasarım kararlarının her biri bu kalemlerden birini oynattı ve hiçbirinin işletme yükü sayılmadı:
önbellek isabet oranını yükseltmek neyi azaltır, veriyi parçalamak neyi çoğaltır, bir kopya eklemek
hangi kalemi kaç katına çıkarır, izlemenin kendi bedeli hangi kalemde görünür. Sonraki ders bu
soruyu ele alır ve kaynak birimiyle yanıtlar.

### Maliyet Farkındalığı

Source: https://academia.sh/tr/kurslar/basarim-ve-izleme/maliyet-farkindaligi

Tasarım kararlarının işletme yükünün kaynak birimiyle sayılması: aylık kaynak tüketiminin iş hacmine bölünerek birim maliyete çevrilmesi, hacim büyümesi ile birim maliyet büyümesinin aynı toplamı verdiği hâlde iş birimi başına kaynakla ayrılması, satın alınan kapasitenin ne kadarının iş yaptığının ölçülmesi ve hiçbir düzeltmenin maliyeti kaldırmayıp kalemler arasında taşıdığının gösterilmesi.

Önceki ders bir sayı üretti: kaç düğüm. Yanına bir kaynak birimi de yazdı — bugün ayda 1.440
düğüm-saat, on kat ölçekte 5.760. Ama o düğümlerin neye mal olduğu ve buraya kadarki bütün
kursların tasarım kararlarının işletme yükünü nasıl değiştirdiği hiç sorulmadı. Önbellek isabetini
yükseltmek neyi azaltır, veriyi ayırmak neyi çoğaltır, bir kopya eklemek hangi kalemi büyütür,
izlemenin kendi bedeli nerede görünür.

Bu ders o soruyu ele alır ve kursun tanı kuralını son bir kez uygular: belirti bir sayıdır, aynı
belirtiyi veren iki neden kurulur, ikisini ayıran bir ölçüm gösterilir.

#### Maliyet Kaynak Birimiyle Yazılır

İşletme maliyeti bu kursta **para birimiyle yazılmaz**. Bir fiyat sağlayıcıya, sözleşmeye ve tarihe
bağlıdır; beş yıl sonra okunduğunda yanlış olur. Kaynak birimi bağlı değildir: **işlemci-saniye**,
**GB-ay**, **düğüm-saat**, **aktarılan GB**, istek başına dokunuş. Bir tasarım kararının işletme
yükü bu birimlerle yazıldığında, fiyat değişse de karşılaştırma geçerli kalır.

**Birim maliyet** bu kursta kaynak cinsindendir: bir iş biriminin — bir isteğin ya da bir kaydın —
tükettiği kaynak. Aşağıdaki hesabın bütün girdileri önceki derslerin sayılarıdır ve hiçbiri yeniden
hesaplanmaz; yeni varsayım da eklenmez.

```js
// maliyet/birim.mjs — tasarim kararlarinin isletme yuku, kaynak birimiyle. Butun girdiler
// onceki derslerin sayilaridir ve hicbiri yeniden hesaplanmaz. Para birimi kullanilmaz.
const SORGU_GUN = 12_000_000;   // K01: gunluk takip sorgusu
const OLAY_GUN = 2_800_000;     // K01: gunluk durum olayi
const YANIT_BAYT = 480;         // K01 (V5): takip yaniti govdesi
const OLAY_BAYT = 220;          // K01 (V6): bir durum olayi kaydi
const TOPLU_GB = 10.80;         // K01: gun sonu isinin okudugu
const SAKLANAN_GB = 712.48;     // K01: 730 gunde saklanan veri
const ISLEMCI_S = 0.00085;      // bu kursun yuk testi dersi: istek basina islemci payi (s)
const TEPE = 513.89;            // K01: tepe ucta istek/s
const IZLEME_HAM = 239.76;      // bu kursun enstrumantasyon dersi: ham olay, GB-ay
const IZLEME_HIST = 2.07;       // ayni ders: saniyelik histogram, GB-ay
const AY = 30, SAAT = 720;
const b = (x, n = 2) => x.toFixed(n);

const dugum = (tepe) => Math.ceil(tepe / ((1 / ISLEMCI_S) * 0.7)) + 1;   // 05'in kurali
const istekAy = (SORGU_GUN + OLAY_GUN) * AY;

const kalem = (k = 1) => ({
  "islemci-saniye": istekAy * k * ISLEMCI_S,
  "dugum-saat": dugum(TEPE * k) * SAAT,
  "aktarilan GB": ((SORGU_GUN * YANIT_BAYT + OLAY_GUN * OLAY_BAYT) * AY * k) / 1e9 + TOPLU_GB * AY * k,
  "saklama GB-ay": SAKLANAN_GB * k,
  "izleme GB-ay": IZLEME_HIST,
});

const taban = kalem();
console.log(`aylik is hacmi ${istekAy.toLocaleString("tr-TR")} istek (K01: gunluk 12.000.000 sorgu + 2.800.000 olay)`);
console.log(`${"kalem".padEnd(16)}${"aylik miktar".padStart(15)}${"istek basina".padStart(16)}`);
for (const [ad, deger] of Object.entries(taban))
  console.log(`${ad.padEnd(16)}${b(deger).padStart(15)}${deger / istekAy < 1e-4
    ? (deger / istekAy).toExponential(2).padStart(16) : b(deger / istekAy, 6).padStart(16)}`);

const alinan = dugum(TEPE) * SAAT * 3600;
console.log(`\nsatin alinan dugum-saniye ${alinan.toLocaleString("tr-TR")}, ` +
  `is yapan islemci-saniye ${Math.round(taban["islemci-saniye"]).toLocaleString("tr-TR")}`);
console.log(`kaynak verimi %${b((100 * taban["islemci-saniye"]) / alinan)} — kalani kapasite payi, ` +
  `yedeklilik ve tepe disi saatlerdir`);

console.log(`\nolcek buyudukce hangi kalem kac katina cikiyor:`);
console.log(`${"olcek".padStart(6)}${"dugum".padStart(7)}${Object.keys(taban).map((a) => a.padStart(16)).join("")}`);
for (const k of [1, 2, 4, 10]) {
  const y = kalem(k);
  console.log(`${(k + "x").padStart(6)}${String(dugum(TEPE * k)).padStart(7)}` +
    `${Object.keys(taban).map((a) => `${b(y[a] / taban[a])} kat`.padStart(16)).join("")}`);
}
console.log(`hacimle buyumeyen iki kalem birim maliyeti dusurur: 10 kat hacimde dugum-saat ` +
  `${b(dugum(TEPE * 10) / dugum(TEPE))} kat, izleme 1,00 kat`);

console.log(`\nayni toplam buyumesi, iki ayri neden (ayiran olcum: istek basina islemci-saniye):`);
console.log(`${"ay".padStart(4)}${"A: istek".padStart(16)}${"A: islemci-sn".padStart(15)}` +
  `${"A: istek basina".padStart(17)}${"B: istek".padStart(16)}${"B: islemci-sn".padStart(15)}${"B: istek basina".padStart(17)}`);
for (const ay of [0, 6, 12]) {
  const buyume = 1.06 ** ay;                       // A: hacim buyuyor, birim maliyet sabit
  const aIstek = istekAy * buyume, aIslemci = aIstek * ISLEMCI_S;
  const bIstek = istekAy, bIslemci = bIstek * ISLEMCI_S * buyume;   // B: hacim sabit, birim buyuyor
  console.log(`${String(ay).padStart(4)}${Math.round(aIstek).toLocaleString("tr-TR").padStart(16)}` +
    `${Math.round(aIslemci).toLocaleString("tr-TR").padStart(15)}${(aIslemci / aIstek).toExponential(3).padStart(17)}` +
    `${Math.round(bIstek).toLocaleString("tr-TR").padStart(16)}` +
    `${Math.round(bIslemci).toLocaleString("tr-TR").padStart(15)}${(bIslemci / bIstek).toExponential(3).padStart(17)}`);
}

console.log(`\nkaynak profili kaymasi — hicbir karar maliyeti kaldirmaz, kalemler arasi tasir:`);
const KARAR = [
  ["kenar onbellegi (K01 V9: isabet 0,90)", `depoya ulasan okuma ${b(416.67 / 41.67)} kat azalir`,
    "tutulan bellek ve bayatlik penceresi buyur (nicel karsiligi K04'te)"],
  ["tek parca kaliciligin ayrilmasi", "donem taramasi 801 kat az kayit tarar",
    `depo yazmasi ${b(194.44 / 97.22)} kat, isletilen depo 1 -> 3`],
  ["bir dugum daha (yedeklilik)", "tek dugum arizasi artik kesinti uretmez",
    `dugum-saat ${b(1440 / 720)} kat (720 -> 1.440)`],
  ["ham olay yerine saniyelik histogram", `izleme ${b(IZLEME_HAM / IZLEME_HIST)} kat az GB-ay tutar`,
    "kayit kimligi kaybolur: p99 hatasi %10,0'a cikar"],
];
for (const [ad, azalan, artan] of KARAR) console.log(`  ${ad}\n    azalan: ${azalan}\n    artan : ${artan}`);
```

```
aylik is hacmi 444.000.000 istek (K01: gunluk 12.000.000 sorgu + 2.800.000 olay)
kalem              aylik miktar    istek basina
islemci-saniye        377400.00        0.000850
dugum-saat              1440.00         3.24e-6
aktarilan GB             515.28         1.16e-6
saklama GB-ay            712.48         1.60e-6
izleme GB-ay               2.07         4.66e-9

satin alinan dugum-saniye 5.184.000, is yapan islemci-saniye 377.400
kaynak verimi %7.28 — kalani kapasite payi, yedeklilik ve tepe disi saatlerdir

olcek buyudukce hangi kalem kac katina cikiyor:
 olcek  dugum  islemci-saniye      dugum-saat    aktarilan GB   saklama GB-ay    izleme GB-ay
    1x      2        1.00 kat        1.00 kat        1.00 kat        1.00 kat        1.00 kat
    2x      3        2.00 kat        1.50 kat        2.00 kat        2.00 kat        1.00 kat
    4x      4        4.00 kat        2.00 kat        4.00 kat        4.00 kat        1.00 kat
   10x      8       10.00 kat        4.00 kat       10.00 kat       10.00 kat        1.00 kat
hacimle buyumeyen iki kalem birim maliyeti dusurur: 10 kat hacimde dugum-saat 4.00 kat, izleme 1,00 kat

ayni toplam buyumesi, iki ayri neden (ayiran olcum: istek basina islemci-saniye):
  ay        A: istek  A: islemci-sn  A: istek basina        B: istek  B: islemci-sn  B: istek basina
   0     444.000.000        377.400         8.500e-4     444.000.000        377.400         8.500e-4
   6     629.822.486        535.349         8.500e-4     444.000.000        535.349         1.206e-3
  12     893.415.233        759.403         8.500e-4     444.000.000        759.403         1.710e-3

kaynak profili kaymasi — hicbir karar maliyeti kaldirmaz, kalemler arasi tasir:
  kenar onbellegi (K01 V9: isabet 0,90)
    azalan: depoya ulasan okuma 10.00 kat azalir
    artan : tutulan bellek ve bayatlik penceresi buyur (nicel karsiligi K04'te)
  tek parca kaliciligin ayrilmasi
    azalan: donem taramasi 801 kat az kayit tarar
    artan : depo yazmasi 2.00 kat, isletilen depo 1 -> 3
  bir dugum daha (yedeklilik)
    azalan: tek dugum arizasi artik kesinti uretmez
    artan : dugum-saat 2.00 kat (720 -> 1.440)
  ham olay yerine saniyelik histogram
    azalan: izleme 115.83 kat az GB-ay tutar
    artan : kayit kimligi kaybolur: p99 hatasi %10,0'a cikar
```

#### Satın Alınan Kaynak ve İş Yapan Kaynak

İkinci blok dersin en rahatsız edici sayısını veriyor. Ayda 5.184.000 düğüm-saniye satın alınıyor
ve bunun 377.400'ü iş yapıyor: **kaynak verimi yüzde 7,28.** Kalanı boşa gitmiş değildir, üç ayrı
karar tarafından bilerek satın alınmıştır. Kapasite payı, doluluğun tavanı geçmemesi için tutulur
ve gerekçesi kuyruk modelidir. Yedeklilik, bir düğüm düştüğünde hizmetin sürmesi için tutulur.
Üçüncüsü aritmetiktir: kapasite tepeye göre alınır, iş ise ortalamada yapılır ve K01'in tepe
çarpanı üçtür.

Bu sayı bir israf ölçüsü olarak okunursa yanlış okunur. Doğru okunuşu şudur: **erişilebilirlik
hedefinin ve gecikme hedefinin kaynak cinsinden fiyatı budur.** Yüzde 7,28'i yükseltmenin tek yolu
o iki hedeften birinden vazgeçmektir; hedefler durduğu sürece oran bir sonuçtur, bir kusur değil.
Maliyet farkındalığı, bu oranı düşürmeye çalışmak değil, ne satın alındığını bilmektir.

#### Belirti: Kaynak Hacimden Hızlı Büyüyor

Aylık kaynak tüketiminin büyümesi bir belirtidir ve tek başına hiçbir şey söylemez. İki neden aynı
toplamı verir. **A:** hacim büyümüştür, birim maliyet sabittir. **B:** hacim sabittir, birim maliyet
büyümüştür. Üçüncü tablo ikisini yan yana koyuyor ve toplam sütunları on ikinci ayda birebir aynı:
759.403 işlemci-saniye.

Ayıran ölçüm **iş birimi başına kaynaktır**. A'da istek başına işlemci-saniye on iki ay boyunca
8,500e-4'te sabit kalıyor; B'de 8,500e-4'ten 1,710e-3'e, iki katına çıkıyor. Toplam kaynağa bakan
biri iki durumu ayıramaz ve büyük olasılıkla ikisine de aynı yanıtı verir: düğüm ekler. A'da bu
doğru yanıttır. B'de düğüm eklemek belirtiyi örter ve nedeni yerinde bırakır.

B'nin gerçek bir kaynağı bu kursta ölçüldü. Dayanıklılık testi, aynı yükte çalışan bir sürecin
bakım geçişi başına adımının 61 katına çıktığını gösterdi; birikme, istek başına işi hacimden
bağımsız olarak büyütür. **Birim maliyetin zaman serisi, bir dayanıklılık testinin bulduğu şeyi
üretimde de bulur** — üstelik test koşturmadan.

#### Birim Maliyet Ölçekle Düşer

İkinci tablo kalemlerin ölçekle aynı hızda büyümediğini gösteriyor. İşlemci-saniye, aktarılan GB ve
saklama hacimle doğrusal: on kat iş, on kat kaynak. Düğüm-saat değil — on kat hacimde yalnız 4,00
katına çıkıyor, çünkü önceki dersin arıza kısıtı küçük ölçekte bir düğüm fazlası ister ve o fazlanın
payı ölçekle erir. İzleme kalemi hiç büyümüyor: saniyelik histogram, istek hızından bağımsız olarak
2,07 GB-ay tutuyor.

Sonuç şudur: **birim maliyet ölçekten bağımsız bir sayı değildir.** İstek başına düğüm-saat on kat
ölçekte 2,5 kat ucuzlar, istek başına izleme on kat ucuzlar, istek başına işlemci-saniye hiç
değişmez. Bir işletme yükü tartışmasında "bu özellik istek başına şu kadar tutuyor" cümlesi, hangi
ölçekte ölçüldüğü söylenmeden okunamaz — kursun ilk dersindeki kuralın aynısı, bu kez maliyet
tarafında.

#### Kaynak Profili Kayması

Son blok dört tasarım kararını alıyor ve hiçbirinin maliyeti **kaldırmadığını**, yalnız kalemler
arasında taşıdığını gösteriyor. Kenar önbelleği depoya ulaşan okumayı 10,00 kat azaltır ve
karşılığında bellek tutar. Tek parça kalıcılığı ayırmak dönem taramasını 801 kat küçültür ve depo
yazmasını 2,00 katına çıkarır, işletilen depo sayısını 1'den 3'e taşır. Bir düğüm daha eklemek
tek düğüm arızasının kesinti üretmesini engeller ve düğüm-saati 2,00 katına çıkarır. Ham olay
yerine histogram toplamak izlemeyi 115,83 kat ucuzlatır ve kayıt kimliğini siler.

Dördünün ortak biçimi kursun tanı kuralının maliyet tarafındaki karşılığıdır: bir düzeltme
savunulurken **hangi kalemin küçüldüğü ve hangisinin büyüdüğü** birlikte yazılır. Yalnız küçüleni
yazan bir gerekçe eksik bir gerekçedir, çünkü kaynak profili değişmiştir ve yeni profilin darboğazı
başka bir yerdedir.

#### Özet

- İşletme yükü para birimiyle değil kaynak birimiyle yazılır — işlemci-saniye, GB-ay, düğüm-saat,
  aktarılan GB — çünkü fiyat sağlayıcıya ve tarihe bağlıdır, kaynak birimi değildir.
- Ayda 5.184.000 düğüm-saniye satın alınıyor, 377.400'ü iş yapıyor: kaynak verimi yüzde 7,28. Kalanı
  kapasite payının, yedekliliğin ve tepe çarpanının kaynak cinsinden fiyatıdır, israf değil.
- Aynı kaynak büyümesini iki neden verir ve toplam onları ayıramaz (on ikinci ayda ikisi de 759.403
  işlemci-saniye); ayıran ölçüm iş birimi başına kaynaktır: A'da 8,500e-4 sabit, B'de 1,710e-3.
- Birim maliyet ölçekle değişir: on kat hacimde işlemci-saniye 10,00, düğüm-saat 4,00, izleme 1,00
  katına çıkar; istek başına maliyet hangi ölçekte ölçüldüğü söylenmeden okunamaz.
- Hiçbir düzeltme maliyeti kaldırmaz, kalemler arasında taşır: histogram izlemeyi 115,83 kat
  ucuzlatıp kayıt kimliğini siler, ayırma taramayı 801 kat küçültüp yazmayı 2,00 katına çıkarır.

#### Kurs Kapanışı

Bu kurs on altı derste tek bir işi yaptı: bir belirtiyi bir nedene bağlamak. Aşağıdaki tablo her
dersin belirtisini, ayırt eden ölçümünü ve düzeltmenin karşılığında büyüttüğü şeyi bir arada
veriyor.

| Ders | Belirti | Ayırt eden ölçüm | Düzeltmenin karşılığında büyüyen |
|---|---|---|---|
| Meşgul Veritabanı | Depo doluyken uygulama boşta | Sınır oranı: 1,00 ise iş yukarı, çok üstündeyse aşağı | Uygulamada satır işi 0 → 240.000 kayıt (saniyede 3333,33) |
| Meşgul Ön Yüz | Sunucu ölçütleri sabitken görünen süre büyür | Kayıt başına iş birimi: yükte %0,95, kodda %16,1 | Yanıt 3.771 → 3.775 bayt ve bölünen önbellek anahtarı |
| Gevezelik Eden Girdi/Çıktı | Her çağrı hızlı, yararlı yük küçük, açılış yavaş | Baytın işe yarayan payı: zarf 0,4661 / kullanılmayan yük 0,6177 | Birleştirmede kullanılmayan yük, ayırmada tur sayısı (6 tur) |
| Gereksiz Veri Çekme | İstek sabitken kenar isabeti 0,90'ın altına iner | Çalışma kümesinin çarpanı: bayt 5,43 kat / çeşit 4,00 kat | Daraltma çeşitlendirir: isabet 0,8042 → 0,6348 |
| Hatalı Nesne Yaratımı | Verim eşzamanlılıkla artmıyor | Yaratım oranı: 1,0000 ise azalt, 0,0007 ise artır | Tutulan nesne-tur: havuz 24, havuz 5'in 2,26 katı kaynak tutar |
| Tek Parça Kalıcılık | Yazma başına iş büyüyor | Kendi sınıfına hizmet eden pay: dizin 0,33, tarama 801 kat | Depo yazması 97,22 → 194,44/s, depo sayısı 1 → 3, atomiklik |
| Önbelleksizlik | Uygulama katmanı doyuma yaklaşıyor | Hesap anahtarına göre tekilleştirme: okuma 1,00 / hesap 125,00 | Bellek 960 → 119.365 girdi ve yanlışlık yüzeyi (0,5000) |
| Gürültülü Komşu | Küçük kiracıların beklemesi 0,24 → 0,98 tur | İstek payı 0,6143'te sabitken iş payı 0,1702 → 0,2415 | S1'in gecikmesi 0,47 → 2,96 tur, tutulan 200 sayaç |
| Eşzamanlı Girdi/Çıktı | Verim tavana çarpıyor, işçi eklemek işe yaramıyor | Bağımlılığın kendi doluluğu: 0,9999 / 0,1819 | Uçuştaki istek 8 → 46 (5,75 kat); kabul denetimi gerekir |
| Yeniden Deneme Fırtınası | Bağımlılığa varan çağrı hızı büyüyor | Tekil istek kimliği 4,00'te sabit, başarı oranı 0,0400 | İzni kısmak düşen istek üretir: izin 1'de 180 istek |
| İzleme Kategorileri | On tanının hepsi var olmayan bir ölçümü varsayıyor | Kategori kapsaması: kullanım 10/10, başarım 7/10 | Toplanacak ölçüt listesi on altı kaleme çıkar |
| Enstrümantasyon Tasarımı | İzleme kaydı sistemin kendi yazmasının 5,29 katı | Bedel başına korunan bilgi: %93,0 hataya karşı %10,0 | Toplulaştırma kayıt kimliğini siler, en büyüğü sınırlayamaz |
| Gösterge Tabloları ve Uyarılar | Uyarı ya sürekli çalıyor ya hiç çalmıyor | Eşik–pencere taraması ve tüketim hızı: %0,5 eşiği 7,7 hız | Pencere uzadıkça 2,0 dk gecikme, 5 dk'da 3,29 dk bütçe |
| Yük Testi | Bugünkü ölçüm yarınki yük hakkında susuyor | Üç ayrı test: ortanca yerinde, p95 ve p99 iki katına yakın | Sıçramanın bedeli sıçramadan büyük: 1.542 fazla istek |
| Kapasite Planlama | Kaç düğüm gerektiği ölçümden okunamıyor | Yük kısıtı ile arıza kısıtı: bugün her tavanda n = 2 | Yedeklilik primi %100; 720 → 1.440 düğüm-saat |
| Maliyet Farkındalığı | Aylık kaynak, iş hacminden hızlı büyüyor | İş birimi başına kaynak: 8,500e-4 sabit / 1,710e-3 büyüyen | Her düzeltme kalemi kaydırır: 115,83 kat az GB-ay, silinen kimlik |

Tablonun üçüncü sütunu kursun kuralıdır: **belirti bir tanı değildir.** "Yavaş", "pahalı",
"ölçeklenmiyor" cümlelerinin hiçbiri bir nedene işaret etmez; tanı, aynı belirtiyi veren iki nedeni
birbirinden ayıran ölçümdür. On altı dersin hiçbirinde belirti tek başına yeterli olmadı ve birkaçında
en çok bakılan ölçüt en az ayırt eden ölçüt çıktı — işçi doluluğu iki nedeni ayıramadı (0,9562 ile
0,9545), toplam kaynak iki nedeni ayıramadı (ikisi de 759.403), yanıt süresi yüzdeliği on tanının
altısında birden göründü.

Dördüncü sütun ikinci kuralı taşır: **hiçbir kalıp her koşulda yanlış değildir.** Her ders aynı
biçimin doğru olduğu koşulu bir sayıyla sınırladı. Hesabın depoda kalması, sınırdan geçen kaydı
azalttığı ölçüde haklıdır — F1'de 100 kat azaltıyor. Fazla alan taşımak, ortadan kaldırdığı çağrının
280 baytlık zarfından küçükse haklıdır. Önbelleksizlik, tekrar 1,01 olan bir akışta doğru tasarımdır;
orada belleme kazanmak yerine 0,71 kata düşer. Paylaşılan kaynak, gürültü katsayısı bire yakınken ve
hiçbir tek istek turluk kapasiteyi aşmazken doğrudur. Engelleyen çağrı, bekleme yerel işle aynı
mertebedeyken doğrudur. Karşıt kalıp bir yasak değil, **bir ölçekte** yanlışa dönen bir seçimdir; o
ölçeği veren şey de üçüncü sütundaki ölçümdür.

Altı kurs bir tasarım dilini parça parça kurdu: trafiğin nasıl karşılandığı, uygulamanın nasıl
bölündüğü, verinin nasıl dağıtıldığı, arızanın nasıl çevrelendiği ve bütün bunların nasıl görünür
kılındığı. Her kurs kendi sorusunu kendi ölçüsüyle yanıtladı. Yapılmayan tek şey şudur: bu parçaların
**tek bir problemde birlikte** kullanılması ve seçimlerin gerekçesiyle baştan sona anlatılması. Bir
tasarım tartışmasında sorular kurs sınırlarına göre sıraya girmez; hepsi aynı anda sorulur ve
aralarındaki ödünleşim tek tek değil birlikte savunulur. Sonraki kurs **Vaka Çalışmaları** o işi
yapar.

