---
title: 'Vaka Çalışmaları'
source: 'https://academia.sh/tr/kurslar/vaka-calismalari'
language: tr
updated: '2026-08-17T04:43:03+00:00'
license: 'CC BY-SA 4.0'
lessons: 14
outcomes:
    - 'Tanınmış sistem tasarımı problemlerini uçtan uca çözer'
    - 'Her vakada kısıtları ve ödünleşimleri açıkça yazar'
    - 'Alternatif tasarımları karşılaştırarak seçim gerekçesi kurar'
---

# Vaka Çalışmaları

Altı kursun ayrı ayrı kurduğu kararların tek bir problemde birlikte savunulması: her vakanın kısıtlarının sayıya çevrilmesi, kendi varsayım tablosunu kurup kabaca büyüklük hesabını çalıştırması, tasarımın önceki kurslarda kurulmuş kalıplardan adıyla derlenmesi, en az bir alternatifin sayıyla elenmesi, kullanılmayan kalıbın gerekçesinin yazılması ve tasarımın arıza altındaki davranışıyla feda ettiği şeyin açıkça söylenmesi.

## Okuma Ağırlıklı Sistemler

Okumanın yazmayı büyük farkla geçtiği dört tasarım: anahtar üretimi, yönlendirme ve önbellekle çalışan bağlantı kısaltma servisi, yazmada dağıtma ile okumada toplama modellerinin aynı akış için karşılaştırılması, önek yapıları ve güncelleme gecikmesiyle kurulan arama önerisi ve statik ile dinamik içeriğin ayrıldığı içerik dağıtım tasarımı.

### Bağlantı Kısaltma Servisi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/baglanti-kisaltma-servisi

Okuma ağırlıklı ilk vaka: kısıtların işlevsel ve işlevsel olmayan gereksinim olarak sayıya çevrilmesi, dokuz varsayımdan kabaca büyüklük hesabının çıkarılması, kısa anahtar uzunluğunun doluluk oranı ile çakışma ve tahmin maliyeti üzerinden seçilmesi, sayaç tabanlı üretimin ölçülmüş bir farkla elenmesi ve tasarımın parça arızasındaki davranışıyla feda ettiği tazeliğin yazılması.

Altı kurs bir tasarım dilini parça parça kurdu. Bu kursun işi yeni bir mekanik öğretmek değildir;
kurulmuş kalıpları **tek bir problemde birlikte** kullanmak ve seçimleri gerekçesiyle savunmaktır.
Her vaka aynı altı adımı yürür: kısıtlar sayıya çevrilir, varsayımlar kendi tablosunda toplanır,
tablodan kabaca büyüklük hesabı çıkar, tasarım kalıplardan adıyla derlenir, en az bir alternatif bir
sayıyla elenir ve tasarımın arıza altındaki davranışıyla feda ettiği şey yazılır.

İlk vaka **bağlantı kısaltma servisidir**: uzun bir bağlantı verildiğinde kısa bir anahtar üretilir,
o anahtar istendiğinde asıl hedefe yönlendirilir. Tasarım tek bir karara bakar — **kısa anahtarın
nasıl üretileceğine** — ve bu ders o kararı ölçer.

#### Kısıtlar

**İşlevsel gereksinimler.** I1: uzun bağlantı için kısa anahtar üretilir ve döndürülür. I2: kısa
anahtar istendiğinde hedefe yönlendirilir. I3: bağlantının sona erme zamanı vardır, süresi dolan
yönlendirilmez. I4: sahibi bağlantıyı siler. I5: bir kısa anahtar iki ayrı hedefe verilmez.

**Kapsam daraltması.** Tıklama sayımı, kişiye özel anahtar talebi, hedef bağlantının zararlılık
taraması ve hesap yönetimi bu tasarımın dışındadır; her biri kendi yazma yolunu getirir ve aşağıdaki
kararları oynatmaz.

**İşlevsel olmayan gereksinimler.** Eşiğin sınıfı değil kaynağı yazılır; biçim Sistem Tasarımına
Giriş kursunun İşlevsel ve İşlevsel Olmayan Gereksinimler dersinindir.

| Kod | Eşik | Eşiğin kaynağı |
|---|---|---|
| G1 | yönlendirme yanıtının ortancası 50 ms'yi geçmez | yönlendirme, tıklama ile hedef sayfa arasına giren fazladan bir turdur |
| G2 | kısaltma çağrısı 300 ms'yi geçmez | sonuç ekranda beklenir |
| G3 | yönlendirme yolunun hizmet erişilebilirliği yüzde 99,95 | kısaltılmış bağlantı yazının içine gömülür ve geri alınamaz |
| G4 | rastgele bir anahtar tahmininin isabet olasılığı 1/1000'in altındadır | bağlantı gizli değildir ama listelenebilir de olmamalıdır |

#### Varsayımlar

Tablo bu dersin kendisinindir; sonraki vakalar kendi tablolarını kurar ve buradan sayı devralmaz.

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BK1 | günde oluşturulan kısaltma | 500.000 | kısaltma bir paylaşım eylemine bağlıdır, seyrektir |
| BK2 | kısaltma başına günlük yönlendirme | 100 | bağlantı paylaşıldığı gün çok kez açılır |
| BK3 | tepe çarpanı | 3 | tepe saatteki hızın gün ortalamasına oranı |
| BK4 | bağlantı kaydı | 500 bayt | hedef bağlantı, sahip, oluşturma ve sona erme zamanı |
| BK5 | saklama süresi | 1825 gün | yenilenmeyen bağlantı beş yıl sonra düşer |
| BK6 | isteklerin 0,80'ini alan anahtar payı | 0,01 | erişim az sayıda yeni bağlantıda yoğunlaşır |
| BK7 | alfabe boyutu | 62 | rakam ve iki kasa harf; ayraç yok, elle yazılabilir |
| BK8 | önbellek kaydı | 300 bayt | anahtar ve hedef bağlantı |
| BK9 | yönlendirme yanıtı | 300 bayt | gövdesiz yanıt ve başlıkları |

#### Kabaca Büyüklük Hesabı

```js
// kisaltma/hesap.mjs — BK1–BK9'dan cikan kabaca buyukluk hesabi ve anahtar uzunlugu taramasi
const BK = {
  gunlukKisaltma: 500_000,   // BK1
  yonlendirmeOrani: 100,     // BK2
  tepeCarpani: 3,            // BK3
  kayitBayt: 500,            // BK4
  saklamaGun: 1825,          // BK5
  sicakPay: 0.01,            // BK6: isteklerin 0,80'ini alan anahtar payi
  sicakIstekPayi: 0.80,      // BK6
  alfabe: 62,                // BK7
  onbellekKayitBayt: 300,    // BK8
  yanitBayt: 300,            // BK9
};
const GUN = 86_400, AY_DK = 43_800, HEDEF = 0.9995;   // HEDEF: G3 esigi, varsayim degil

function hesapla(v) {
  const okumaTepe = ((v.gunlukKisaltma * v.yonlendirmeOrani) / GUN) * v.tepeCarpani;
  const toplam = v.gunlukKisaltma * v.saklamaGun;
  return {
    toplam,
    okumaTepe,
    yazmaTepe: (v.gunlukKisaltma / GUN) * v.tepeCarpani,
    depoOkuma: okumaTepe * (1 - v.sicakIstekPayi),
    cikisMbit: (okumaTepe * v.yanitBayt * 8) / 1e6,
    depoGB: (toplam * v.kayitBayt) / 1e9,
    onbellekGB: (toplam * v.sicakPay * v.onbellekKayitBayt) / 1e9,
  };
}

const b = (x, n = 2) => x.toFixed(n);
const r = hesapla(BK);
console.log(`saklama penceresinde toplam baglanti = ${r.toplam.toLocaleString("tr-TR")}`);
console.log(`tepe yonlendirme/s = ${b(r.okumaTepe)}   tepe kisaltma/s = ${b(r.yazmaTepe)}   oran = ${b(r.okumaTepe / r.yazmaTepe)}`);
console.log(`onbellek arkasi yonlendirme/s = ${b(r.depoOkuma)}   cikis = ${b(r.cikisMbit)} Mbit/s`);
console.log(`depo = ${b(r.depoGB)} GB   onbellek = ${b(r.onbellekGB)} GB   G3 kesinti butcesi = ${b((1 - HEDEF) * AY_DK, 1)} dk/ay`);

console.log(`\n${"uzunluk".padEnd(9)}${"anahtar uzayi".padStart(19)}${"doluluk".padStart(11)}` +
  `${"fazla deneme/kayit".padStart(20)}${"tepede fazla dokunus/s".padStart(24)}${"isabet basina tahmin".padStart(22)}`);
const fazlaDeneme = {};
for (const L of [5, 6, 7, 8]) {
  const uzay = BK.alfabe ** L, d = r.toplam / uzay;
  // Yeniden deneme beklentisi: k anahtar dagitilmisken bir denemenin bosa cikma olasiligi k/uzay.
  let fazla = 0;
  for (let k = 0; k < r.toplam; k += r.toplam / 1e5) fazla += (1 / (1 - k / uzay) - 1) / 1e5;
  fazlaDeneme[L] = fazla;
  console.log(`${String(L).padEnd(9)}${uzay.toLocaleString("tr-TR").padStart(19)}${b(d, 6).padStart(11)}` +
    `${b(fazla, 6).padStart(20)}${b(fazla * r.yazmaTepe, 4).padStart(24)}${b(1 / d, 1).padStart(22)}`);
}

const PARCA = 8, dusen = r.okumaTepe / PARCA * (1 - BK.sicakIstekPayi);
console.log(`\ntek parca arizasi (${PARCA} parca): dusen yonlendirme ${b(dusen)}/s = isteklerin ` +
  `${b(dusen / r.okumaTepe, 4)}'i, reddedilen kisaltma ${b(r.yazmaTepe / PARCA)}/s`);
console.log(`G3 butcesinin tek parca arizasina karsiligi = ${b((1 - HEDEF) * AY_DK / (dusen / r.okumaTepe), 1)} dk/ay`);

const iki = hesapla({ ...BK, gunlukKisaltma: BK.gunlukKisaltma * 2 });
console.log(`\nBK1 duyarliligi: gunluk kisaltma 500.000 -> 1.000.000 ise depo ${b(r.depoGB)} -> ${b(iki.depoGB)} GB, ` +
  `7 karakterde doluluk ${b(r.toplam / BK.alfabe ** 7, 6)} -> ${b(iki.toplam / BK.alfabe ** 7, 6)}, ` +
  `isabet basina tahmin ${b(BK.alfabe ** 7 / r.toplam, 1)} -> ${b(BK.alfabe ** 7 / iki.toplam, 1)}`);
```

```
saklama penceresinde toplam baglanti = 912.500.000
tepe yonlendirme/s = 1736.11   tepe kisaltma/s = 17.36   oran = 100.00
onbellek arkasi yonlendirme/s = 347.22   cikis = 4.17 Mbit/s
depo = 456.25 GB   onbellek = 2.74 GB   G3 kesinti butcesi = 21.9 dk/ay

uzunluk        anahtar uzayi    doluluk  fazla deneme/kayit  tepede fazla dokunus/s  isabet basina tahmin
5                916.132.832   0.996035            4.550910                 79.0089                   1.0
6             56.800.235.584   0.016065            0.008120                  0.1410                  62.2
7          3.521.614.606.208   0.000259            0.000130                  0.0022                3859.3
8        218.340.105.584.896   0.000004            0.000002                  0.0000              239276.8

tek parca arizasi (8 parca): dusen yonlendirme 43.40/s = isteklerin 0.0250'i, reddedilen kisaltma 2.17/s
G3 butcesinin tek parca arizasina karsiligi = 876.0 dk/ay

BK1 duyarliligi: gunluk kisaltma 500.000 -> 1.000.000 ise depo 456.25 -> 912.50 GB, 7 karakterde doluluk 0.000259 -> 0.000518, isabet basina tahmin 3859.3 -> 1929.7
```

Bu sayılar **hesap** sınıfındadır ve dördü tasarımı belirliyor. Uçtaki oran tam 100: saniyede
1736,11 yönlendirmeye karşılık 17,36 kısaltma. Saklama penceresi 912.500.000 bağlantı ve 456,25 GB
tutuyor, tek düğümün taşımayacağı kadar. Önbelleğin arkasına 347,22 istek/s düşüyor ve sıcak kümenin
tamamı 2,74 GB, yani belleğe sığıyor. En belirleyicisi anahtar uzunluğu tablosudur: beş karakter bu
hacim için **dolu**dur (doluluk 0,996035), altı karakter G4'ü karşılamaz (isabet başına 62,2 tahmin,
eşik 1000), yedi karakter ikisini birden karşılar (3859,3 tahmin, kayıt başına 0,000130 fazla
deneme). BK1 iki katına çıksa bile yedide isabet başına 1929,7 tahmin kalıyor ve eşik tutuyor.

#### Anahtar Üretiminin Çakışma Oranı

Tablodaki `fazla deneme/kayit` sütunu bir beklentidir. Rastgele üretimin böyle davrandığı, çakışma
oranının **yalnız doluluk oranına** bağlı olduğu ve sayaçla farkın nerede oluştuğu ölçülür.

```js
// kisaltma/anahtar.mjs — anahtar uretiminin cakisma orani: rastgele uretimin doluluga bagimliligi
// ve sayac tabanli uretimle karsilastirilmasi. Anahtar uzayi bir yuva dizisiyle temsil edilir,
// uretec kendi yazilmistir, tohum sabittir; sayilar makineye bagli degildir. MODELDIR.
const TOHUM = 20260730;
const uretec = (t) => { let s = t % 2147483647; return () => (s = (s * 48271) % 2147483647) / 2147483647; };

function rastgeleDoldur(m, n, rast) {           // cakisirsa yeni anahtar cekilir
  const dolu = new Uint8Array(m);
  let deneme = 0;
  for (let i = 0; i < n; i += 1) {
    let y;
    do { y = Math.floor(rast() * m); deneme += 1; } while (dolu[y]);
    dolu[y] = 1;
  }
  return { dolu, fazla: deneme - n };
}

function beklenenFazla(m, n) {                  // k yuva doluyken bir denemenin bosa cikma olasiligi k/m
  let t = 0;
  for (let k = 0; k < n; k += 1) t += 1 / (1 - k / m) - 1;
  return t;
}

const M = 2_000_000, DOLULUK = [0.016065, 0.1, 0.5, 0.9, 0.996035];
const b = (x, n = 6) => x.toFixed(n);
const say = (x) => x.toLocaleString("tr-TR");
console.log(`model: ${say(M)} yuvali anahtar uzayi, tohum ${TOHUM}, uretec kendi yazildi`);
console.log(`\n${"doluluk".padEnd(10)}${"eklenen".padStart(10)}${"fazla deneme".padStart(14)}` +
  `${"olculen/kayit".padStart(15)}${"beklenen/kayit".padStart(16)}${"olculen/beklenen".padStart(18)}`);
for (const d of DOLULUK) {
  const n = Math.round(d * M);
  const { fazla } = rastgeleDoldur(M, n, uretec(TOHUM));
  const bek = beklenenFazla(M, n);
  console.log(`${b(d).padEnd(10)}${say(n).padStart(10)}${say(fazla).padStart(14)}` +
    `${b(fazla / n).padStart(15)}${b(bek / n).padStart(16)}${b(fazla / bek, 4).padStart(18)}`);
}

// Olcek denetimi: cakisma orani uzayin buyuklugune degil doluluga bagli.
console.log("");
for (const m of [250_000, 2_000_000, 8_000_000]) {
  const n = m / 2;
  const { fazla } = rastgeleDoldur(m, n, uretec(TOHUM));
  console.log(`olcek denetimi: uzay ${say(m).padStart(9)}, doluluk 0,500000 -> fazla deneme/kayit ${b(fazla / n)}`);
}

// Iki uretim bicimi ayni dolulukta: sayac cakismaz, ama anahtarlari bitisiktir.
const TAHMIN = 20_000, BLOK = 1000, d6 = 0.016065, n6 = Math.round(d6 * M);
const rastgele = rastgeleDoldur(M, n6, uretec(TOHUM));
const sayacDolu = new Uint8Array(M);
for (let i = 0; i < n6; i += 1) sayacDolu[i] = 1;                // sayac 0'dan artarak dagitir
const tahminci = uretec(TOHUM + 1);
let rastIsabet = 0, sayacIsabet = 0;
for (let g = 0; g < TAHMIN; g += 1) {
  if (rastgele.dolu[Math.floor(tahminci() * M)]) rastIsabet += 1; // rastgele anahtarda rastgele tahmin
  if (sayacDolu[g]) sayacIsabet += 1;                             // sayac anahtarda sirali tahmin
}
const satir = (ad, fazla, esgudum, isabet) => console.log(`${ad.padEnd(18)}${say(fazla).padStart(14)}` +
  `${esgudum.padStart(17)}${say(isabet).padStart(12)}${(TAHMIN / isabet).toFixed(1).padStart(22)}`);
console.log(`\ndoluluk ${b(d6)}, ${say(n6)} anahtar dagitilmis, ${say(TAHMIN)} tahmin`);
console.log(`${"uretim bicimi".padEnd(18)}${"fazla deneme".padStart(14)}${"esgudum cagrisi".padStart(17)}` +
  `${"isabet".padStart(12)}${"isabet basina tahmin".padStart(22)}`);
satir("rastgele", rastgele.fazla, "0", rastIsabet);
satir(`sayac (blok ${BLOK})`, 0, (n6 / BLOK).toFixed(1), sayacIsabet);
```

```
model: 2.000.000 yuvali anahtar uzayi, tohum 20260730, uretec kendi yazildi

doluluk      eklenen  fazla deneme  olculen/kayit  beklenen/kayit  olculen/beklenen
0.016065      32.130           256       0.007968        0.008119            0.9813
0.100000     200.000        10.771       0.053855        0.053605            1.0047
0.500000   1.000.000       385.317       0.385317        0.386294            0.9975
0.900000   1.800.000     2.802.181       1.556767        1.558425            0.9989
0.996035   1.992.070     9.053.122       4.544580        4.552201            0.9983

olcek denetimi: uzay   250.000, doluluk 0,500000 -> fazla deneme/kayit 0.386128
olcek denetimi: uzay 2.000.000, doluluk 0,500000 -> fazla deneme/kayit 0.385317
olcek denetimi: uzay 8.000.000, doluluk 0,500000 -> fazla deneme/kayit 0.384476

doluluk 0.016065, 32.130 anahtar dagitilmis, 20.000 tahmin
uretim bicimi       fazla deneme  esgudum cagrisi      isabet  isabet basina tahmin
rastgele                     256                0         344                  58.1
sayac (blok 1000)              0             32.1      20.000                   1.0
```

Ölçüm üç şey veriyor. Ölçülen ile beklenen oranı her dolulukta 0,98 ile 1,01 arasında; kapalı biçim
rastgele üretimi temsil ediyor ve tablodaki 0,000130 güvenilir. Ölçek denetimi çakışma oranının
uzayın büyüklüğüne değil doluluğa bağlı olduğunu gösteriyor: uzay otuz iki katına çıktığında oran
0,386128'den 0,384476'ya, aynı üçüncü haneye oturuyor — iki milyon yuvalık modelin 3,5 trilyonluk
uzay hakkında konuşabilmesinin gerekçesi budur. Üçüncüsü, oran doluluğa doğrusal bağlı değil:
0,10'da kayıt başına 0,05 fazla deneme varken 0,996035'te 4,54.

Son tablo iki üretim biçimini yan yana koyuyor. Sayaç hiç çakışmıyor; bedeli iki kalemdir. Birincisi
eşgüdüm: sayaç tek yerden dağıtılır, bin anahtarlık bloklarla 32.130 anahtar için 32,1 çağrı eder —
tepe hızda saniyede 0,017 çağrı, ihmal edilebilir. İkincisi tahmin edilebilirlik ve ihmal edilemez:
sıralı tahmin eden bir istemci 20.000 denemenin 20.000'inde isabet ediyor, rastgelede 344'ünde.

#### Tasarım

Hiçbir bileşen yeniden anlatılmaz; her biri kurulduğu derse bağlanır ve bu vakadaki **parametresi**
yazılır.

- **Kenar önbelleği** (Trafik Katmanı, İçerik Dağıtım Ağları). Parametre, yönlendirme yanıtının
  kenardaki ömrüdür: **300 saniye**. Bu, Veri Katmanı Ölçekleme kursunun Tutarlılık ve Bayat Veri
  dersindeki **bayatlık penceresidir** ve I3 ile I4'ün kenarda gecikmeli geçerli olması demektir.
- **Yönlendirme servisi durumsuzdur** (Uygulama Katmanı, Durumsuz Servisler); istek başına taşınan
  durum sıfır olduğu için **oturum yapışkanlığı** (Trafik Katmanı, Oturum Yapışkanlığı) kapalıdır.
- **Anahtar–değer deposu** (Veri Katmanı Ölçekleme, Depo Türleri), sekiz parçaya **parçalanmış**
  (Veri Dağıtımı, Parçalama). Parça anahtarı **kısa anahtarın kendisidir**: hem yönlendirme hem
  silme anahtarı zaten bilir, iki örüntü de tek düğüme dokunur ve dağıt–topla oluşmaz.
- **Yanında okuma önbelleği** (Veri Katmanı Ölçekleme, Yanında Okuma). Parametre BK6'dan gelir:
  2,74 GB'lık sıcak küme isteklerin 0,80'ini karşılar, arkasına 347,22 istek/s kalır.
- **API ağ geçidi ve hız sınırlama** (Trafik Katmanı, API Ağ Geçidi), yalnız kısaltma yolunda.
  Parametre hesap başına **dakikada 20 kısaltmadır**: tepe yazma saniyede 17,36'dır ve tek bir
  istemcinin bu akışı doldurabilmesi istenmez.
- **Sonunda tutarlılık** (Sistem Tasarımına Giriş, Tutarlılık Modelleri). Yeni anahtarın kopyalara
  yayılması zaman alır; parametre, kısaltmadan hemen sonraki ilk yönlendirmenin ana kopyaya
  gitmesidir, ki oluşturan kendi bağlantısını açabilsin.

**Bilerek kullanılmayan iki kalıp.** Komut ve sorgu sorumluluğu ayrımı (Veri Katmanı Ölçekleme,
Komut ve Sorgu Ayrımı) kullanılmıyor: okuma modeli yazma modelinin aynısıdır, türetilecek izdüşüm
yoktur. **İleti kuyruğu** (Uygulama Katmanı, Mesaj Kuyrukları) kısaltma yolunda kullanılmıyor:
çağıran anahtarı yanıtın içinde bekler.

#### Elenen Alternatif: Sayaç Tabanlı Üretim

Alternatif tasarım anahtarı rastgele çekmez; artan bir sayacı 62 tabanına çevirerek üretir. Çakışma
tanım gereği sıfırdır, I5 aritmetikle garanti edilir ve deponun varlık denetimi hiç yapılmaz.

Eleme sayısı G4'tedir. Sayaç anahtarları bitişik olduğu için sıralı tahmin eden bir istemci her
denemesinde isabet eder (ölçümde 20.000/20.000, isabet başına 1,0 tahmin); yedi karakterlik rastgele
üretimde isabet başına 3859,3 tahmin gerekir. Fark **3859 kat**tır ve eşik 1000'dir; sayaç bunu
hiçbir anahtar uzunluğunda karşılayamaz, çünkü sorun uzay büyüklüğü değil bitişikliktir. Kazandığı
şey ise ölçüldü ve küçük: yedi karakterde rastgele üretimin tepede eklediği fazla depo dokunuşu
saniyede 0,0022'dir, 17,36 yazmanın on binde birinden azı.

Alternatif hangi kısıt değişirse kazanır: anahtar uzunluğu beş karaktere sabitlenseydi doluluk
0,996035 olur, rastgele üretim kayıt başına 4,55 fazla deneme ve tepede saniyede 79,01 fazla dokunuş
üretir — yazma yükünün dört buçuk katı. O durumda tek çalışan üretim biçimi sayaçtır ve G4 zaten
hiçbir biçimle karşılanamaz.

#### Arıza Davranışı ve Feda Edilen

**Bir depo parçası düşerse** (Dayanıklılık ve Güvenilirlik, Arıza Kipleri) o parçanın anahtarları
yalnız önbellekten karşılanır: yönlendirmelerin 0,0250'si, saniyede 43,40 istek düşer ve 2,17
kısaltma reddedilir. G3'ün aylık 21,9 dakikalık kesinti bütçesi bu kısmi arızayla ölçüldüğünde
**876,0 dakikalık** tek parça kesintisine karşılık geliyor.

**Depo tümüyle düşerse** yönlendirme **zarif bozulmayla** (Dayanıklılık ve Güvenilirlik, Zarif
Bozulma) önbellekten sürer, isteklerin 0,80'i yanıtlanır; kısaltma yolu kapatılır, çünkü
yazılamayan bir anahtarı döndürmek I5'i bozar.

**Feda edilen tazeliktir.** Silinen ya da süresi dolan bir bağlantı kenardaki 300 saniyelik pencere
boyunca çalışmaya devam eder. Tasarım bu pencereyi bilerek satın aldı: karşılığında yönlendirmelerin
büyük bölümü kökene hiç ulaşmıyor.

#### Özet

- Kısıtlar beş işlevsel gereksinime ve dört eşiğe indi; kapsam dışı dört iş yazıldı ve eşiklerin
  yanına sınıf değil kaynak kondu.
- Dokuz varsayımdan çıkan hesap uçtaki oranı 100 verdi (1736,11 yönlendirme/s, 17,36 kısaltma/s),
  saklama penceresini 456,25 GB'ye ve sıcak kümeyi 2,74 GB'ye oturttu.
- Anahtar uzunluğu iki eşik tarafından birden belirlendi: beş karakter dolu (0,996035), altı karakter
  G4'ü karşılamıyor (62,2 tahmin), yedi karakter ikisini de karşılıyor (0,000130 fazla deneme, 3859,3
  tahmin).
- Çakışma oranı uzayın büyüklüğüne değil doluluğa bağlı: 0,50 dolulukta üç ayrı uzayda 0,386128 /
  0,385317 / 0,384476 çıktı, ölçülen/beklenen her satırda 0,98–1,01 arasında.
- Sayaç tabanlı üretim çakışmayı sıfırlıyor ama sıralı tahminde isabet başına 1,0 tahmin bırakıyor;
  kazandığı şey tepede saniyede 0,0022 depo dokunuşudur, bu yüzden elendi.
- Tek parça arızası isteklerin 0,0250'sini düşürüyor, G3'ün 21,9 dakikalık bütçesi 876,0 dakikalık
  parça kesintisine denk geliyor; feda edilen, kenardaki 300 saniyelik bayatlık penceresidir.

#### Sonraki Adım

Bu vakada okuma yazmayı yüz katı geçiyordu ve okumanın yanıtı **tek bir kayıttı**: anahtar verilir,
hedef döner. Önbellek kararı bu yüzden basit kaldı ve yönlendirme yolunda hiçbir birleştirme
yapılmadı. Sonraki vakada okuma oranı benzer kalır ama yanıt değişir: tek kayıt değil, çok sayıda
kaynaktan derlenmiş sıralı bir liste istenir. Soru o zaman "kaydı nerede önbelleğe alayım" olmaktan
çıkar ve şuna döner: liste **ne zaman** derlenir — yazma anında herkesin kutusuna dağıtılarak mı,
okuma anında toplanarak mı? İki seçeneğin iş miktarı aynı akış için çok farklıdır ve fark kullanıcı
başına değil, takipçi dağılımının ucuna göre belirlenir.

### Haber Akışı

Source: https://academia.sh/tr/kurslar/vaka-calismalari/haber-akisi

Aynı akışın iki dağıtım modeliyle kurulması: yazmada dağıtma ile okumada toplamanın iş miktarının ölçülmesi, takipçi dağılımının ucunun ortalamayla yapılan hesabı yanıltması, karma modelin eşiğinin dağıtım kapasitesi ile görünme penceresinden bir sayı olarak türetilmesi ve dağıtım tüketicisi yavaşladığında pencerenin aşılma anının hesaplanması.

Önceki vakada okumanın yanıtı tek bir kayıttı: anahtar verilir, hedef döner. Bu vakada okuma oranı
benzer kalır ama yanıt değişir — **haber akışı** çok sayıda kaynaktan derlenmiş sıralı bir listedir.
Soru bu yüzden "kaydı nerede önbelleğe alayım" olmaktan çıkar ve şuna döner: liste **ne zaman**
derlenir? İki seçenek vardır — **yazmada dağıtma**, gönderiyi yayımlandığı anda her takipçinin
kutusuna yazar; **okumada toplama**, akış istendiği anda takip edilen hesapların gönderilerini
birleştirir. Bu ders ikisinin iş miktarını ölçer ve karma modelin eşiğini bir sayı olarak verir.

#### Kısıtlar

**İşlevsel gereksinimler.** I1: kullanıcı gönderi yayımlar. I2: kullanıcı, takip ettiklerinin
gönderilerini zamana göre sıralı olarak ister. I3: kullanıcı takip eder ve takibi bırakır. I4:
silinen gönderi akışta görünmez.

**Kapsam daraltması.** İlgi puanına göre sıralama, ortam dosyaları, bildirim ve arama bu tasarımın
dışındadır; sıralama modeli akışın **içeriğini** değiştirir, dağıtım kararını değil.

| Kod | Eşik | Eşiğin kaynağı |
|---|---|---|
| G1 | akışın ilk sayfası (20 gönderi) 200 ms'de döner | akış açılışında beklenen ilk görüntü |
| G2 | yayımlanan gönderi takipçi akışında 60 saniye içinde görünür | akışın canlı sayılması |
| G3 | akış kutusu deposu 20 GB'yi geçmez | kutunun bellekte tutulması |

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| VH1 | günlük etkin kullanıcı | 1.000.000 | akışı günde en az bir kez açan kullanıcı |
| VH2 | kullanıcı başına günlük gönderi | 0,2 | çoğu kullanıcı okur, azı yazar |
| VH3 | kullanıcı başına günlük akış isteği | 8 | akış gün içinde birkaç kez tazelenir |
| VH4 | tepe çarpanı | 3 | tepe saatteki hızın gün ortalamasına oranı |
| VH5 | takipçi dağılımı | taban 33, üs 1,16 | takipçi az sayıda hesapta yoğunlaşır; üs kuyruğun kalınlığıdır |
| VH6 | kutuda tutulan girdi | 400 | yirmi sayfalık geçmiş yeter, gerisi gönderi deposundan gelir |
| VH7 | kutu girdisi | 32 bayt | gönderi kimliği ve zaman damgası |

VH5 bir sayı değil bir **dağılımdır**; ortalaması modelden çıkar ve bir varsayım olarak
seçilmemiştir. Bir hesabın takipçi sayısı kullanıcı sayısını geçemez, model bu tavanı uygular.

#### Kabaca Büyüklük Hesabı ve İki Modelin İş Miktarı

```js
// akis/hesap.mjs — VH1–VH7'den cikan hesap ve iki dagitim modelinin is miktari.
// Takipci dagilimi kendi yazilmis uretecle uretilir (tohum sabit), tavan kullanici sayisidir. MODELDIR.
const VH = { kullanici: 1_000_000, gonderi: 0.2, akisIstegi: 8, tepe: 3, taban: 33, us: 1.16, kutuGirdi: 400, girdiBayt: 32 };
const GUN = 86_400;
const gonderiTepe = (VH.kullanici * VH.gonderi / GUN) * VH.tepe;
const akisTepe = (VH.kullanici * VH.akisIstegi / GUN) * VH.tepe;

let s = 20260730 % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
const takipci = new Float64Array(VH.kullanici);
let tavanda = 0;
for (let i = 0; i < VH.kullanici; i += 1) {
  const v = Math.round(VH.taban * Math.pow(rast(), -1 / VH.us));
  if (v > VH.kullanici - 1) tavanda += 1;
  takipci[i] = Math.min(VH.kullanici - 1, v);
}
const toplamBag = takipci.reduce((a, b) => a + b, 0), ortalama = toplamBag / VH.kullanici;
const sirali = Float64Array.from(takipci).sort();
const y = (p) => sirali[Math.floor(p * VH.kullanici)];
const ustPay = sirali.slice(VH.kullanici - VH.kullanici / 1000).reduce((a, b) => a + b, 0) / toplamBag;

const b = (x, n = 2) => x.toFixed(n);
console.log(`tepe gonderi/s = ${b(gonderiTepe)}   tepe akis istegi/s = ${b(akisTepe)}   okuma/yazma orani = ${b(akisTepe / gonderiTepe)}`);
console.log(`kutu deposu = ${b(VH.kullanici * VH.kutuGirdi * VH.girdiBayt / 1e9)} GB (kullanici basina ${VH.kutuGirdi} girdi)`);
console.log(`\ntakipci dagilimi: ortalama ${b(ortalama)}  p50 ${y(0.5)}  p90 ${y(0.9)}  p99 ${y(0.99)}  ` +
  `p99,9 ${y(0.999)}  en buyuk ${sirali[VH.kullanici - 1]}  tavandaki hesap ${tavanda}`);
console.log(`en ustteki binde birlik dilim baglantilarin ${b(ustPay, 4)}'ini tutuyor`);

const yazmaIs = gonderiTepe * ortalama, okumaIs = akisTepe * ortalama;
console.log(`\n${"model".padEnd(20)}${"yazma basina is".padStart(17)}${"okuma basina is".padStart(17)}${"tepede is/s".padStart(14)}`);
console.log(`${"yazmada dagitma".padEnd(20)}${b(ortalama).padStart(17)}${"1.00".padStart(17)}${b(yazmaIs + akisTepe).padStart(14)}`);
console.log(`${"okumada toplama".padEnd(20)}${"1.00".padStart(17)}${b(ortalama).padStart(17)}${b(okumaIs + gonderiTepe).padStart(14)}`);
console.log(`oran = ${b((okumaIs + gonderiTepe) / (yazmaIs + akisTepe))}`);

const iki = ((VH.kullanici * VH.akisIstegi * 2 / GUN) * VH.tepe) * ortalama;
console.log(`\nVH3 duyarliligi: gunluk akis istegi 8 -> 16 ise okumada toplama ${b(okumaIs)} -> ${b(iki)} is/s, ` +
  `yazmada dagitma ${b(yazmaIs)} is/s degismiyor`);
```

```
tepe gonderi/s = 6.94   tepe akis istegi/s = 277.78   okuma/yazma orani = 40.00
kutu deposu = 12.80 GB (kullanici basina 400 girdi)

takipci dagilimi: ortalama 203.63  p50 60  p90 241  p99 1763  p99,9 12474  en buyuk 999999  tavandaki hesap 7
en ustteki binde birlik dilim baglantilarin 0.2753'ini tutuyor

model                 yazma basina is  okuma basina is   tepede is/s
yazmada dagitma                203.63             1.00       1691.87
okumada toplama                  1.00           203.63      56570.71
oran = 33.44

VH3 duyarliligi: gunluk akis istegi 8 -> 16 ise okumada toplama 56563.76 -> 113127.53 is/s, yazmada dagitma 1414.09 is/s degismiyor
```

Üç sayı kararı taşıyor. Birincisi oran: aynı akış yazmada dağıtmayla tepede 1691,87, okumada
toplamayla 56.570,71 iş/s istiyor — **33,44 kat**. Fark okuma/yazma oranının kendisidir; iş, hangi
tarafta çoğaltılırsa o tarafın hızıyla çarpılır ve okuma tarafı kırk kat daha sık koşar. İkincisi
kutu deposu 12,80 GB, G3'ün 20 GB'lik eşiğinin altında. Üçüncüsü duyarlılık: akış isteği ikiye
katlandığında okumada toplama 113.127,53'e çıkarken yazmada dağıtma 1414,09'da kalıyor. Yazmada
dağıtma **okuma hacminden bağımsızdır**; asıl özelliği budur.

Dağılım satırı bir uyarı taşıyor. Ortalama 203,63 ama ortanca 60, yüzde 99,9'luk değer 12.474 ve en
büyük hesap tavanda. En üstteki binde birlik dilim bağlantıların 0,2753'ünü tutuyor. Ortalamayla
yapılan hesap kararlı hızı doğru veriyor, ama tek bir gönderinin ne istediğini hiç göstermiyor.

#### Karma Modelin Eşiği

```js
// akis/karma.mjs — karma modelin esigi: esigin ustundeki hesaplar okuma aninda toplanir.
// Ayni dagilim ureteci ve ayni tohum; takip olasiligi populerlikle orantili kabul edilir. MODELDIR.
const VH = { kullanici: 1_000_000, gonderi: 0.2, akisIstegi: 8, tepe: 3, taban: 33, us: 1.16 };
const GUN = 86_400, PENCERE = 60;                       // PENCERE: G2 esigi
const gonderiTepe = (VH.kullanici * VH.gonderi / GUN) * VH.tepe;
const akisTepe = (VH.kullanici * VH.akisIstegi / GUN) * VH.tepe;

let s = 20260730 % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
const takipci = new Float64Array(VH.kullanici);
for (let i = 0; i < VH.kullanici; i += 1)
  takipci[i] = Math.min(VH.kullanici - 1, Math.round(VH.taban * Math.pow(rast(), -1 / VH.us)));
const sirali = Float64Array.from(takipci).sort();
const ortalama = sirali.reduce((a, b) => a + b, 0) / VH.kullanici;

// Esigin altinda kalan baglantilar yazmada dagitilir, ustundekiler okumada toplanir.
function is(esik) {
  let alt = 0, ust = 0, enBuyukAlt = 0, ustHesap = 0;
  for (const f of sirali) {
    if (f <= esik) { alt += f; enBuyukAlt = f; } else { ust += f; ustHesap += 1; }
  }
  return { yazma: gonderiTepe * (alt / VH.kullanici), okuma: akisTepe * (ust / VH.kullanici), enBuyukAlt, ustHesap };
}

const b = (x, n = 2) => x.toFixed(n);
const tamYazma = is(VH.kullanici), KAPASITE = 3 * tamYazma.yazma;    // kapasite kararli hizin uc kati kurulur
const ESIK = Math.round(PENCERE * (KAPASITE - tamYazma.yazma));      // tek gonderiye kalan pay penceredeki paydir
console.log(`kararli yazma hizi = ${b(tamYazma.yazma)} kutu yazmasi/s, kapasite ${b(KAPASITE)}/s, ` +
  `tek gonderiye kalan ${b(KAPASITE - tamYazma.yazma)}/s -> ${PENCERE} s penceresinde esik = ${ESIK.toLocaleString("tr-TR")} takipci`);
console.log(`\n${"esik".padStart(9)}${"ustteki hesap".padStart(15)}${"yazma is/s".padStart(13)}${"okuma is/s".padStart(13)}` +
  `${"toplam is/s".padStart(14)}${"en buyuk patlama/s".padStart(20)}`);
for (const e of [1000, 10_000, 84_846, ESIK, VH.kullanici]) {
  const r = is(e);
  console.log(`${e.toLocaleString("tr-TR").padStart(9)}${String(r.ustHesap).padStart(15)}${b(r.yazma).padStart(13)}` +
    `${b(r.okuma).padStart(13)}${b(r.yazma + r.okuma).padStart(14)}${b(r.enBuyukAlt / PENCERE).padStart(20)}`);
}

const secilen = is(ESIK);
console.log(`\nsecilen esikte: ustteki ${secilen.ustHesap} hesap okuma aninda toplaniyor, ` +
  `akis istegi basina ${b(secilen.okuma / akisTepe, 3)} ek kaynak, toplam is ${b(secilen.yazma + secilen.okuma)}/s`);
console.log(`esiksiz tasarimda en buyuk tek gonderi ${b(sirali[VH.kullanici - 1] / PENCERE)} kutu yazmasi/s istiyor = ` +
  `kararli hizin ${b(sirali[VH.kullanici - 1] / PENCERE / tamYazma.yazma)} kati`);

// Dagitim tuketicisi yavaslarsa: birikim / kapasite, G2 penceresini ne zaman asar.
console.log(`\n${"tuketici kapasitesi".padEnd(21)}${"birikim/s".padStart(11)}${"G2'yi asma ani".padStart(16)}`);
for (const k of [1200, 1000, 700]) {
  const birikim = secilen.yazma - k;
  console.log(`${`${k} kutu yazmasi/s`.padEnd(21)}${b(birikim).padStart(11)}${`${b(PENCERE * k / birikim)} s`.padStart(16)}`);
}
console.log(`ortalama takipci ${b(ortalama)}; esigin ustundeki baglanti payi ${b(secilen.okuma / akisTepe / ortalama, 4)}`);
```

```
kararli yazma hizi = 1414.09 kutu yazmasi/s, kapasite 4242.28/s, tek gonderiye kalan 2828.19/s -> 60 s penceresinde esik = 169.691 takipci

     esik  ustteki hesap   yazma is/s   okuma is/s   toplam is/s  en buyuk patlama/s
    1.000          19203       700.05     28561.87      29261.92               16.67
   10.000           1308      1000.84     16530.11      17530.95              166.52
   84.846            115      1193.70      8815.60      10009.30             1393.05
  169.691             52      1247.71      6655.38       7903.09             2820.88
1.000.000              0      1414.09         0.00       1414.09            16666.65

secilen esikte: ustteki 52 hesap okuma aninda toplaniyor, akis istegi basina 23.959 ek kaynak, toplam is 7903.09/s
esiksiz tasarimda en buyuk tek gonderi 16666.65 kutu yazmasi/s istiyor = kararli hizin 11.79 kati

tuketici kapasitesi    birikim/s  G2'yi asma ani
1200 kutu yazmasi/s        47.71       1509.13 s
1000 kutu yazmasi/s       247.71        242.22 s
700 kutu yazmasi/s        547.71         76.68 s
ortalama takipci 203.63; esigin ustundeki baglanti payi 0.1177
```

Tablonun son satırı ilk bakışta tartışmayı bitiriyor: eşiksiz yazmada dağıtma toplam işi 1414,09/s
ile en aza indiriyor ve her eşik bu sayıyı büyütüyor. Eşiği 1000'e çekmek toplam işi 29.261,92'ye,
yirmi katına yakın çıkarıyor. Nedeni değişmiyor — eşiğin üstüne taşınan her bağlantı, kırk kat daha sık
koşan tarafta yeniden sayılıyor.

Eşiği koyan şey toplam iş değil, **son sütundur**. Eşiksiz tasarımda en büyük hesabın tek bir
gönderisi, 60 saniyelik G2 penceresi içinde saniyede 16.666,65 kutu yazması istiyor: kararlı hızın
**11,79 katı**. Bu talep ortalamayla yapılan hesapta görünmez, çünkü ortalama 203,63'tür ve tek bir
kaydın büyüklüğü hakkında hiçbir şey söylemez.

Eşik buradan **hesaplanır**. Dağıtım kapasitesi kararlı hızın üç katına kurulursa (4242,28/s) tek
bir gönderiye 2828,19 yazma/s kalır; 60 saniyelik pencerede bu **169.691 takipçi** eder. Eşik budur
ve iki girdisi vardır: pencere ve kapasite. Seçilen eşikte 52 hesap okuma tarafına geçiyor, akış
isteği başına 23,959 ek kaynak okunuyor ve toplam iş 7903,09/s oluyor — eşiksiz tasarımın 5,59 katı.
Bu, tek bir gönderinin bütün dağıtım kapasitesini kaçırmasını önlemek için ödenen bedeldir.

Eşiğin üstündeki 52 hesap bağlantıların yalnızca 0,1177'sini tutuyor. Karma modelin çalışmasının
nedeni budur: kuyruğun ucundaki elli iki hesap bağlantıların yalnızca bu payını taşıyor ama
patlamanın tamamını üretiyor.

#### Tasarım

- **Somutlaştırılmış görünüm** (Veri Katmanı Ölçekleme, Somutlaştırılmış Görünümler). Akış kutusu bir
  izdüşümdür; parametre kutu başına **400 girdi** ve girdi başına **32 bayttır**, toplam 12,80 GB.
- **Komut ve sorgu sorumluluğu ayrımı** (Veri Katmanı Ölçekleme, Komut ve Sorgu Ayrımı). Yazma yolu
  gönderi deposuna, okuma yolu kutuya bakar; parametre, kutunun gönderi metnini değil kimliğini
  tutmasıdır.
- **Mesaj kuyruğu ve rekabet eden tüketiciler** (Uygulama Katmanı, Mesaj Kuyrukları; Rekabet Eden
  Tüketiciler). Dağıtım işleri kuyruğa düşer; parametre kapasitedir: kararlı 1414,09/s'nin üç katı,
  4242,28 kutu yazması/s.
- **Parçalama** (Veri Dağıtımı, Parçalama). Kutu deposu kullanıcı kimliğine göre parçalanır;
  parametre, akış isteğinin **tek düğüme** dokunmasıdır — dağıt–topla yalnız eşiğin üstündeki 52
  hesap için oluşur.
- **Geri basınç** (Dayanıklılık ve Güvenilirlik, Geri Basınç). Kuyruk birikince yayımlama yoluna
  sinyal gider; parametre, G2'nin 60 saniyelik penceresidir.

**Bilerek kullanılmayan iki kalıp.** Kenar önbelleği (Trafik Katmanı, İçerik Dağıtım Ağları)
kullanılmıyor: akış kişiye özeldir, iki kullanıcı aynı yanıtı almaz ve paylaşılan önbellek isabet
üretmez. Sıralı konvoy (Uygulama Katmanı, Sıralı Konvoy) kullanılmıyor: dağıtım işlerinin sırası
önemsizdir, çünkü akış kutuda zaman damgasına göre sıralanır.

#### Elenen Alternatif: Saf Okumada Toplama

Alternatif tasarım kutuyu hiç kurmaz; akış istendiğinde takip edilen hesapların son gönderileri
birleştirilir. Kazandığı iki şey ölçüldü: kutu deposu 12,80 GB'den sıfıra iner ve G2 penceresi
ortadan kalkar — gönderi yayımlandığı anda görünür, çünkü görünmesi için taşınması gerekmez. Takip
bırakma da anında yansır.

Eleme sayısı iş miktarındadır: tepede 56.570,71 iş/s, yazmada dağıtmanın 33,44 katı. Dahası bu sayı
okuma hacmine bağlıdır; VH3 ikiye katlandığında 113.127,53'e çıkarken yazmada dağıtma 1414,09'da
kalır. Alternatif hangi kısıt değişirse kazanır: okuma/yazma oranı 40'tan 1'e yaklaşırsa iki modelin
işi eşitlenir ve kutu tutmanın gerekçesi kalmaz. Bu, gönderi başına takipçi sayısının değil, **akış
isteğinin gönderiye oranının** kararı belirlediği anlamına gelir.

#### Arıza Davranışı ve Feda Edilen

**Dağıtım tüketicisi yavaşlarsa** (Dayanıklılık ve Güvenilirlik, Arıza Kipleri) kuyruk birikir ve
görünme gecikmesi büyür. Tablo aşılma anını veriyor: kapasite 1200 kutu yazması/s'ye düşerse birikim
saniyede 47,71 olur ve G2'nin 60 saniyelik penceresi **1509,13 saniyede** aşılır; 1000'e düşerse
242,22 saniyede, 700'e düşerse 76,68 saniyede. Kısmi yavaşlama akışı durdurmaz, **geciktirir**; bu
yüzden arıza belirtisi hata değil, tazeliğin kaymasıdır.

**Kutu deposunun bir parçası düşerse** o parçadaki kullanıcıların akışı boş döner. Zarif bozulma
(Dayanıklılık ve Güvenilirlik, Zarif Bozulma) burada okumada toplamaya geçmektir: eşiğin üstündeki
52 hesap için zaten kurulmuş olan yol, düşen parçadaki kullanıcılar için geçici olarak bütün takip
edilenlere açılır — pahalıdır ama boş akıştan iyidir.

**Feda edilen yazma amplifikasyonudur.** Her gönderi ortalama 203,63 kutuya yazılıyor ve bu iş,
okuma hiç gelmese bile yapılıyor. Tasarım okumayı ucuzlatmak için yazmayı iki yüz katına yakın çoğalttı;
kararın tamamı okuma/yazma oranının 40 olmasına dayanıyor.

#### Özet

- Aynı akış yazmada dağıtmayla tepede 1691,87, okumada toplamayla 56.570,71 iş/s istiyor: 33,44 kat.
  Fark okuma/yazma oranının kendisidir.
- Yazmada dağıtma okuma hacminden bağımsız: akış isteği ikiye katlandığında okumada toplama
  113.127,53'e çıkarken yazmada dağıtma 1414,09'da kalıyor.
- Takipçi dağılımının ortalaması 203,63 ama ortancası 60, p99,9 değeri 12.474; en üstteki binde
  birlik dilim bağlantıların 0,2753'ünü tutuyor.
- Eşiği toplam iş koymuyor — eşiksiz tasarım 1414,09/s ile en ucuzu. Eşiği koyan, en büyük hesabın
  tek gönderisinin 60 saniyelik pencerede istediği 16.666,65 yazma/s, yani kararlı hızın 11,79 katı.
- Eşik pencere ile kapasiteden hesaplanıyor: kapasite kararlı hızın üç katıysa eşik 169.691 takipçi,
  52 hesap okuma tarafına geçiyor ve toplam iş 7903,09/s oluyor.
- Tüketici kapasitesi 1200/1000/700'e düştüğünde G2 penceresi 1509,13 / 242,22 / 76,68 saniyede
  aşılıyor; feda edilen, gönderi başına 203,63 kutu yazmasıdır.

#### Sonraki Adım

Bu vakada okumanın maliyeti derlemeydi ve derleme yazma anına kaydırılarak ucuzlatıldı. Kaydırmayı
mümkün kılan bir özellik vardı: yanıtın **kimin için** hazırlanacağı yazma anında biliniyordu, çünkü
takipçi listesi yazıldığı anda bellidir. Bir sonraki vakada bu özellik kaybolur. Kullanıcı bir metin
yazmaya başlar ve her tuş vuruşunda bir öneri listesi ister; sorgunun ne olacağı önceden bilinmez ve
olası sorgu sayısı bir kullanıcı listesi gibi sonlu değildir. Yanıt yazma anında hazırlanamıyorsa
okuma anında hazırlanmak zorundadır — ve bu kez okuma eşiği 200 milisaniye değil, tuş vuruşları
arasına sığacak kadar küçüktür. Soru, hangi veri yapısının bu eşiği tutabildiğine ve tazeliğin ne
kadarının feda edileceğine döner.

### Arama Önerisi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/arama-onerisi

Yanıtın yazma anında hazırlanamadığı bir okuma vakası: önek ağacının bellek ve arama maliyetinin düz listeyle karşılaştırılması, ağacın derinliğinin bellek bütçesinden bir sayı olarak seçilmesi, güncelleme gecikmesinin ilk on öneride ürettiği değişimin ölçülmesi ve donan bir yapının bayatlıkla bozulması.

Önceki vakada yanıt yazma anında hazırlanabiliyordu, çünkü **kimin için** hazırlanacağı yazıldığı
anda belliydi. Burada o özellik yok: kullanıcı her tuş vuruşunda bir **arama önerisi** ister ve ne
yazacağı önceden bilinmez. Yanıt yazma anında hazırlanamıyorsa okuma anında hazırlanmak zorundadır
ve bu kez okuma eşiği tuş vuruşları arasına sığacak kadar küçüktür. Bu ders hangi yapının o eşiği
tutabildiğini ve güncelleme gecikmesinin sonuçlara ne yaptığını ölçer.

#### Kısıtlar

**İşlevsel gereksinimler.** I1: verilen önek için en çok 10 öneri döner. I2: öneriler sorgu
sıklığına göre sıralanır. I3: yeni sorgular öneri havuzuna girer. I4: engellenen sorgu önerilmez.

**Kapsam daraltması.** Yazım düzeltme, kişiselleştirme, anlamsal eşleştirme ve çok dillilik
dışarıdadır. Öneri yalnız ilk **10 karaktere** kadar olan önek için verilir; daha uzun önekte
istemci elindeki listeyi süzer. Bu daraltma tasarımın en pahalı kaleminde karşılığını bulacak.

| Kod | Eşik | Eşiğin kaynağı |
|---|---|---|
| G1 | öneri yanıtının ortancası 20 ms'yi geçmez | iki tuş vuruşu arasındaki aralık |
| G2 | öneri yapısı 8 GB'yi geçmez | yapının bölünmeden tek düğümün belleğinde tutulması |
| G3 | yeni sorgunun öneri listesine girme gecikmesi 600 saniyeyi geçmez | yükselen sorgunun aynı gün yakalanması |

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| VA1 | günlük arama | 20.000.000 | tamamlanan arama sayısı |
| VA2 | arama başına öneri isteği | 12 | yazılan her karakter bir istek üretir |
| VA3 | tepe çarpanı | 3 | tepe saatteki hızın gün ortalamasına oranı |
| VA4 | farklı sorgu sayısı | 30.000.000 | uzun kuyruk, çoğu sorgu birkaç kez sorulur |
| VA5 | günde eklenen yeni sorgu | 300.000 | havuzun yüzde biri her gün yenilenir |
| VA6 | sorgu sıklık dağılımı | Zipf, üs 1 | sıra r'inci sorgunun sıklığı 1/r ile orantılı |
| VA7 | öneri listesi uzunluğu | 10 | I1'in listesi |

#### Kabaca Büyüklük Hesabı

```js
// oneri/hesap.mjs — VA1–VA7'den cikan kabaca buyukluk hesabi ve esiklerin sayiya cevrilmesi
const VA = { gunlukArama: 20e6, aramaBasinaOneri: 12, tepe: 3, farkliSorgu: 30e6, gunlukYeniSorgu: 300_000, listeUzunlugu: 10 };
const GUN = 86_400, BELLEK = 8e9, PENCERE = 600;      // BELLEK: G2 esigi, PENCERE: G3 esigi
const b = (x, n = 2) => x.toFixed(n);

const oneriTepe = (VA.gunlukArama * VA.aramaBasinaOneri / GUN) * VA.tepe;
const aramaTepe = (VA.gunlukArama / GUN) * VA.tepe;
console.log(`tepe oneri istegi/s = ${b(oneriTepe)}   tepe arama/s = ${b(aramaTepe)}   oran = ${b(oneriTepe / aramaTepe)}`);
console.log(`G2'nin sorgu basina bayt butcesi = ${b(BELLEK / VA.farkliSorgu)} bayt (${BELLEK / 1e9} GB / ${(VA.farkliSorgu / 1e6)} milyon sorgu)`);
console.log(`G3 penceresinde biriken yeni sorgu = ${b(VA.gunlukYeniSorgu / GUN * PENCERE)} (gunde ${VA.gunlukYeniSorgu.toLocaleString("tr-TR")})`);
console.log(`yeniden kurma isi gunde ${b(GUN / PENCERE)} kez kosar; her kosum ${(VA.farkliSorgu / 1e6)} milyon sorguyu tarar`);

const iki = (VA.gunlukArama * VA.aramaBasinaOneri * 2 / GUN) * VA.tepe;
console.log(`VA2 duyarliligi: arama basina oneri 12 -> 24 ise tepe ${b(iki)} istek/s; bellek butcesi degismiyor, cunku yapi istek sayisina bagli degil`);
```

```
tepe oneri istegi/s = 8333.33   tepe arama/s = 694.44   oran = 12.00
G2'nin sorgu basina bayt butcesi = 266.67 bayt (8 GB / 30 milyon sorgu)
G3 penceresinde biriken yeni sorgu = 2083.33 (gunde 300.000)
yeniden kurma isi gunde 144.00 kez kosar; her kosum 30 milyon sorguyu tarar
VA2 duyarliligi: arama basina oneri 12 -> 24 ise tepe 16666.67 istek/s; bellek butcesi degismiyor, cunku yapi istek sayisina bagli degil
```

Üç sayı tasarımı belirliyor. Tepe öneri isteği saniyede 8333,33 — aramanın on iki katı, çünkü her
karakter bir istek üretiyor. G2'nin 8 GB'lik eşiği sorgu başına **266,67 bayta** dönüşüyor ve veri
yapısı seçimini bir bütçe sorusuna çeviriyor. G3'ün penceresi yeniden kurma işini günde 144 koşuma
bağlıyor, her koşumda 2083,33 yeni sorgu birikiyor.

#### Yapıların Ölçülmesi

**Önek ağacı** (prefix tree), Veri Yapıları kursunun Sözcük Ağaçları dersinde kurulan yapıdır.
Mekaniği burada yeniden anlatılmaz; ölçülen şey bu vakadaki iki parametresidir — **derinlik** ve
**düğüm başına tutulan hazır liste**.

```js
// oneri/yapilar.mjs — onek agaci ile duz listenin bellek ve arama maliyeti, ve guncelleme
// gecikmesinin sonuc tazeligine etkisi. Sorgu kumesi kendi yazilmis uretecle uretilir. MODELDIR.
const TOHUM = 20260730, SORGU = 50_000, SOZCUK = 4000, ORNEK = 2000, K = 10, VA4 = 30e6;
const DUGUM_BAYT = 48, KAYIT_EK = 8;                 // muhasebe: dugum 48 bayt, kayit basina 8 bayt ek
let s = TOHUM % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
const sec = (a) => a[Math.floor(rast() * a.length)];

const HECE = ["ka", "le", "mi", "tu", "ro", "sa", "ne", "bi", "dol", "gar", "ver", "tan", "yi", "us", "ce"];
const sozluk = [];
for (let i = 0; i < SOZCUK; i += 1) {
  let k = "";
  for (let h = 0, n = 2 + Math.floor(rast() * 2); h < n; h += 1) k += sec(HECE);
  sozluk.push(k);
}
const sorgular = [], gorulen = new Set();
while (sorgular.length < SORGU) {
  const n = 1 + Math.floor(rast() * 3);
  let q = sec(sozluk);
  for (let i = 1; i < n; i += 1) q += " " + sec(sozluk);
  if (gorulen.has(q) === false) { gorulen.add(q); sorgular.push(q); }
}
const siklik = new Float64Array(SORGU);              // Zipf: sira r -> 1e6/r
for (let i = 0; i < SORGU; i += 1) siklik[i] = 1e6 / (i + 1);

const kok = new Map(), derinlikte = new Int32Array(64);
for (const q of sorgular) {                          // onek agaci: her dugum bir Map
  let d = kok, k = 0;
  for (const c of q) {
    k += 1;
    let alt = d.get(c);
    if (alt === undefined) { alt = new Map(); d.set(c, alt); derinlikte[Math.min(k, 63)] += 1; }
    d = alt;
  }
}
const b = (x, n = 2) => x.toFixed(n);
const karakter = sorgular.reduce((a, q) => a + q.length, 0);
console.log(`model: ${SORGU.toLocaleString("tr-TR")} farkli sorgu, ortalama uzunluk ${b(karakter / SORGU)} karakter`);
console.log(`\n${"yapi".padEnd(30)}${"dugum".padStart(11)}${"sorgu basina bayt".padStart(19)}${"VA4 sorguda GB".padStart(16)}`);
console.log(`${"duz liste".padEnd(30)}${"-".padStart(11)}${b((karakter + SORGU * KAYIT_EK) / SORGU).padStart(19)}` +
  `${b((karakter + SORGU * KAYIT_EK) / SORGU * VA4 / 1e9).padStart(16)}`);
let birikim = 1;
for (let d = 1; d <= 63; d += 1) {
  birikim += derinlikte[d];
  if ([6, 8, 10].includes(d) === false && d !== 63) continue;
  const bayt = birikim * (DUGUM_BAYT + K * 4);       // dugum + dugumdeki hazir liste
  console.log(`${`onek agaci, derinlik ${d === 63 ? "tam" : d}`.padEnd(30)}${birikim.toLocaleString("tr-TR").padStart(11)}` +
    `${b(bayt / SORGU).padStart(19)}${b(bayt / SORGU * VA4 / 1e9).padStart(16)}`);
}

// Istek sikligi Zipf'e gore agirlikli: siklik 1/r oldugundan sira = SORGU^u dagilimindan cekilir.
const agirlikliSec = () => Math.min(SORGU - 1, Math.floor(Math.pow(SORGU, rast())) - 1);
const sirali = sorgular.map((q, i) => [q, i]).sort((a, c) => (a[0] < c[0] ? -1 : a[0] > c[0] ? 1 : 0));
const ikiliLog = Math.ceil(Math.log2(SORGU));
const agacB = [], listeB = [], aralik = [];
for (let i = 0; i < ORNEK; i += 1) {
  const q = sorgular[agirlikliSec()], onek = q.slice(0, 1 + Math.floor(rast() * Math.min(10, q.length)));
  let alt = 0, ust = sirali.length;                   // ikili arama ile aralik basi
  while (alt < ust) { const o = (alt + ust) >> 1; if (sirali[o][0] < onek) alt = o + 1; else ust = o; }
  let n = 0;
  while (alt + n < sirali.length && sirali[alt + n][0].startsWith(onek)) n += 1;
  agacB.push(onek.length + 1);                        // inis + dugumdeki hazir listenin okunmasi
  listeB.push(ikiliLog + n);
  aralik.push([alt, n]);
}
const yuzde = (a, p) => [...a].sort((x, y) => x - y)[Math.floor(p * a.length)];
const ort = (a) => a.reduce((x, y) => x + y, 0) / a.length;
console.log(`\n${"arama".padEnd(32)}${"ortalama birim".padStart(16)}${"p99".padStart(8)}`);
for (const [ad, a] of [["onek agaci (hazir liste okunur)", agacB], ["duz sirali liste (ikili + tarama)", listeB]])
  console.log(`${ad.padEnd(32)}${b(ort(a)).padStart(16)}${String(yuzde(a, 0.99)).padStart(8)}`);

// Guncelleme gecikmesi: sorgularin bir payi zamanla yukseliyor; eski anlik goruntuyle uretilen
// oneri listesi ile o andaki dogru liste karsilastiriliyor.
const YUKSELEN = 0.02, YARILANMA = 3600;
const yukselen = new Uint8Array(SORGU);
for (let i = 0; i < SORGU; i += 1) yukselen[i] = rast() < YUKSELEN ? 1 : 0;
const enUst = (alt, n, f) => {
  const p = [];
  for (let i = 0; i < n; i += 1) { const j = sirali[alt + i][1]; p.push([f(j), j]); }
  p.sort((x, y) => y[0] - x[0]);
  return p.slice(0, K).map((x) => x[1]);
};
console.log(`\n${"gecikme".padStart(9)}${"yukselme kati".padStart(15)}${"ilk 10'da ortusme".padStart(19)}${"basi degisen onek".padStart(19)}`);
for (const gecikme of [600, 3600, 21_600, 86_400]) {
  const kat = 1 + gecikme / YARILANMA;
  let ortusme = 0, bas = 0;
  for (const [alt, n] of aralik) {
    const eski = enUst(alt, n, (j) => siklik[j]);
    const yeni = enUst(alt, n, (j) => siklik[j] * (yukselen[j] ? kat : 1));
    const kume = new Set(yeni);
    ortusme += eski.filter((x) => kume.has(x)).length / Math.max(1, Math.min(K, n));
    if (eski[0] !== yeni[0]) bas += 1;
  }
  console.log(`${`${gecikme} s`.padStart(9)}${b(kat).padStart(15)}${b(ortusme / aralik.length, 4).padStart(19)}` +
    `${b(bas / aralik.length, 4).padStart(19)}`);
}

const TEPE = 8333.33;                                 // hesap blogundan: tepe oneri istegi/s
console.log(`\ntepede birim/s: onek agaci ${b(ort(agacB) * TEPE)}, duz sirali liste ${b(ort(listeB) * TEPE)} ` +
  `(oran ${b(ort(listeB) / ort(agacB))})`);
```

```
model: 50.000 farkli sorgu, ortalama uzunluk 15.42 karakter

yapi                                dugum  sorgu basina bayt  VA4 sorguda GB
duz liste                               -              23.42            0.70
onek agaci, derinlik 6              4.588               8.07            0.24
onek agaci, derinlik 8             24.762              43.58            1.31
onek agaci, derinlik 10            86.967             153.06            4.59
onek agaci, derinlik tam          357.080             628.46           18.85

arama                             ortalama birim     p99
onek agaci (hazir liste okunur)             6.10      11
duz sirali liste (ikili + tarama)         1047.32    6593

  gecikme  yukselme kati  ilk 10'da ortusme  basi degisen onek
    600 s           1.17             0.9979             0.0000
   3600 s           2.00             0.9898             0.0015
  21600 s           7.00             0.9546             0.0795
  86400 s          25.00             0.8382             0.1640

tepede birim/s: onek agaci 50833.31, duz sirali liste 8727688.18 (oran 171.69)
```

Birinci tablo derinliği bir bütçe kararına çeviriyor. Tam derinlikte önek ağacı sorgu başına 628,46
bayt istiyor, VA4 ölçeğinde **18,85 GB** — G2'nin 8 GB'lik eşiğinin iki katından fazla. Derinlik
10'a kesildiğinde sorgu başına 153,06 bayta, **4,59 GB**'ye iniyor ve eşiğin altına giriyor. Kapsam
daraltmasının karşılığı burada: on karakterden uzun öneke öneri verilmemesi bir kolaylık değil,
ağacın bütçeye sığmasını sağlayan **kısıttır**. Maliyet derinlikle doğrusal büyümüyor — 6'dan 8'e
geçiş baytı beş katına, 8'den 10'a geçiş üç buçuk katına çıkarıyor.

İkinci tablo arama maliyetini veriyor. Birim, ziyaret edilen bir düğüm ya da karşılaştırılan bir
kayıttır; süre değildir. Önek ağacında ortalama 6,10 birim ve p99'da 11 birim harcanıyor: maliyet
sorgu sayısına değil önek uzunluğuna bağlı, bu yüzden en kötü durum da sınırlı. Düz sıralı listede
ikili arama aralığın başını buluyor ama eşleşen kayıtların hepsi taranmak zorunda: ortalama 1047,32
birim, p99'da 6593. Tepe yükte fark 50.833,31'e karşı 8.727.688,18 birim/s, yani **171,69 kat**.

Üçüncü tablo güncelleme gecikmesinin bedelini veriyor. G3'ün 600 saniyelik penceresinde ilk on
önerinin örtüşmesi 0,9979 ve hiçbir öneğin baştaki önerisi değişmiyor. Pencere bir saate çıkınca
örtüşme 0,9898, altı saate çıkınca 0,9546 ve öneklerin 0,0795'inde baş değişiyor; günde bir kez
kurulan bir yapıda örtüşme 0,8382'ye iniyor ve öneklerin **0,1640'ında** en üstteki öneri yanlış
oluyor. Tazelik doğrusal bozulmuyor: ilk on dakika neredeyse bedava, ilk gün pahalı.

#### Tasarım

- **Önek ağacı, derinlik 10** (Veri Yapıları, Sözcük Ağaçları). Parametre derinliktir; 4,59 GB ile
  G2'nin altında kalır.
- **Somutlaştırılmış görünüm** (Veri Katmanı Ölçekleme, Somutlaştırılmış Görünümler). Her düğümde
  o önekin ilk 10 önerisi hazır durur; parametre düğüm başına **10 kayıt × 4 bayttır** ve arama
  maliyetini alt ağaç yürüyüşünden 6,10 birime indiren şey budur.
- **Çoğaltma** (Veri Dağıtımı, Çoğaltma). Her öneri düğümü yapının tam kopyasını bellekte tutar;
  parametre kopya başına 4,59 GB'dir.
- **İtme tabanlı dağıtım** (Trafik Katmanı, İtme ve Çekme Tabanlı Dağıtım). Yapı toplu olarak
  kurulup düğümlere itilir; parametre 600 saniyelik periyot, yani günde 144 koşumdur.
- **Görev kuyruğu ve arka plan işi** (Uygulama Katmanı, Görev Kuyrukları ve Arka Plan İşleri).
  Parametre, yeniden kurma koşumu başına taranan 30 milyon sorgudur.
- **Kenar önbelleği** (Trafik Katmanı, İçerik Dağıtım Ağları). Öneri yanıtı kişiye özel olmadığı
  için kenarda önbelleklenebilir; parametre, ömrün G3 ile aynı **600 saniye** olmasıdır. Önceki
  vakada bu kalıp tam da bu nedenle kullanılamamıştı.

**Bilerek kullanılmayan iki kalıp.** Parçalama (Veri Dağıtımı, Parçalama) kullanılmıyor: yapı
4,59 GB ile tek düğüme sığdığı için parçalamak her isteği dağıt–topla'ya çevirir ve p99'u bozardı.
Yanında okuma önbelleği (Veri Katmanı Ölçekleme, Yanında Okuma) kullanılmıyor: yapının tamamı zaten
bellekte, arkasında ısınacak bir depo yok.

#### Elenen Alternatif: Düz Sıralı Liste

Alternatif tasarım ağaç kurmaz; sorguları sıralı bir dizide tutar, öneki ikili aramayla bulur ve
eşleşen aralığı tarar. Kazandığı şey ölçüldü ve büyüktür: sorgu başına 23,42 bayt, VA4 ölçeğinde
**0,70 GB**, önek ağacının kullandığının altıda birinden az; üstelik derinlik sınırı gerekmediği
için uzun önekler de karşılanır.

Eleme sayısı arama maliyetindedir: ortalama 1047,32 birime karşı 6,10 birim ve tepe yükte
8.727.688,18'e karşı 50.833,31 birim/s. Asıl sorun ortalama değil uçtur — p99'da 6593 birim, çünkü
kısa önekler binlerce kayıtla eşleşiyor ve hepsi sıklığa göre sıralanmak zorunda. G1 tuş vuruşları
arasına sığma eşiğidir ve p99'u bozan bir yapı bu eşiği taşıyamaz. Alternatif hangi kısıt değişirse
kazanır: öneri yalnız uzun öneklerde verilseydi eşleşen aralık küçülür ve iki yapı yaklaşırdı;
kararı belirleyen, en kısa öneğin kaç kayıtla eşleştiğidir.

#### Arıza Davranışı ve Feda Edilen

**Yeniden kurma işi durursa** yapı donar ve istekler yanıtlanmaya devam eder — arıza belirtisi hata
değil, tazeliğin kaymasıdır. Üçüncü tablo bu kaymayı sayıyor: iş bir saat durursa ilk on önerinin
örtüşmesi 0,9898, altı saat durursa 0,9546 ve öneklerin 0,0795'inde baştaki öneri yanlış, bir gün
durursa 0,8382 ve 0,1640. Zarif bozulma (Dayanıklılık ve Güvenilirlik, Zarif Bozulma) burada
kendiliğinden gelir, çünkü okuma yolu kurma yoluna bağlı değildir.

**Bir öneri düğümü düşerse** hiçbir istek kaybolmaz: her düğüm tam kopya tuttuğu için yük ötekilere
dağılır, yalnız kapasite payı azalır. Çoğaltmanın parçalamaya yeğlenmesinin ikinci karşılığı budur.

**Feda edilen derinlik ve tazeliktir.** On karakterden uzun önek hazır liste bulamaz, istemcinin
süzmesine kalır; yeni bir sorgu 600 saniyeye kadar önerilmez. İkisi de bilerek satın alındı:
karşılığında yapı tek düğümün belleğine sığdı ve arama maliyeti önek uzunluğuna indi.

#### Özet

- Tepe öneri isteği saniyede 8333,33, aramanın on iki katı; G2'nin 8 GB'lik eşiği sorgu başına
  266,67 baytlık bir bütçeye dönüşüyor.
- Tam derinlikte önek ağacı sorgu başına 628,46 bayt ve 18,85 GB istiyor — bütçenin dışında.
  Derinlik 10'a kesilince 153,06 bayt ve 4,59 GB oluyor; kapsam daraltması burada karşılığını buluyor.
- Düğümde hazır tutulan ilk 10 öneri, aramayı ortalama 6,10 birime ve p99'da 11 birime indiriyor.
- Düz sıralı liste belleğin altıda birinden azını kullanıyor (0,70 GB) ama ortalama 1047,32, p99'da
  6593 birim harcıyor; tepe yükte fark 171,69 kat ve eleme uçtan geliyor.
- Güncelleme gecikmesi doğrusal bozulmuyor: 600 saniyede örtüşme 0,9979 ve baş değişimi 0,0000;
  bir günde örtüşme 0,8382 ve öneklerin 0,1640'ında en üstteki öneri yanlış.
- Feda edilen, on karakterden uzun öneğin hazır listesi ve 600 saniyelik tazelik penceresidir.

#### Sonraki Adım

Üç vakada da yanıt bir **kayıt** oldu: bir hedef bağlantı, bir gönderi listesi, bir öneri listesi.
Taşınan bayt küçüktü ve pahalı kalem hesap ya da bellekti; ağ hiç kısıt olmadı. Bir sonraki vakada
bu değişir: aynı sistem hem birkaç yüz baytlık kişiye özel yanıtlar hem de her istekte tekrar
gönderilen büyük ve değişmeyen dosyalar taşır. İkisi aynı yoldan geçtiğinde biri ötekinin kuyruğunu
bozar. Soru, iki trafiğin nereden ayrılacağı ve ayrımın kökene ulaşan istek sayısına ve taşınan
bayta ne yaptığıdır.

### İçerik Dağıtım Tasarımı

Source: https://academia.sh/tr/kurslar/vaka-calismalari/icerik-dagitim-tasarimi

Statik ve dinamik trafiğin ayrılması: ayrımın kökene inen istek sayısına ve bayta etkisinin kenar önbelleği modeliyle ölçülmesi, eşiğin gerektirdiği isabet oranının hesaptan türetilmesi, istek isabeti ile bayt isabetinin ayrılması ve sürümlü adın bayatlık penceresini sıfırlaması.

Üç vakada da yanıt bir kayıttı, taşınan bayt küçüktü ve pahalı kalem hesap ya da bellekti. Bu
vakada aynı sistem iki farklı trafik taşıyor: birkaç yüz baytlık **kişiye özel yanıtlar** ve her
istekte tekrar gönderilen büyük, değişmeyen **statik varlıklar**. İkisi aynı yoldan geçtiğinde
birinin özelliği ötekini bozar — kişiye özel yanıt önbelleklenemez ve bütün yol o yüzden
önbelleklenemez sayılır. Bu ders ayrımın kökene inen istek sayısına ve bayta ne yaptığını ölçer.

#### Kısıtlar

**İşlevsel gereksinimler.** I1: uygulama kabuğu ve varlıkları (betik, biçem, görsel) istemciye
ulaşır. I2: kişiye özel veri isteği yanıtlanır. I3: yeni sürüm yayımlandığında istemci eskisini
kullanmaz. I4: yayımlanan bir sürüm geri alınabilir.

**Kapsam daraltması.** Görsellerin yeniden boyutlandırılması ve kodlanması, çevrimdışı çalışma ve
kabuğun sunucuda mı istemcide mi kurulacağı dışarıdadır; üçü de taşınan **bayt sayısını** değiştirir,
trafiğin nereden ayrılacağını değil.

| Kod | Eşik | Eşiğin kaynağı |
|---|---|---|
| G1 | kişiye özel yanıtın ortancası 150 ms'yi geçmez | ekranda veri beklenen süre |
| G2 | kökene saniyede 2000'den fazla istek inmez | kökenin kurulu kapasitesi |
| G3 | yeni sürüm 300 saniye içinde bütün istemcilerde etkin olur | hatalı sürümün geri alınabilmesi |
| G4 | kenar düğümü başına önbellek 8 GB'yi geçmez | düğümün yerel depolama payı |

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| VI1 | günlük oturum | 4.000.000 | uygulamayı açan kullanıcı sayısı |
| VI2 | oturum başına statik varlık isteği | 25 | betik, biçem ve görsellerin toplamı |
| VI3 | oturum başına kişiye özel istek | 8 | ekranı dolduran veri çağrıları |
| VI4 | tepe çarpanı | 3 | tepe saatteki hızın gün ortalamasına oranı |
| VI5 | statik varlık boyutu | taban 20 KB, üs 2, tavan 4 MB | birkaç büyük paket, çok sayıda küçük varlık; ortalaması 40 KB |
| VI6 | kişiye özel yanıt | 6 KB | ekranı dolduran alanlar |
| VI7 | farklı statik varlık | 500.000 | sürümlü adlar ve görseller dahil |
| VI8 | statik istek dağılımı | Zipf, üs 0,9 | az sayıda varlık her oturumda, çoğu seyrek istenir |

#### Kabaca Büyüklük Hesabı

```js
// dagitim/hesap.mjs — VI1–VI8'den cikan hesap ve G2'nin gerektirdigi isabet oraninin turetilmesi
const VI = { oturum: 4e6, statikIstek: 25, dinamikIstek: 8, tepe: 3, varlikBayt: 40_960, yanitBayt: 6144 };
const GUN = 86_400, KOKEN = 2000;                     // KOKEN: G2 esigi, istek/s
const b = (x, n = 2) => x.toFixed(n);
const mbit = (x) => (x * 8) / 1e6;

const statikTepe = (VI.oturum * VI.statikIstek / GUN) * VI.tepe;
const dinamikTepe = (VI.oturum * VI.dinamikIstek / GUN) * VI.tepe;
const statikBayt = statikTepe * VI.varlikBayt, dinamikBayt = dinamikTepe * VI.yanitBayt;
console.log(`tepe statik istek/s = ${b(statikTepe)}   tepe dinamik istek/s = ${b(dinamikTepe)}   toplam = ${b(statikTepe + dinamikTepe)}`);
console.log(`istek orani statik/dinamik = ${b(statikTepe / dinamikTepe)}   bayt orani = ${b(statikBayt / dinamikBayt)}`);
console.log(`tepe cikis: statik ${b(mbit(statikBayt))} Mbit/s, dinamik ${b(mbit(dinamikBayt))} Mbit/s`);
console.log(`G2 esigi ${KOKEN} istek/s; ayrimsiz tasarim ${b(statikTepe + dinamikTepe - KOKEN)} istek/s asiyor`);
console.log(`G2'yi tutmak icin gereken statik istek isabeti = ${b(1 - (KOKEN - dinamikTepe) / statikTepe, 4)}`);
console.log(`dinamik taban tek basina kokene ${b(dinamikTepe)} istek/s = toplamin ${b(dinamikTepe / (statikTepe + dinamikTepe), 4)}'i`);

const iki = (VI.oturum * VI.statikIstek * 2 / GUN) * VI.tepe;
console.log(`VI2 duyarliligi: oturum basina statik istek 25 -> 50 ise tepe statik ${b(iki)} istek/s ve gereken isabet ${b(1 - (KOKEN - dinamikTepe) / iki, 4)}`);
```

```
tepe statik istek/s = 3472.22   tepe dinamik istek/s = 1111.11   toplam = 4583.33
istek orani statik/dinamik = 3.13   bayt orani = 20.83
tepe cikis: statik 1137.78 Mbit/s, dinamik 54.61 Mbit/s
G2 esigi 2000 istek/s; ayrimsiz tasarim 2583.33 istek/s asiyor
G2'yi tutmak icin gereken statik istek isabeti = 0.7440
dinamik taban tek basina kokene 1111.11 istek/s = toplamin 0.2424'i
VI2 duyarliligi: oturum basina statik istek 25 -> 50 ise tepe statik 6944.44 istek/s ve gereken isabet 0.8720
```

Dört sayı tasarımı belirliyor. Birincisi iki oranın **birbirinden farklı** olması: statik trafik
istek olarak dinamiğin 3,13 katı, bayt olarak 20,83 katı. Tek bir "trafik" sayısıyla konuşmak bu
yüzden yanıltıcıdır — hangi kaynağın sıkıştığına göre iki farklı yanıt çıkar. İkincisi ayrımsız
tasarımın G2'yi 2583,33 istek/s ile aşması. Üçüncüsü eşiğin bir isabet oranına dönüşmesi: kökeni
2000 istek/s'nin altında tutmak için statik isabetin en az **0,7440** olması gerekiyor. Dördüncüsü
tabanın kendisi: dinamik akış tek başına kökene 1111,11 istek/s indiriyor ve bu, isabet oranı ne
olursa olsun **inmez**; toplam isteğin 0,2424'ü hiçbir önbellekle azalmaz.

#### Ayrımın Ölçülmesi

```js
// dagitim/ayrim.mjs — statik ve dinamik ayriminin kokene inen istege ve bayta etkisi.
// Kenar onbellegi en son kullanilani tutan bir Map'tir (ekleme sirasi = kullanim sirasi).
// Varlik boyutlari ve istek akisi kendi yazilmis uretecle uretilir, tohum sabittir. MODELDIR.
const TOHUM = 20260730, VARLIK = 500_000, ISTEK = 1_800_000, ISINMA = 600_000;
const TABAN = 20_480, US = 2.0, TAVAN = 4 << 20, ZIPF = 0.9;   // VI5 ve VI8
const STATIK_TEPE = 3472.22, DINAMIK_TEPE = 1111.11, DINAMIK_BAYT = 6144;   // hesap blogundan
let s = TOHUM % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;

const boyut = new Int32Array(VARLIK);
for (let i = 0; i < VARLIK; i += 1) boyut[i] = Math.min(TAVAN, Math.round(TABAN * Math.pow(rast(), -1 / US)));
const toplamBayt = boyut.reduce((a, b) => a + b, 0);
// Zipf(0,9) tersten ornekleme: sira = (1 + u (N^(1-z) - 1))^(1/(1-z))
const ust = Math.pow(VARLIK, 1 - ZIPF);
const cek = () => Math.min(VARLIK - 1, Math.floor(Math.pow(1 + rast() * (ust - 1), 1 / (1 - ZIPF))) - 1);
const istekler = new Int32Array(ISTEK);
for (let i = 0; i < ISTEK; i += 1) istekler[i] = cek();

function kosum(kapasite) {
  const onbellek = new Map();
  let gezgin = onbellek.keys();
  let dolu = 0, isabet = 0, isabetBayt = 0, olcum = 0, olcumBayt = 0;
  for (let i = 0; i < ISTEK; i += 1) {
    const v = istekler[i], bt = boyut[v], sayiliyor = i >= ISINMA;
    if (sayiliyor) { olcum += 1; olcumBayt += bt; }
    if (onbellek.has(v)) {
      onbellek.delete(v); onbellek.set(v, bt);
      if (sayiliyor) { isabet += 1; isabetBayt += bt; }
      continue;
    }
    if (bt > kapasite) continue;                       // sigmayan varlik onbellege alinmaz
    while (dolu + bt > kapasite) {                     // en eski kullanilan cikarilir
      let n = gezgin.next();
      if (n.done) { gezgin = onbellek.keys(); n = gezgin.next(); }
      const eskiB = onbellek.get(n.value);
      if (eskiB !== undefined) { onbellek.delete(n.value); dolu -= eskiB; }
    }
    onbellek.set(v, bt); dolu += bt;
  }
  return { istekIsabet: isabet / olcum, baytIsabet: isabetBayt / olcumBayt, tutulan: onbellek.size };
}

const b = (x, n = 2) => x.toFixed(n);
const mbit = (baytS) => (baytS * 8) / 1e6;
console.log(`model: ${VARLIK.toLocaleString("tr-TR")} varlik, toplam ${b(toplamBayt / 1e9)} GB, ortalama ` +
  `${b(toplamBayt / VARLIK / 1024)} KB, ${ISTEK.toLocaleString("tr-TR")} istek (ilk ${ISINMA.toLocaleString("tr-TR")} isinma)`);
const ayrimsizIstek = STATIK_TEPE + DINAMIK_TEPE;
const ayrimsizBayt = STATIK_TEPE * (toplamBayt / VARLIK) + DINAMIK_TEPE * DINAMIK_BAYT;
console.log(`\nayrim yok (kisiye ozel belge butun yolu onbelleklenemez kiliyor):`);
console.log(`  kokene inen ${b(ayrimsizIstek)} istek/s ve ${b(mbit(ayrimsizBayt))} Mbit/s`);

console.log(`\n${"kenar onbellegi".padStart(16)}${"tutulan varlik".padStart(16)}${"istek isabeti".padStart(15)}` +
  `${"bayt isabeti".padStart(14)}${"kokene istek/s".padStart(16)}${"kokene Mbit/s".padStart(15)}${"istek kati".padStart(12)}${"bayt kati".padStart(11)}`);
for (const gb of [0.5, 2, 8, 20.5]) {
  const r = kosum(gb * 1e9);
  const kokIstek = STATIK_TEPE * (1 - r.istekIsabet) + DINAMIK_TEPE;
  const kokBayt = STATIK_TEPE * (1 - r.baytIsabet) * (toplamBayt / VARLIK) + DINAMIK_TEPE * DINAMIK_BAYT;
  console.log(`${`${gb} GB`.padStart(16)}${r.tutulan.toLocaleString("tr-TR").padStart(16)}${b(r.istekIsabet, 4).padStart(15)}` +
    `${b(r.baytIsabet, 4).padStart(14)}${b(kokIstek).padStart(16)}${b(mbit(kokBayt)).padStart(15)}${b(ayrimsizIstek / kokIstek).padStart(12)}${b(ayrimsizBayt / kokBayt).padStart(11)}`);
}
```

```
model: 500.000 varlik, toplam 20.44 GB, ortalama 39.93 KB, 1.800.000 istek (ilk 600.000 isinma)

ayrim yok (kisiye ozel belge butun yolu onbelleklenemez kiliyor):
  kokene inen 4583.33 istek/s ve 1190.40 Mbit/s

 kenar onbellegi  tutulan varlik  istek isabeti  bayt isabeti  kokene istek/s  kokene Mbit/s  istek kati  bayt kati
          0.5 GB          12.118         0.4562        0.4390         2999.18         691.80        1.53       1.72
            2 GB          49.111         0.6188        0.6059         2434.84         502.20        1.88       2.37
            8 GB         196.218         0.8272        0.8209         1711.28         258.06        2.68       4.61
         20.5 GB         324.829         0.8773        0.8727         1537.00         199.14        2.98       5.98
```

Model varlıkların ortalamasını 39,93 KB buluyor; hesap bloğu VI5'in kuramsal ortalamasını (40 KB)
kullandığı için iki bloğun bayt sayıları ikinci hanede ayrışıyor. Ölçülen değer tavanla kesilmiş
dağılımın gerçek ortalamasıdır.

**Ayrım G2'yi ancak yeterli önbellekle tutuyor.** G4'ün izin verdiği 8 GB'de istek isabeti 0,8272,
gereken 0,7440'ın üstünde; kökene 1711,28 istek/s iniyor ve eşik tutuyor. 2 GB'de isabet 0,6188'e,
köken 2434,84 istek/s'ye çıkıyor ve **G2 kırılıyor**. Önbellek boyutu bu tasarımda bir kolaylık
değil, eşiği tutan parametredir.

**Ayrım isteği ve baytı aynı oranda azaltmıyor.** 8 GB'de istek 2,68 kat, bayt 4,61 kat azalıyor.
Nedeni hesapta duruyordu: dinamik akış isteklerin 0,2424'ünü tutuyor ama baytların
yirmide birinden azını. Önbellek statik tarafı kaldırdıkça geriye **istek olarak ağır, bayt olarak hafif**
bir taban kalıyor. Bir tasarım "kökeni yüzde yetmiş azalttık" cümlesiyle savunulamaz; hangi
sayının azaldığı söylenmelidir.

**İstek isabeti ile bayt isabeti ayrı sayılardır.** Her satırda bayt isabeti istek isabetinden
düşük (0,8272'ye karşı 0,8209): büyük varlıklar önbellekten daha çabuk çıkıyor, çünkü yerlerine
onlarca küçük varlık sığıyor. Fark küçüktür ama işareti sabittir ve önbellek küçüldükçe büyür
(0,5 GB'de 0,4562'ye karşı 0,4390).

#### Tasarım

- **İçerik dağıtım ağı ve kenar önbelleği** (Trafik Katmanı, İçerik Dağıtım Ağları). Parametre
  düğüm başına **8 GB**'dir ve ölçülen istek isabeti 0,8272 ile G2'nin gerektirdiği 0,7440'ın
  üstündedir.
- **Statik içerik barındırma** (Trafik Katmanı, Statik İçerik Barındırma). Parametre **sürümlü
  addır**: içeriği değişen varlığın adı da değişir, bu yüzden ömrü sınırsız verilebilir.
- **API ağ geçidi** (Trafik Katmanı, API Ağ Geçidi). Dinamik yol buradan geçer; parametre, kökene
  inen 1111,11 istek/s'lik tabandır.
- **Ön yüz için backend** (Trafik Katmanı, Ön Yüz İçin Backend). Parametre oturum başına **8**
  kişiye özel istektir; bu sayı düşerse taban da düşer, çünkü taban önbellekle değil **birleştirmeyle**
  azaltılır.
- **İtme tabanlı dağıtım** (Trafik Katmanı, İtme ve Çekme Tabanlı Dağıtım). Yeni sürümün varlıkları
  yayımdan önce kenara itilir; parametre G3'ün **300 saniyelik** penceresidir.
- **Zarif bozulma** (Dayanıklılık ve Güvenilirlik, Zarif Bozulma). Köken düştüğünde kenar elindeki
  varlıkları sunmaya devam eder; parametre, ayakta kalan istek payıdır.

**Bilerek kullanılmayan iki kalıp.** Önden tazeleme (Veri Katmanı Ölçekleme, Önden Tazeleme)
kullanılmıyor: sürümlü adda bir varlık hiç bayatlamaz, tazelenecek bir şey yoktur. Oturum
yapışkanlığı (Trafik Katmanı, Oturum Yapışkanlığı) kullanılmıyor: statik yolda durum yoktur ve
yapışkanlık kenar önbelleğinin paylaşılmasını engelleyerek isabeti düşürürdü.

#### Elenen Alternatif: Tek Yol, Kısa Ömürlü Önbellek

Alternatif tasarım iki trafiği ayırmaz; hepsini aynı yoldan geçirir ve statik varlıklara kısa bir
önbellek ömrü verir. Kazandığı şey gerçektir: tek dağıtım hattı, tek yayım adımı, sürüm karışması
olmaz — belge ve varlıklar hep birlikte tazelenir. Yayımın atomik kalması küçük bir kazanç değildir.

Eleme sayısı kökendedir. Kişiye özel belge önbelleklenemediği için tek yolda önbellekleme yalnız
varlıklara verilebilir ve ömrü kısa tutulmak zorundadır, çünkü adları değişmez; bu da isabet
oranını tablonun 0,5 GB satırına yaklaştırır. Ayrımsız tasarımda köken 4583,33 istek/s ve
1190,40 Mbit/s alır — G2'yi 2583,33 istek/s aşar ve hiçbir önbellek boyutu bunu kurtarmaz, çünkü
sorun boyut değil **önbelleklenebilirliktir**. Alternatif hangi kısıt değişirse kazanır: kökenin
kapasitesi 5000 istek/s olsaydı G2 kendiliğinden tutar ve ayrımın tek gerekçesi bant genişliği
kalırdı.

#### Arıza Davranışı ve Feda Edilen

**Köken düşerse** statik yol ayakta kalır: kenar isteklerin 0,8272'sini elindeki varlıklarla
karşılamaya devam eder ve sürümlü ad sayesinde sunduğu şey bayat değil, doğru sürümdür. Düşen,
dinamik tabanın tamamıdır — isteklerin 0,2424'ü. Uygulama açılır ama verisi gelmez; bu, zarif
bozulmanın bu vakadaki biçimidir.

**Bir kenar düğümü düşerse** yükü öteki düğümler alır ama önbellekleri o varlıklar için soğuktur.
Soğuk önbelleğin alt sınırını tablonun ilk satırı veriyor: 0,4562 isabetle köken 2999,18 istek/s
alır ve **G2 kırılır**. Devralmanın maliyeti burada kapasite değil, **ısınma süresidir**.

**Feda edilen yayımın atomikliğidir.** İki yol iki hızda ilerlediği için G3'ün 300 saniyelik
penceresinde bir istemci yeni belgeyle eski varlığı ya da tersini görebilir. Sürümlü ad bayatlık
penceresini statik tarafta sıfırladı, ama karşılığında **iki tarafın birbirine göre bayatlığı**
diye yeni bir pencere açtı.

#### Özet

- Statik trafik istek olarak dinamiğin 3,13 katı, bayt olarak 20,83 katı; tek bir trafik sayısıyla
  yapılan savunma hangi kaynağın sıkıştığını gizler.
- G2'nin 2000 istek/s eşiği bir isabet oranına dönüşüyor: statik isabet en az 0,7440 olmalı. G4'ün
  izin verdiği 8 GB'de ölçülen isabet 0,8272, 2 GB'de 0,6188 ve eşik kırılıyor.
- Ayrım isteği 2,68, baytı 4,61 kat azaltıyor; fark, dinamik tabanın isteklerin 0,2424'ünü ama
  baytların çok küçük bir payını tutmasından geliyor.
- Bayt isabeti istek isabetinden hep düşük (8 GB'de 0,8209'a karşı 0,8272) ve önbellek küçüldükçe
  fark büyüyor, çünkü büyük varlıklar önce çıkarılıyor.
- Sürümlü ad statik tarafta bayatlık penceresini sıfırlıyor; önden tazeleme bu yüzden gereksiz.
- Feda edilen yayımın atomikliğidir: 300 saniyelik pencerede yeni belge eski varlıkla eşleşebilir.

#### Sonraki Adım

Dört vaka aynı ailedendi. Bağlantı kısaltmada okuma yazmanın 100 katıydı, haber akışında 40,
arama önerisinde 12, içerik dağıtımında dinamik istek bile önbellekli statiğin yanında küçük kaldı.
Bu oran her dört tasarımın da belkemiğiydi: iş okuma tarafından yazma tarafına kaydırıldı (kutuya
dağıtma, hazır öneri listesi, önek ağacı), çünkü yazma seyrekti ve kaydırılan işin bedeli ucuzdu.
Hepsinde ikinci bir ortak nokta vardı: **bayatlık ödenebilir bir bedeldi.** Kenarda 300 saniye eski
bir yönlendirme, 60 saniye görünmeyen bir gönderi, 600 saniye önerilmeyen bir sorgu, iki tarafın
birbirine göre kayması — hiçbiri kabul edilemez sayılmadı, çünkü bir okumanın biraz eski olması
düzeltilebilir bir hatadır.

Yazmanın hâkim olduğu bir sistemde bu kararların ikisi de tutmaz. Yazma seyrek değilse okuma
tarafına iş kaydırmak bir kazanç değil bir çarpandır; ve bir kaydın yanlış olması kabul edilemiyorsa
bayatlık artık ödenebilir bir bedel değil, doğrudan bir arızadır. Sonraki konu bu tersine dönmüş
kısıtlarla açılır: yazmanın okumayı geçtiği, sıranın ve teslimin garanti altına alınması gerektiği
ve bir kaydın iki kez işlenmesinin gerçek bir zarar ürettiği sistemler.

## Yazma ve Tutarlılık Ağırlıklı Sistemler

Yazmanın ve doğruluğun tasarımı belirlediği beş vaka: bağlantı yönetimi, teslim garantileri ve sıra sorunlarıyla sohbet sistemi, çok kanallı dağıtım ve yeniden denemeyle bildirim sistemi, dağıtık sayaç ve pencere algoritmalarıyla hız sınırlayıcı, sınırlı kaynakta çekişme yönetimiyle bilet ve stok sistemi ve etkisizlik, mutabakat ile denetim izine dayanan ödeme akışı.

### Sohbet Sistemi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/sohbet-sistemi

Yazmanın hâkim olduğu ilk vaka: dokuz varsayımdan çıkan bağlantı ve teslim ölçeği, teslim garantisi seçiminin kaybolan ve yinelenen ileti sayısına etkisinin bir saniyelik tepe penceresinde ölçülmesi, sıra ihlalinin ters çiftle sayılması, sıralı konvoyun bekleme turu cinsinden bedeli ve yoklamalı çekme modelinin itme hızının 140,63 katı istek gerektirmesiyle elenmesi.

Okuma Ağırlıklı Sistemler konusu dört vaka çıkardı ve dördünde de okuma yazmayı büyük farkla
geçiyordu. Bu denge bir şeyi ucuzlatır: bir yanıtın birkaç saniye eski olması ödenebilir bir
bedeldi, çünkü aynı kayıt binlerce kez okunuyor ve tazeliği tek bir yazmaya bakıyordu.
Bayatlık penceresi orada bir tasarım aracıydı.

Bu konu dengenin tersini alır. Sohbette bir kayıt bir kez yazılır ve az sayıda kez okunur;
kaydın kendisi bir kullanıcının gönderdiği iletidir. Kaybolması, ikinci kez görünmesi ya da
yanlış sırada görünmesi kabul edilmez. Aynı kalıplar elde duruyor, ama parametreleri başka
yerde duruyor.

#### Kısıtlar ve Kapsam

İşlevsel gereksinimler beş maddedir: bire bir ileti gönderme, en çok 50 üyeli grup iletisi,
varlık bilgisi (çevrimiçi ya da son görülme), teslim ve okundu göstergesi, çevrimdışı alıcı
için bekletme.

Tasarlanmayan işler açıkça yazılır: sesli ve görüntülü aktarım, dosya eki, ileti içinde arama,
uçtan uca şifreleme ve geçmişin dışa aktarımı bu vakanın dışındadır.

İşlevsel olmayan gereksinimler eşikle ve eşiğin kaynağıyla yazılır: kaybolan ileti sıfırdır
(kaynak: gönderene "gönderildi" gösterildikten sonra iletinin var sayılması), aynı iletinin
ikinci kez gösterilmesi sıfırdır (kaynak: alıcının gördüğü listenin gönderilen listeye
eşitliği), bir sohbet içinde sıra ihlali sıfırdır (kaynak: iletilerin birbirine yanıt olması),
bağlı alıcıya teslim gecikmesinin ortancası 200 milisaniyeyi geçmez (kaynak: yazan tarafın
karşıdaki yazma göstergesinden beklediği eşzamanlılık).

#### Varsayımlar ve Ölçek

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| SO1 | günlük etkin kullanıcı | 5.000.000 | uygulamayı günde en az bir kez açan hesap |
| SO2 | kullanıcı başına günlük gönderilen ileti | 40 | birkaç sohbette süren kısa alışveriş |
| SO3 | ileti gövdesi | 220 bayt | metin, gönderen, sohbet kimliği, zaman damgası |
| SO4 | tepe çarpanı | 4 | akşam saatlerinin gün ortalamasına oranı |
| SO5 | eşzamanlı bağlı kullanıcı oranı | 0,25 | bağlantı uygulama açıkken tutulur |
| SO6 | ileti başına alıcı | 2,4 | bire bir ve grup karışımının ortalaması |
| SO7 | çevrimdışı alıcı oranı | 0,35 | teslim anında bağlı olmayan alıcı payı |
| SO8 | saklama süresi | 365 gün | geçmişin bir yıl geriye açılması |
| SO9 | düğüm başına bağlantı | 50.000 | tek düğümün taşıdığı açık bağlantı sayısı |

```js
// sohbet/olcek.mjs — SO varsayim tablosundan cikan kabaca buyukluk hesabi
export const SO = {
  gunlukKullanici: 5_000_000,    // SO1
  kullaniciBasinaIleti: 40,      // SO2
  iletiBayt: 220,                // SO3
  tepeCarpani: 4,                // SO4
  bagliOran: 0.25,               // SO5
  aliciOrt: 2.4,                 // SO6
  cevrimdisiOran: 0.35,          // SO7
  saklamaGun: 365,               // SO8
  dugumBasinaBaglanti: 50_000,   // SO9
};
const GUN = 86_400;

export function hesapla(v) {
  const gunlukIleti = v.gunlukKullanici * v.kullaniciBasinaIleti;
  const gonderimOrt = gunlukIleti / GUN;
  const gonderimTepe = gonderimOrt * v.tepeCarpani;
  const teslimTepe = gonderimTepe * v.aliciOrt;
  const bagli = v.gunlukKullanici * v.bagliOran;
  return {
    "gunluk ileti": gunlukIleti,
    "ortalama gonderim/s": gonderimOrt,
    "tepe gonderim/s": gonderimTepe,
    "tepe teslim/s": teslimTepe,
    "tepe kutu yazma/s": teslimTepe * v.cevrimdisiOran,
    "esz. bagli kullanici": bagli,
    "baglanti dugumu": bagli / v.dugumBasinaBaglanti,
    "tepe cikis Mbit/s": (teslimTepe * v.iletiBayt * 8) / 1e6,
    "gunluk veri artisi GB": (gunlukIleti * v.iletiBayt) / 1e9,
    "saklanan veri TB": (gunlukIleti * v.iletiBayt * v.saklamaGun) / 1e12,
  };
}

const r = hesapla(SO);
const bicim = (x) => (Number.isInteger(x) ? String(x) : x.toFixed(2));
for (const [ad, d] of Object.entries(r)) console.log(`${ad.padEnd(23)}${bicim(d).padStart(13)}`);

const SONUC = ["tepe teslim/s", "baglanti dugumu", "gunluk veri artisi GB"];
console.log(`\n${"iki katina cikan varsayim".padEnd(25)}${SONUC.map((s) => s.split(" ")[0].padStart(11)).join("")}`);
for (const ad of ["kullaniciBasinaIleti", "bagliOran", "aliciOrt"]) {
  const y = hesapla({ ...SO, [ad]: SO[ad] * 2 });
  console.log(`${ad.padEnd(25)}${SONUC.map((s) => `x${(y[s] / r[s]).toFixed(2)}`.padStart(11)).join("")}`);
}
```

```
gunluk ileti               200000000
ortalama gonderim/s          2314.81
tepe gonderim/s              9259.26
tepe teslim/s               22222.22
tepe kutu yazma/s            7777.78
esz. bagli kullanici         1250000
baglanti dugumu                   25
tepe cikis Mbit/s              39.11
gunluk veri artisi GB             44
saklanan veri TB               16.06

iki katina cikan varsayim       tepe   baglanti     gunluk
kullaniciBasinaIleti           x2.00      x1.00      x2.00
bagliOran                      x1.00      x2.00      x1.00
aliciOrt                       x2.00      x1.00      x1.00
```

Bu sayılar **hesap** sınıfındadır. Tasarımı dört tanesi belirliyor. Birincisi, tepe gönderim
9259,26 iken tepe teslim 22.222,22: her yazma ortalama 2,4 teslim doğuruyor ve sistemin
gördüğü yük yazma değil **teslim** hızıdır. İkincisi, eşzamanlı 1.250.000 bağlantı 25 bağlantı
düğümü demektir; bu, durumu bir yerde tutulan 25 birimdir. Üçüncüsü, tepe kutu yazma saniyede
7777,78 kayıt: teslimlerin üçte birinden fazlası bağlı olmayan bir alıcıya gidiyor ve kalıcı
bir kutuya yazılıyor. Dördüncüsü, bant genişliği küçüktür: 39,11 Mbit/s çıkış, 44 GB
günlük artış. Bu vakada darboğaz bayt değil **kayıt sayısı** ve **bağlantı sayısıdır**.

Duyarlılık tablosu iki kaynağı ayırıyor: bağlı oran ikiye katlandığında yalnız düğüm sayısı,
alıcı sayısı ikiye katlandığında yalnız teslim hızı ikiye katlanıyor.

#### Tasarım

**Bağlantı düğümü.** Uzun ömürlü bağlantı, Trafik Katmanı kursunun Oturum Yapışkanlığı dersinde
ele alınan yapışkanlığı bir tercih olmaktan çıkarıp zorunluluk yapar: bağlantı bir düğüme
bağlıdır ve o düğümde kalır. Parametre yapışkanlık anahtarıdır: kullanıcı kimliği değil cihaz
kimliği, çünkü aynı kullanıcının ikinci cihazı ayrı bir bağlantıdır. Cihazdan düğüme eşleme
aynı kursun Dengeleme Algoritmaları dersindeki tutarlı karma ile yapılır; 25 düğümlük halkada
bir düğüm eklendiğinde taşınan bağlantı payı 1/26'dır.

**Yönlendirme defteri.** Gönderenin düğümü, alıcının hangi düğümde bağlı olduğunu bilmek
zorundadır. Bu, Uygulama Katmanı ve Servis Etkileşimi kursunun Durumsuz Servisler dersindeki
durum deposudur; tuttuğu kayıt kullanıcı kimliği, düğüm adı ve son canlılık damgasıdır, yani
varlık bilgisiyle aynı kayıttır.

**Teslim yolu.** Her teslim bir ileti aracısından geçer. Sohbet içi sıra, aynı kursun Sıralı
Konvoy dersindeki şeritle korunur ve **şerit anahtarı sohbet kimliğidir**, gönderen kimliği
değil: sıra kısıtı sohbetin içinde vardır, gönderenin bütün sohbetleri arasında değil.

**Tekillik.** İleti kimliğini istemci üretir ve yeniden gönderimde aynı kimlik gider.
Dayanıklılık ve Güvenilirlik kursunun Etkisiz İşlemler dersindeki tekillik anahtarının
kapsamı burada `(gönderen, istemci ileti kimliği)` ikilisidir; sohbet kimliği kapsama girmez,
çünkü aynı iletinin iki sohbete gitmesi diye bir durum yoktur.

**Çevrimdışı kutu.** Bağlı olmayan alıcının iletileri kalıcı bir kutuya yazılır; Veri Katmanı
Ölçekleme kursunun Bölümleme Stratejileri dersindeki bölüm anahtarı alıcı kimliğidir.

**Bilerek kullanılmayan kalıp.** Kenar önbelleği (Trafik Katmanı, Giriş Noktaları) bu tasarımda
yer almaz: her ileti tek bir alıcı kümesine bir kez gider, paylaşılan bir isabet doğmaz.

#### Teslim Garantisi ve Sıra Ölçülüyor

Ölçüm süreç içi bir modeldir: gerçek soket, ağ ya da aracı yoktur, kanal bir olasılık ve tur
soyut bir adımdır. Üç varsayım daha eklenir. **SO10 — kanal kayıp olasılığı 0,02**, gerekçesi
hücresel bağlantının kısa kopmaları; **SO11 — onay kaybı olasılığı 0,01**, gerekçesi onayın
dönüş yolunun daha kısa olması; **SO12 — yeniden gönderim geri çekilmesi 3 tur**. Model bir
saniyelik tepe penceresini alır: 22.222 teslim, 40 sohbet şeridi.

```js
// sohbet/teslim.mjs — teslim garantisi ve sira bozulmasinin surec ici modeli.
// Gercek ag, soket ya da kuyruk yoktur: kanal bir olasilik, tur soyut bir adimdir.

export function uretec(tohum) {                 // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

const TESLIM = 22222, SOHBET = 40;              // hesap: tepe teslim/s x 1 saniyelik pencere
const KAYIP = 0.02, ONAY_KAYBI = 0.01;          // SO10, SO11
const GERI_CEKILME = 3;                         // SO12: yeniden gonderim geri cekilme turu

export function kos({ deneme, defterli, konvoy, tohum }) {
  const rnd = uretec(tohum);
  const seritSonVaris = new Array(SOHBET).fill(0);
  const seritAkis = Array.from({ length: SOHBET }, () => []);
  const s = { kanalDenemesi: 0, kaybolan: 0, yinelenen: 0, beklemeTuru: 0 };

  for (let i = 0; i < TESLIM; i += 1) {
    const sohbet = i % SOHBET;
    const no = Math.floor(i / SOHBET);                 // seritte iletinin sirasi ve gonderim turu
    let tur = konvoy ? Math.max(no, seritSonVaris[sohbet]) : no;
    s.beklemeTuru += tur - no;                         // seritte onceki iletiyi bekleme
    let ulasan = 0, varis = null;
    for (let d = 0; d < deneme; d += 1) {
      s.kanalDenemesi += 1;
      if (rnd() < KAYIP) { tur += GERI_CEKILME; continue; }   // teslim kanalda dustu
      ulasan += 1;
      if (varis === null) varis = tur;
      if (rnd() < ONAY_KAYBI) { tur += GERI_CEKILME; continue; }  // onay dustu, gonderen yineler
      break;
    }
    if (ulasan === 0) s.kaybolan += 1;
    else {
      if (defterli === false) s.yinelenen += ulasan - 1;
      seritAkis[sohbet].push({ no, varis });
      seritSonVaris[sohbet] = varis;
    }
  }

  s.tersCift = seritAkis.reduce((t, akis) => {
    const gelis = akis.map((o, i) => ({ ...o, i })).sort((a, b) => a.varis - b.varis || a.i - b.i);
    let n = 0;
    for (let a = 0; a < gelis.length; a += 1)
      for (let b = a + 1; b < gelis.length; b += 1) if (gelis[a].no > gelis[b].no) n += 1;
    return t + n;
  }, 0);
  return s;
}

const DUZEN = [
  ["en cok bir kez / serbest", { deneme: 1, defterli: false, konvoy: false }],
  ["en az bir kez / serbest", { deneme: 5, defterli: false, konvoy: false }],
  ["en az bir kez + anahtar", { deneme: 5, defterli: true, konvoy: false }],
  ["ayni + sirali konvoy", { deneme: 5, defterli: true, konvoy: true }],
];

console.log(`model: ${TESLIM} teslim, ${SOHBET} sohbet seridi, kayip ${KAYIP}, onay kaybi ${ONAY_KAYBI}, tohum 20260730`);
console.log(`\n${"duzen".padEnd(24)}${"kanal denemesi".padStart(15)}${"kaybolan".padStart(10)}` +
  `${"yinelenen".padStart(11)}${"ters cift".padStart(11)}${"bekleme turu".padStart(14)}`);
const R = {};
for (const [ad, secim] of DUZEN) {
  const r = (R[ad] = kos({ ...secim, tohum: 20260730 }));
  console.log(`${ad.padEnd(24)}${String(r.kanalDenemesi).padStart(15)}${String(r.kaybolan).padStart(10)}` +
    `${String(r.yinelenen).padStart(11)}${String(r.tersCift).padStart(11)}${String(r.beklemeTuru).padStart(14)}`);
}

const enCok = R["en cok bir kez / serbest"], enAz = R["en az bir kez / serbest"];
const konvoy = R["ayni + sirali konvoy"];
console.log(`\nkayip beklentisi = ${TESLIM} x ${KAYIP} = ${(TESLIM * KAYIP).toFixed(2)}; olculen ${enCok.kaybolan}`);
console.log(`yeniden denemenin bedeli: kanal denemesi x${(enAz.kanalDenemesi / enCok.kanalDenemesi).toFixed(4)}, ` +
  `yinelenen gosterim orani %${((100 * enAz.yinelenen) / TESLIM).toFixed(2)}`);
console.log(`konvoyun bedeli: ileti basina ${(konvoy.beklemeTuru / TESLIM).toFixed(3)} bekleme turu; ` +
  `ters cift ${enAz.tersCift} -> ${konvoy.tersCift}`);
```

```
model: 22222 teslim, 40 sohbet seridi, kayip 0.02, onay kaybi 0.01, tohum 20260730

duzen                    kanal denemesi  kaybolan  yinelenen  ters cift  bekleme turu
en cok bir kez / serbest          22222       408          0          0             0
en az bir kez / serbest           22870         0        229        827             0
en az bir kez + anahtar           22870         0          0        827             0
ayni + sirali konvoy              22870         0          0          0          1414

kayip beklentisi = 22222 x 0.02 = 444.44; olculen 408
yeniden denemenin bedeli: kanal denemesi x1.0292, yinelenen gosterim orani %1.03
konvoyun bedeli: ileti basina 0.064 bekleme turu; ters cift 827 -> 0
```

Bu sayılar **ölçüm** sınıfındadır; tohum 20260730 ile yeniden üretilir.

İlk satır en çok bir kez teslimin ne aldığını gösteriyor: bir saniyelik tepe penceresinde 408
ileti kayboluyor ve karşılığında hiçbir yineleme, hiçbir sıra ihlali doğmuyor. Beklenen kayıp
444,44'tü; ölçülen 408 bu koşumun sayısıdır. Kayıp iletiler geride kalanların sırasını bozmuyor,
çünkü hiçbir şey yeniden gönderilmiyor. En çok bir kez teslimin sıra açısından temiz olması
tesadüf değildir, kaybın kendisidir.

İkinci satır en az bir kez teslimin faturasını veriyor. Kayıp sıfıra iniyor; bedel kanal
denemesinin 1,0292 katına çıkması, 229 iletinin alıcıya ikinci kez ulaşması (teslimlerin yüzde
1,03'ü) ve 827 ters çifttir. Yineleme onayın kaybolmasından gelir: ileti ulaşmıştır, gönderen
bunu bilmez.

Üçüncü satır tekillik anahtarının payını ayırıyor: 229 yinelenen gösterim sıfıra iniyor,
ters çift 827'de kalıyor. Anahtar defteri yinelemeyi çözer, **sırayı çözmez**; ikisi ayrı
sorundur ve ayrı kalıp ister.

Dördüncü satır sıralı konvoyun payıdır: 827 ters çift sıfıra iniyor, karşılığında ileti başına
0,064 bekleme turu ödeniyor, penceredeki toplam 1414 tur. Bekleme kayıptan doğar — bir teslim
düştüğünde arkasındaki iletiler şeritte bekler. Kayıpsız bir turda konvoyun bedeli sıfırdır.

#### Elenen Alternatif: Yoklamalı Çekme

Bağlantı tutmayan bir tasarım kurulabilir: iletiler kutuya yazılır, istemci sabit aralıkla
sorar. Bu tasarım 25 bağlantı düğümünü, yönlendirme defterini ve oturum yapışkanlığını birden
kaldırır. Elenme gerekçesi bir orandır.

```js
// sohbet/alternatif.mjs — itme modeli ile yoklamali cekme modelinin sayiyla karsilastirilmasi
const BAGLI = 1_250_000, TESLIM_TEPE = 22222.22;   // olcek.mjs hesabi: bagli kullanici, tepe teslim/s
const ESIK_MS = 200;                               // teslim gecikmesi esigi (kaynagi: yazma-yaniti beklentisi)

console.log(`${"yoklama araligi".padStart(16)}${"yoklama istek/s".padStart(17)}${"itmeye orani".padStart(14)}` +
  `${"dolu yoklama".padStart(14)}${"ort. gecikme ms".padStart(17)}`);
for (const aralik of [(2 * ESIK_MS) / 1000, 1, 3, 10, 30]) {
  const istek = BAGLI / aralik;
  const dolu = Math.min(1, (TESLIM_TEPE * aralik) / BAGLI);
  console.log(`${`${aralik} s`.padStart(16)}${istek.toFixed(0).padStart(17)}` +
    `${`x${(istek / TESLIM_TEPE).toFixed(2)}`.padStart(14)}${`%${(100 * dolu).toFixed(2)}`.padStart(14)}` +
    `${((aralik * 1000) / 2).toFixed(0).padStart(17)}`);
}

const basabas = BAGLI / TESLIM_TEPE;
console.log(`\nbasabas aralik = bagli / tepe teslim = ${basabas.toFixed(2)} s`);
console.log(`bu aralikta ortalama teslim gecikmesi = ${((basabas * 1000) / 2 / 1000).toFixed(2)} s`);
console.log(`esik ${ESIK_MS} ms icin gereken aralik = ${((2 * ESIK_MS) / 1000).toFixed(1)} s, ` +
  `yani itmenin ${(BAGLI / ((2 * ESIK_MS) / 1000) / TESLIM_TEPE).toFixed(2)} kati istek`);
```

```
 yoklama araligi  yoklama istek/s  itmeye orani  dolu yoklama  ort. gecikme ms
           0.4 s          3125000       x140.63         %0.71              200
             1 s          1250000        x56.25         %1.78              500
             3 s           416667        x18.75         %5.33             1500
            10 s           125000         x5.63        %17.78             5000
            30 s            41667         x1.88        %53.33            15000

basabas aralik = bagli / tepe teslim = 56.25 s
bu aralikta ortalama teslim gecikmesi = 28.13 s
esik 200 ms icin gereken aralik = 0.4 s, yani itmenin 140.63 kati istek
```

Eleme sayısı son satırdır: 200 milisaniyelik ortalama gecikme eşiği 0,4 saniyelik bir aralık
ister, bu da saniyede 3.125.000 istek, yani itme modelindeki teslim hızının 140,63 katıdır.
Üstelik bu isteklerin yüzde 99,29'u boş döner. Yoklama, taşınmayan iş için ödenen bir hızdır.

Alternatifin kazandığı da yazılır. Başabaş aralık 56,25 saniyedir: gecikme eşiği bunun üstüne
çıkarsa yoklama daha az istek üretir ve 25 bağlantı düğümü ile yönlendirme defteri tasarımdan
düşer. Eleme bir teslim gecikmesi eşiği kararıdır; eşik bir dakika olsaydı seçim tersine
dönerdi.

#### Arıza Davranışı ve Feda Edilen

Bir bağlantı düğümü düştüğünde 50.000 bağlantı aynı anda kopar ve hepsi yeniden bağlanmaya
çalışır. Bu, Başarım Karşıt Kalıpları ve İzleme kursunun Yeniden Deneme Fırtınası dersindeki
senaryodur; karşılığı Dayanıklılık ve Güvenilirlik kursunun Yeniden Deneme ve Fırtına Riski
dersindeki sarsıntı ile deneme bütçesidir. Parametre tavandır: kalan 24 düğüme dağılan 50.000
bağlantı düğüm başına 2083 fazladan bağlantı, yani yüzde 4,17 doluluk demektir.

Aracı düştüğünde tasarım Zarif Bozulma dersindeki yolu izler: bağlantı ayakta kalır, varlık
bilgisi ve okundu göstergesi durur, ileti gönderimi çevrimdışı kutuya düşer. Feda edilen tek
cümleyle şudur: sohbet başına tek şerit, sohbet içi sıra uğruna sohbet düzeyinde koşutluğu
ve kayıp turlarında 1414 turluk bir beklemeyi kabul eder.

#### Özet

- Yazma hâkim olduğunda ölçü kayıt sayısıdır: tepe gönderim 9259,26 iken tepe teslim 22.222,22
  ve bant genişliği 39,11 Mbit/s ile küçük kalıyor.
- Eşzamanlı 1.250.000 bağlantı 25 düğüm demektir; bağlı oranı ikiye katlandığında teslim hızı
  değişmez, yalnız düğüm sayısı ikiye katlanır.
- En çok bir kez teslim bir saniyelik tepe penceresinde 408 ileti kaybetti ve sırayı hiç
  bozmadı; en az bir kez teslim kaybı sıfırladı ama 229 yinelenen gösterim ve 827 ters çift
  üretti.
- Tekillik anahtarı yinelemeyi sıfırladı, ters çifti 827'de bıraktı; sıralı konvoy ters çifti
  sıfırladı, ileti başına 0,064 bekleme turu aldı.
- Yoklamalı çekme 200 milisaniyelik eşikte itmenin 140,63 katı istek ister ve bunların yüzde
  99,29'u boş döner; başabaş aralık 56,25 saniyedir.
- Kenar önbelleği bilerek kullanılmadı: her ileti tek bir alıcı kümesine gider ve paylaşılan
  bir isabet doğurmaz.

#### Sonraki Adım

Bu vakada teslim tek bir yol üzerinden yürüdü: alıcı ya bağlıydı ya da kutusuna yazıldı ve
başarısızlık yeniden denemeyle karşılandı. Bir sonraki vaka aynı soruyu birden çok yolun
bulunduğu yerde sorar. Aynı bildirim farklı kanallardan gidebilir, kanalların başarısızlık
oranları birbirinden farklıdır ve birinin başarısızlığı ötekini denemek için gerekçedir. O
zaman sorulacak sayı şudur: kanalların sırayla mı yoksa aynı anda mı denendiği toplam teslim
oranını ve yeniden deneme yükünü ne kadar oynatır.

### Bildirim Sistemi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/bildirim-sistemi

Aynı iletinin birden çok yoldan gidebildiği vaka: kanal başına geçici ve kalıcı hata oranının ayrılması, tek kanalda üç denemenin teslim oranını eşiğin 3,01 puan altında bırakması, tek kanal ile paralel ve kademeli kanal sırasının teslim oranı, dış çağrı, yinelenen teslim ve gecikme cinsinden ölçülmesi ve paralel dağıtımın çağrı çarpanıyla elenmesi.

Önceki vakada teslim tek bir yol üzerinden yürüdü ve başarısızlığa verilen tek yanıt aynı yolu
yeniden denemekti. Bu vakada yol birden fazladır. Aynı bildirim uygulama itmesiyle, kısa
iletiyle ya da e-postayla gidebilir; kanalların başarısızlık oranları birbirinden farklıdır ve
birinin başarısızlığı ötekini denemek için gerekçedir.

Bu, yeniden deneme sorusunu değiştirir. Bir denemenin nereye harcanacağı artık "aynı yere mi,
başka yere mi" sorusudur ve yanıtı iki sayıyı birden oynatır: toplam teslim oranını ve dışarıya
yapılan çağrı sayısını.

#### Kısıtlar ve Kapsam

İşlevsel gereksinimler: bildirim isteğini kabul etmek, alıcının tercihine göre kanal seçmek,
kanal başına biçimlendirmek, teslim durumunu kaydetmek, aynı olayın alıcıya ikinci kez
gitmesini engellemek.

Tasarlanmayan işler: bildirim metninin yazımı ve şablon dili, çeviri, izin ekranı, alıcı
kümelerinin seçilmesi ve kanal sağlayıcılarının kendi iç işleyişi.

İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: toplam teslim oranı 0,99'un
altına inmez (kaynak: bildirimin bir işlemin tek habercisi olması), acil bildirimin ortalama
teslim gecikmesi 5 saniyeyi geçmez (kaynak: alıcının işlemi yaptığı anda haberi beklemesi),
bir olay için alıcıya giden teslim sayısı birdir (kaynak: aynı haberi iki kanaldan almanın
alıcı için hata sayılması).

#### Varsayımlar ve Ölçek

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BI1 | günlük etkin alıcı | 5.000.000 | bildirim alabilir durumdaki hesap |
| BI2 | alıcı başına günlük bildirim | 6 | işlem haberleri ve hatırlatmalar |
| BI3 | tepe çarpanı | 5 | bildirimlerin gün içinde birkaç saate yığılması |
| BI4 | kanal payı | 0,55 / 0,25 / 0,20 | itme, kısa ileti, e-posta tercih dağılımı |
| BI5 | kanal başına geçici hata | 0,05 / 0,02 / 0,01 | anlık ulaşılamama, yeniden deneme düzeltir |
| BI6 | kanal başına kalıcı hata | 0,06 / 0,02 / 0,01 | geçersiz adres, kapalı izin, yeniden deneme düzeltmez |
| BI7 | bildirim kaydı | 300 bayt | olay kimliği, alıcı, kanal, durum, zaman damgası |
| BI8 | deneme bütçesi | 3 | bildirim başına yapılacak en çok dış çağrı |
| BI9 | denemeler arası bekleme | 30 saniye | geçici hatanın geçmesine bırakılan süre |

BI5 ile BI6 ayrımı bu vakanın belirleyici varsayımıdır. Geçici hata aynı kanalda tekrar
denendiğinde düzelir; kalıcı hata düzelmez ve aynı kanalda harcanan her deneme boşa gider.

```js
// bildirim/olcek.mjs — BI varsayim tablosundan cikan kabaca buyukluk hesabi
export const BI = {
  gunlukAlici: 5_000_000,        // BI1
  aliciBasinaBildirim: 6,        // BI2
  tepeCarpani: 5,                // BI3
  pay: { itme: 0.55, "kisa-ileti": 0.25, "e-posta": 0.20 },        // BI4
  gecici: { itme: 0.05, "kisa-ileti": 0.02, "e-posta": 0.01 },     // BI5
  kalici: { itme: 0.06, "kisa-ileti": 0.02, "e-posta": 0.01 },     // BI6
  kayitBayt: 300,                // BI7
  butce: 3,                      // BI8
  bekleme: 30,                   // BI9
};
const GUN = 86_400;

const gunluk = BI.gunlukAlici * BI.aliciBasinaBildirim;
const tepe = (gunluk / GUN) * BI.tepeCarpani;
console.log(`gunluk bildirim        ${gunluk}`);
console.log(`tepe bildirim/s        ${tepe.toFixed(2)}`);
console.log(`gunluk veri artisi GB  ${((gunluk * BI.kayitBayt) / 1e9).toFixed(2)}`);

console.log(`\n${"kanal".padEnd(11)}${"pay".padStart(6)}${"tepe cagri/s".padStart(14)}${"gecici".padStart(8)}` +
  `${"kalici".padStart(8)}${"3 denemeden sonra kalan".padStart(25)}`);
let karmaKalan = 0;
for (const k of Object.keys(BI.pay)) {
  const kalan = BI.kalici[k] + BI.gecici[k] ** BI.butce;
  karmaKalan += BI.pay[k] * kalan;
  console.log(`${k.padEnd(11)}${BI.pay[k].toFixed(2).padStart(6)}${(tepe * BI.pay[k]).toFixed(2).padStart(14)}` +
    `${BI.gecici[k].toFixed(2).padStart(8)}${BI.kalici[k].toFixed(2).padStart(8)}${kalan.toFixed(6).padStart(25)}`);
}
console.log(`\ntek kanalda ${BI.butce} deneme -> karma kalan hata ${karmaKalan.toFixed(6)}, ` +
  `teslim orani ${(1 - karmaKalan).toFixed(4)}`);
console.log(`esik 0.9900 -> tek kanal ${1 - karmaKalan >= 0.99 ? "gecer" : "gecmez"}; ` +
  `acigi ${((0.99 - (1 - karmaKalan)) * 100).toFixed(2)} puan`);
console.log(`ulasilamayan bildirim: tepe ${(tepe * karmaKalan).toFixed(2)}/s, ` +
  `gunde ${Math.round(gunluk * karmaKalan)}`);
console.log(`kademeli en kotu teslim = (${BI.butce} - 1) x ${BI.bekleme} s = ${(BI.butce - 1) * BI.bekleme} s`);
```

```
gunluk bildirim        30000000
tepe bildirim/s        1736.11
gunluk veri artisi GB  9.00

kanal         pay  tepe cagri/s  gecici  kalici  3 denemeden sonra kalan
itme         0.55        954.86    0.05    0.06                 0.060125
kisa-ileti   0.25        434.03    0.02    0.02                 0.020008
e-posta      0.20        347.22    0.01    0.01                 0.010001

tek kanalda 3 deneme -> karma kalan hata 0.040071, teslim orani 0.9599
esik 0.9900 -> tek kanal gecmez; acigi 3.01 puan
ulasilamayan bildirim: tepe 69.57/s, gunde 1202129
kademeli en kotu teslim = (3 - 1) x 30 s = 60 s
```

Bu sayılar **hesap** sınıfındadır. Üçü tasarımı belirliyor. Birincisi, tepe yük kanallara
eşit dağılmıyor: itme kanalı saniyede 954,86 çağrı alırken e-posta 347,22 alıyor, yani en
kalabalık kanal en yüksek kalıcı hata oranına sahip olan kanaldır. İkincisi, aynı kanalda üç
deneme geçici hatayı pratikte siliyor (itmede 0,05 üzeri üç, yani 0,000125) ama kalıcı hataya
dokunmuyor; kalan hata neredeyse tümüyle BI6'dan geliyor. Üçüncüsü, tek kanallı tasarımın
teslim oranı 0,9599 çıkıyor ve 0,99 eşiğinin 3,01 puan altında kalıyor: günde 1.202.129
bildirim hiç ulaşmıyor. Eşik bir kanal sırası kararını zorunlu kılıyor.

#### Tasarım

**Alma ucu ve kuyruk.** Bildirim isteği eşzamansız kabul edilir; Uygulama Katmanı ve Servis
Etkileşimi kursunun Mesaj Kuyrukları ve Rekabet Eden Tüketiciler dersleri buradaki yapıyı
verir. Parametre öncelik sınıflarıdır: aynı kursun Öncelikli Kuyruk dersindeki iki sınıf
kullanılır, acil bildirim ve hatırlatma, çünkü eşiklerden yalnız biri gecikme içerir.

**Kanal başına bölme kalıbı.** Dayanıklılık ve Güvenilirlik kursunun Bölme Kalıbı dersindeki
kaynak havuzu kanal başına ayrılır ve havuz payları tepe çağrı hızına göre verilir: 954,86,
434,03 ve 347,22 çağrı/s. Ayrımın gerekçesi, bir kanalın yavaşlamasının öteki kanalların
çağrılarını bekletmemesidir.

**Kanal başına devre kesici.** Aynı kursun Devre Kesici dersindeki eşik burada bir sayaç
seçimidir: devre kesici yalnız **geçici** hataları sayar. Kalıcı hata alıcıya özgüdür ve
kanalın sağlığı hakkında bilgi taşımaz; kalıcı hatalar sayaca girseydi itme kanalının devresi
yüzde 6'lık bir taban yüzünden sürekli açılırdı.

**Deneme bütçesinin yerleşimi.** Yeniden Deneme ve Fırtına Riski dersindeki deneme bütçesi
3'tür ve bu vakanın kararı bütçenin **nereye** harcandığıdır: aynı kanala mı, sıradaki kanala
mı. Ölçüm bölümü bu kararı sayıya çeviriyor.

**Tekillik.** Dayanıklılık ve Güvenilirlik kursunun Etkisiz İşlemler dersindeki tekillik
anahtarının kapsamı burada `(olay kimliği, alıcı)` ikilisidir; **kanal kapsama girmez**, çünkü
aynı olayın iki kanaldan gitmesi tam olarak engellenmek istenen şeydir.

**Bilerek kullanılmayan kalıp.** Sıralı konvoy (Uygulama Katmanı ve Servis Etkileşimi,
Kuyruklar ve İş Akışları) bu tasarımda yoktur: iki bildirim arasında sıra kısıtı yoktur, her
biri bağımsız bir olayın habercisidir ve şerit koymak koşutluğu bedelsiz yere keserdi.

#### Kanal Sırası Ölçülüyor

Ölçüm süreç içi bir modeldir: gerçek kanal, dış servis ya da ağ yoktur; her deneme bir
olasılık, tur soyut bir adımdır ve bir tur BI9 ile 30 saniyedir. Model bir dakikalık tepe
penceresini alır: 104.167 bildirim. Üç düzen aynı 3 çağrılık bütçeyi farklı yerlere harcar —
**tek kanal** üçünü de tercih edilen kanala, **paralel** üçünü aynı anda üç kanala, **kademeli**
birini tercih edilen kanala ve kalanları sırayla ötekilere.

```js
// bildirim/dagitim.mjs — kanal sirasinin surec ici modeli. Gercek kanal, dis servis ya da ag
// yoktur: her deneme bir olasilik, tur soyut bir adimdir (BI9: tur basina 30 s bekleme).
const KANAL = ["itme", "kisa-ileti", "e-posta"];
const PAY = { itme: 0.55, "kisa-ileti": 0.25, "e-posta": 0.20 };        // BI4
const GECICI = { itme: 0.05, "kisa-ileti": 0.02, "e-posta": 0.01 };     // BI5
const KALICI = { itme: 0.06, "kisa-ileti": 0.02, "e-posta": 0.01 };     // BI6
const BUTCE = 3, BEKLEME = 30;                                          // BI8, BI9
const BILDIRIM = 104_167;                    // hesap: tepe 1736,11 bildirim/s x 60 s penceresi

function uretec(tohum) {                     // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

export function kos({ duzen, tohum, carpan = 1, dusen = null }) {
  const rnd = uretec(tohum);
  const s = { teslim: 0, disCagri: 0, yinelenen: 0, turToplam: 0 };
  for (let i = 0; i < BILDIRIM; i += 1) {
    let u = rnd(), tercih = KANAL[KANAL.length - 1];
    for (const k of KANAL) { if (u < PAY[k]) { tercih = k; break; } u -= PAY[k]; }
    const kalici = {};
    for (const k of KANAL) kalici[k] = k === dusen || rnd() < Math.min(1, KALICI[k] * carpan);
    const sira = duzen === "tek kanal" ? [tercih, tercih, tercih]
      : [tercih, ...KANAL.filter((k) => k !== tercih)];

    let ulasan = 0, ilkTur = null;
    for (let t = 0; t < BUTCE; t += 1) {
      const k = sira[t];
      s.disCagri += 1;
      const basarili = kalici[k] === false && rnd() >= Math.min(1, GECICI[k] * carpan);
      if (basarili) {
        ulasan += 1;
        if (ilkTur === null) ilkTur = duzen === "paralel" ? 0 : t;
        if (duzen !== "paralel") break;
      }
    }
    if (ulasan > 0) { s.teslim += 1; s.turToplam += ilkTur; s.yinelenen += ulasan - 1; }
  }
  s.oran = s.teslim / BILDIRIM;
  return s;
}

const DUZEN = ["tek kanal", "paralel", "kademeli"];
console.log(`model: ${BILDIRIM} bildirim, ${KANAL.length} kanal, deneme butcesi ${BUTCE}, tohum 20260730`);
console.log(`\n${"duzen".padEnd(11)}${"teslim orani".padStart(14)}${"dis cagri".padStart(11)}` +
  `${"cagri/bildirim".padStart(16)}${"yinelenen".padStart(11)}${"ort. teslim s".padStart(15)}`);
const R = {};
for (const d of DUZEN) {
  const r = (R[d] = kos({ duzen: d, tohum: 20260730 }));
  console.log(`${d.padEnd(11)}${r.oran.toFixed(4).padStart(14)}${String(r.disCagri).padStart(11)}` +
    `${(r.disCagri / BILDIRIM).toFixed(3).padStart(16)}${String(r.yinelenen).padStart(11)}` +
    `${((r.turToplam / r.teslim) * BEKLEME).toFixed(2).padStart(15)}`);
}

console.log(`\n${"hata carpani".padEnd(13)}${DUZEN.map((d) => `${d} orani`.padStart(19)).join("")}`);
for (const c of [1, 2, 3]) {
  const satir = DUZEN.map((d) => kos({ duzen: d, tohum: 20260730, carpan: c }).oran.toFixed(4).padStart(19));
  console.log(`${`x${c}`.padEnd(13)}${satir.join("")}`);
}

console.log(`\n${"itme kanali tumden dustu".padEnd(25)}${DUZEN.map((d) => d.padStart(12)).join("")}`);
const dusuk = DUZEN.map((d) => kos({ duzen: d, tohum: 20260730, dusen: "itme" }));
console.log(`${"teslim orani".padEnd(25)}${dusuk.map((r) => r.oran.toFixed(4).padStart(12)).join("")}`);
console.log(`${"dis cagri".padEnd(25)}${dusuk.map((r) => String(r.disCagri).padStart(12)).join("")}`);

const tek = R["tek kanal"], par = R["paralel"], kad = R["kademeli"];
console.log(`\nkademeli - tek kanal teslim orani farki = ${((kad.oran - tek.oran) * 100).toFixed(2)} puan`);
console.log(`paralelin bedeli: dis cagri x${(par.disCagri / kad.disCagri).toFixed(2)}, ` +
  `${par.yinelenen} yinelenen teslim, bildirim basina ${(par.yinelenen / BILDIRIM).toFixed(2)} fazladan`);
console.log(`paralelin kazanci: ort. teslim ${((par.turToplam / par.teslim) * BEKLEME).toFixed(2)} s, ` +
  `kademelide ${((kad.turToplam / kad.teslim) * BEKLEME).toFixed(2)} s`);
```

```
model: 104167 bildirim, 3 kanal, deneme butcesi 3, tohum 20260730

duzen        teslim orani  dis cagri  cagri/bildirim  yinelenen  ort. teslim s
tek kanal          0.9590     116305           1.117          0           1.08
paralel            0.9999     312501           3.000     190861           0.00
kademeli           0.9999     112357           1.079          0           2.35

hata carpani     tek kanal orani      paralel orani     kademeli orani
x1                        0.9590             0.9999             0.9999
x2                        0.9199             0.9993             0.9994
x3                        0.8790             0.9979             0.9979

itme kanali tumden dustu    tek kanal     paralel    kademeli
teslim orani                   0.4443      0.9993      0.9991
dis cagri                      220661      312501      166806

kademeli - tek kanal teslim orani farki = 4.09 puan
paralelin bedeli: dis cagri x2.78, 190861 yinelenen teslim, bildirim basina 1.83 fazladan
paralelin kazanci: ort. teslim 0.00 s, kademelide 2.35 s
```

Bu sayılar **ölçüm** sınıfındadır; tohum 20260730 ile yeniden üretilir.

İlk tablo bütçenin yerleşimini fiyatlandırıyor. Tek kanal 0,9590 teslim oranı veriyor ve
hesapla bulunan 0,9599 ile aynı büyüklükte; kademeli aynı üç çağrılık bütçeyle 0,9999'a
çıkıyor, fark 4,09 puan. Dikkat çekici olan ikinci sütundur: kademeli düzen bunu **daha az**
dış çağrıyla yapıyor, bildirim başına 1,079 çağrı, tek kanalın 1,117 çağrısının altında. Kalıcı
hataya çarpan bir bildirim tek kanallı düzende üç çağrıyı da boşa harcar; kademeli düzende
ikinci çağrı işi bitirir. Yeniden denemenin yeri, sayısından önemlidir.

İkinci tablo hata oranlarının duyarlılığını veriyor. Bütün kanalların hata oranları üç katına
çıktığında tek kanal 0,8790'a düşüyor, kademeli 0,9979'da kalıyor. Kanal çeşitliliğinin
kazancı hata oranı büyüdükçe artıyor: fark 4,09 puandan 11,89 puana çıkıyor.

#### Elenen Alternatif: Paralel Dağıtım

Paralel düzen teslim oranında kademeliyle aynı sonucu veriyor (0,9999) ve gecikmede onu
geçiyor: ortalama teslim 0,00 saniye, kademelide 2,35 saniye. Buna karşın iki sayıyla eleniyor.

Birincisi, dış çağrı sayısı kademelinin 2,78 katıdır ve bildirim başına 3,000 çağrıda sabittir;
kanal başarısızlığı düşse bile azalmaz, çünkü paralel düzen sonucu beklemeden hepsini gönderir.
İkincisi ve belirleyicisi, 190.861 yinelenen teslim üretir: bildirim başına 1,83 fazladan
teslim. Bu doğrudan işlevsel olmayan gereksinimin ihlalidir — alıcı aynı haberi ortalama üç
kanaldan birden alır.

Alternatifin kazandığı da yazılır: paralel düzen 60 saniyelik en kötü teslim penceresini
sıfırlar. Gecikme eşiği 5 saniye yerine 1 saniye olsaydı ve "tek teslim" gereksinimi kalksaydı
seçim tersine dönerdi; iki kanalın aynı anda denendiği bir orta yol da bu eşikte savunulabilir
olurdu.

#### Arıza Davranışı ve Feda Edilen

Arıza senaryosu bir kanalın tümden durmasıdır: itme kanalı bütün alıcılar için başarısız
oluyor. Üçüncü tablo sonucu veriyor. Tek kanallı düzen 0,4443'e düşüyor — bildirimlerin yarıdan
fazlası hiç ulaşmıyor — ve dış çağrısı 220.661'e, yani normalin 1,90 katına çıkıyor, çünkü boşa
giden denemeler bütçeyi sonuna kadar kullanıyor. Kademeli düzen 0,9991'de kalıyor ve dış
çağrısı 166.806'ya çıkıyor. Kanal başına devre kesicinin görevi burada görünür: itme kanalının
devresi açıldığında sıra doğrudan ikinci kanaldan başlar ve arızanın eklediği 54.449 boşa
çağrı (166.806 eksi 112.357) yapılmaz.

Feda edilen tek cümlededir: kademeli sıra, teslim oranını gecikmeyle satın alır — ilk kanalı
kalıcı hataya çarpan alıcı bildirimi 30 saniye, ikincisi de başarısızsa 60 saniye geç alır.

#### Özet

- Kanal hatası ikiye ayrılmadan tasarım yapılamaz: geçici hata üç denemede siliniyor
  (0,05 üzeri üç), kalıcı hata aynı kanalda hiç azalmıyor ve kalan hatanın tamamına yakınını o
  oluşturuyor.
- Tek kanallı düzen 0,9599 hesaplanmış, 0,9590 ölçülmüş teslim oranı veriyor; 0,99 eşiğinin
  3,01 puan altında ve günde 1.202.129 bildirim ulaşmıyor.
- Kademeli sıra aynı üç çağrılık bütçeyle 0,9999 veriyor ve bunu daha az dış çağrıyla yapıyor:
  bildirim başına 1,079 çağrı, tek kanalın 1,117 çağrısına karşı.
- Hata oranları üç katına çıktığında tek kanal 0,8790'a iniyor, kademeli 0,9979'da kalıyor;
  çeşitliliğin kazancı 4,09 puandan 11,89 puana çıkıyor.
- Paralel dağıtım aynı teslim oranını veriyor ama 2,78 kat dış çağrı ve bildirim başına 1,83
  fazladan teslim üretiyor; tek teslim gereksinimini ihlal ettiği için eleniyor.
- İtme kanalı tümden düştüğünde tek kanal 0,4443'e iniyor, kademeli 0,9991'de kalıyor; bedeli
  bildirimin 30 ya da 60 saniye gecikmesidir.

#### Sonraki Adım

Bu iki vakada da sistemin işi bir isteği **karşılamaktı** ve başarısızlık kötü bir sonuçtu.
Sonraki vaka tersini tasarlar: işi isteği **reddetmek** olan bir bileşen. Reddetme kararı
paylaşılan bir sayaca bakarak verilir ve sayaç dağıtıktır; iki soru buradan çıkar. Sayacın
tuttuğu pencere hangi biçimde tanımlanırsa sınırın üstüne çıkılabilir ve eşzamanlı okuyup
yazan düğümler sayaçta ne kadar sapma üretir. İkisi de sayılabilir.

### Hız Sınırlayıcı

Source: https://academia.sh/tr/kurslar/vaka-calismalari/hiz-sinirlayici

İşi isteği reddetmek olan bileşenin tasarlanması: sabit pencere, kaydırmalı dilim ve belirteç kovasının izin verdiği aşımın üç istek deseninde ölçülmesi, pencere sınırındaki iki katına çıkan geçişin sayılması, dağıtık sayacın merkezî, bölünmüş kota ve gecikmeli eşitleme düzenlerinde ürettiği aşım ile yanlış ret sayısı ve eşitleme aralığının eşiği belirlemesi.

Önceki iki vakada sistemin işi bir isteği karşılamaktı; başarısızlık kötü bir sonuçtu. Bu vaka
tersini tasarlar: işi isteği **reddetmek** olan bir bileşeni. Hız sınırlama kararının **yeri**
Trafik Katmanı ve Eşzamansız İşleme kurslarında ele alınmıştı; algoritması orada işlenmedi.
Burada tasarlanan şey **hız sınırlayıcı** bileşenin kendisidir.

Reddetme kararı paylaşılan bir sayaca bakarak verilir ve sayaç dağıtıktır. İki soru buradan
çıkar: sayacın tuttuğu pencere hangi biçimde tanımlanırsa ilan edilen sınırın üstüne
çıkılabilir ve eşzamanlı okuyup yazan düğümler sayaçta ne kadar sapma üretir.

#### Kısıtlar ve Kapsam

İşlevsel gereksinimler: istemci başına isteği saymak, sınırı aşan isteği reddetmek, kalan hakkı
ve bekleme süresini yanıtta bildirmek, sınırı bir kural kümesinden okumak.

Tasarlanmayan işler: kimlik doğrulama, kotanın ücretlendirilmesi, kötüye kullanım tespiti ve
kural yönetim arayüzü.

İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: bir istemcinin herhangi bir 60
saniyelik aralıkta geçirdiği istek sayısı ilan edilen sınırın **1,1 katını geçmez** (kaynak:
sınırın istemciye verilmiş bir söz olması), sınırın altında kalan bir istemci hiç
reddedilmez (kaynak: aynı sözün karşı yönü), sayaç deposu düştüğünde istek reddedilmez
(kaynak: sınırlayıcının kendisinin kesinti kaynağı olmaması).

#### Varsayımlar ve Ölçek

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| HS1 | izlenen istemci | 200.000 | sınır uygulanan ayrı anahtar sayısı |
| HS2 | uçta tepe istek | 40.000/s | sınırlayıcıdan geçen toplam istek hızı |
| HS3 | istemci başına ilan edilen sınır | 600 istek / 60 s | sözleşmede yazılı hak |
| HS4 | sınırlayıcı düğüm sayısı | 8 | uçtaki yatay ölçek |
| HS5 | sayaç eşitleme aralığı | 0,2 saniye | düğümlerin ortak görüntüyü tazeleme sıklığı |
| HS6 | sayaç kaydı | 48 bayt | anahtar, sayı, pencere damgası |
| HS7 | kaydırmalı pencere dilimi | 12 (5 saniyelik) | 60 saniyenin bölünme inceliği |
| HS8 | belirteç kovası kapasitesi | 60 belirteç | ani yığına bırakılan pay |

```js
// hiz/olcek.mjs — HS varsayim tablosundan cikan kabaca buyukluk hesabi
const HS = { istemci: 200_000, tepeIstek: 40_000, sinir: 600, pencereS: 60,
  dugum: 8, esitlemeS: 0.2, kayitBayt: 48, dilim: 12 };  // HS1..HS8

const dugumBasina = HS.tepeIstek / HS.dugum;
console.log(`dugum basina tepe istek/s   ${dugumBasina}`);
console.log(`merkezi sayac kullanilirsa dokunus/s   ${HS.tepeIstek}`);
console.log(`istemci sinirinin saniye karsiligi     ${HS.sinir / HS.pencereS}`);

console.log(`\n${"sayac duzeni".padEnd(18)}${"istemci basina kayit".padStart(21)}${"toplam MB".padStart(11)}`);
for (const [ad, n] of [["sabit pencere", 1], ["belirtec kovasi", 1], ["kaydirmali dilim", HS.dilim]])
  console.log(`${ad.padEnd(18)}${String(n).padStart(21)}` +
    `${((HS.istemci * n * HS.kayitBayt) / 1e6).toFixed(2).padStart(11)}`);

const korTavan = (HS.sinir / HS.pencereS) * HS.esitlemeS * HS.dugum;
console.log(`\nesitleme koru: bir dugumun ${HS.esitlemeS} s icinde gormedigi istek = ` +
  `${dugumBasina * HS.esitlemeS} (tum istemciler)`);
console.log(`tek istemci icin kor tavan = (sinir/s) x esitleme x dugum = ${korTavan} istek`);
console.log(`esik: sinirin 1.1 kati = ${HS.sinir * 1.1}; kor tavanla ${HS.sinir + korTavan} -> ` +
  `${HS.sinir + korTavan <= HS.sinir * 1.1 ? "gecer" : "gecmez"}`);
```

```
dugum basina tepe istek/s   5000
merkezi sayac kullanilirsa dokunus/s   40000
istemci sinirinin saniye karsiligi     10

sayac duzeni       istemci basina kayit  toplam MB
sabit pencere                         1       9.60
belirtec kovasi                       1       9.60
kaydirmali dilim                     12     115.20

esitleme koru: bir dugumun 0.2 s icinde gormedigi istek = 1000 (tum istemciler)
tek istemci icin kor tavan = (sinir/s) x esitleme x dugum = 16 istek
esik: sinirin 1.1 kati = 660; kor tavanla 616 -> gecer
```

Bu sayılar **hesap** sınıfındadır. Üçü tasarımı belirliyor. Birincisi, merkezî bir sayaç
kullanılırsa tepe uçta saniyede 40.000 dokunuş gerekir; bu, sınırlayıcının kendisini
sistemdeki en yoğun yazma kaynağı yapar. İkincisi, kaydırmalı dilim 115,20 MB istiyor, sabit
pencerenin 9,60 MB'ının tam 12 katı; incelik doğrudan belleğe yazılıyor. Üçüncüsü, kör tavan
16 istektir: 0,2 saniyelik eşitleme aralığında sekiz düğüm bir istemci için en çok 16 isteği
birbirinden habersiz geçirebilir, bu da 616 eder ve 660 eşiğinin altında kalır. Eşitleme
aralığı bu hesapla seçilmiştir.

#### Pencerenin Biçimi Aşımı Belirliyor

Ölçüm süreç içi bir modeldir: gerçek saat, ağ ya da depo yoktur; zaman model saatidir ve istek
listesi belirlenimlidir. Üç desen denenir: sınırın iki katı hızda **düzgün** akış, pencere
sınırının iki yanına **yığılan** akış ve sınırın tam üstünde ama aralıklı gelen akış. Ölçülen
şey, kabul edilen isteklerin herhangi bir 60 saniyelik kayan aralıkta ulaştığı en yüksek
sayıdır.

```js
// hiz/pencere.mjs — uc pencere algoritmasinin surec ici modeli. Gercek saat, ag ya da depo
// yoktur: zaman model saatidir (ms) ve istek listesi belirlenimlidir.
const SINIR = 600, PENCERE = 60_000, DILIM = 5_000, SURE = 180_000;   // HS3, HS7
const KOVA = 60, DOLDURMA = SINIR / (PENCERE / 1000);                 // HS8

const desen = {
  "duzgun 2x": () => Array.from({ length: (SURE / 1000) * 20 }, (_, i) => i * 50),
  "sinirda yigin": () => {
    const t = [];
    for (const sinir of [PENCERE, 2 * PENCERE])
      for (const [bas, n] of [[sinir - DILIM, SINIR], [sinir, SINIR]])
        for (let i = 0; i < n; i += 1) t.push(bas + Math.floor((i * DILIM) / n));
    return t.sort((a, b) => a - b);
  },
  "aralikli tam sinir": () => {
    const t = [];
    for (let k = 0; k < SURE / PENCERE; k += 1)
      for (let i = 0; i < SINIR; i += 1) t.push(k * PENCERE + Math.floor((i * DILIM) / SINIR));
    return t;
  },
};

const ALGORITMA = {
  "sabit pencere": () => {
    let p = -1, n = 0;
    return (t) => {
      const k = Math.floor(t / PENCERE);
      if (k !== p) { p = k; n = 0; }
      return n < SINIR && (n += 1, true);
    };
  },
  "kaydirmali dilim": () => {
    const say = new Map();
    return (t) => {
      const d = Math.floor(t / DILIM), ilk = d - PENCERE / DILIM + 1;
      for (const k of say.keys()) if (k < ilk) say.delete(k);
      let toplam = 0;
      for (const v of say.values()) toplam += v;
      if (toplam >= SINIR) return false;
      say.set(d, (say.get(d) ?? 0) + 1);
      return true;
    };
  },
  "belirtec kovasi": () => {
    let belirtec = KOVA, son = 0;
    return (t) => {
      belirtec = Math.min(KOVA, belirtec + ((t - son) / 1000) * DOLDURMA);
      son = t;
      return belirtec >= 1 && (belirtec -= 1, true);
    };
  },
};

const enYogunPencere = (kabul) => {
  let en = 0;
  for (let i = 0, j = 0; i < kabul.length; i += 1) {
    while (kabul[i] - kabul[j] >= PENCERE) j += 1;
    en = Math.max(en, i - j + 1);
  }
  return en;
};

console.log(`model: ${SURE / 1000} s, sinir ${SINIR}/${PENCERE / 1000} s, kova ${KOVA} belirtec, doldurma ${DOLDURMA}/s`);
for (const [dad, uret] of Object.entries(desen)) {
  const istek = uret();
  console.log(`\ndesen "${dad}": ${istek.length} istek`);
  console.log(`${"algoritma".padEnd(18)}${"kabul".padStart(8)}${"red".padStart(8)}` +
    `${"en yogun 60 s".padStart(15)}${"asim".padStart(8)}${"asim orani".padStart(12)}`);
  for (const [aad, kur] of Object.entries(ALGORITMA)) {
    const izin = kur(), kabul = istek.filter((t) => izin(t));
    const en = enYogunPencere(kabul);
    console.log(`${aad.padEnd(18)}${String(kabul.length).padStart(8)}${String(istek.length - kabul.length).padStart(8)}` +
      `${String(en).padStart(15)}${String(en - SINIR).padStart(8)}${`x${(en / SINIR).toFixed(3)}`.padStart(12)}`);
  }
}
```

```
model: 180 s, sinir 600/60 s, kova 60 belirtec, doldurma 10/s

desen "duzgun 2x": 3600 istek
algoritma            kabul     red  en yogun 60 s    asim  asim orani
sabit pencere         1800    1800            600       0      x1.000
kaydirmali dilim      1800    1800            600       0      x1.000
belirtec kovasi       1859    1741            659      59      x1.098

desen "sinirda yigin": 2400 istek
algoritma            kabul     red  en yogun 60 s    asim  asim orani
sabit pencere         1800     600           1200     600      x2.000
kaydirmali dilim      1200    1200            600       0      x1.000
belirtec kovasi        318    2082            159    -441      x0.265

desen "aralikli tam sinir": 1800 istek
algoritma            kabul     red  en yogun 60 s    asim  asim orani
sabit pencere         1800       0            600       0      x1.000
kaydirmali dilim      1800       0            600       0      x1.000
belirtec kovasi        327    1473            109    -491      x0.182
```

Bu sayılar **ölçüm** sınıfındadır ve belirlenimlidir; rastgelelik yoktur.

Birinci desen üç algoritmayı da sınırın içinde tutuyor, tek istisna belirteç kovasıdır: 659,
yani 1,098 kat. Bu 59 isteklik fazlalık, HS8'in seçtiği 60 belirteçlik kova kapasitesine bir
eksiktir. 1,1 katlık eşikle karşılaştırıldığında kapasite 60'ın tavan olduğu görülüyor; 66'ya
çıkarılsaydı eşik aşılırdı.

İkinci desen sabit pencerenin bilinen kusurunu sayıya çeviriyor: pencere sınırının iki yanına
yığılan istekler iki ayrı sayacın içine düşüyor ve kayan 60 saniyelik aralıkta 1200 istek
geçiyor, tam **iki kat**. Sınır 600 diye ilan edilmiş bir sistemde bir istemci 1200 istek
geçirebiliyor. Kaydırmalı dilim aynı desende 600'de kalıyor.

Üçüncü desen belirteç kovasının bedelini gösteriyor. İstemci sınırın tam üstünde, penceresi
başına 600 istek gönderiyor ve hiçbir sınırı aşmıyor; kaydırmalı dilim hepsini geçiriyor,
belirteç kovası 1800 isteğin 1473'ünü **reddediyor**. Kova boşta geçen zamanı biriktirmez;
aralıklı çalışan bir istemci hakkını kullanamaz. İkinci işlevsel olmayan gereksinim bu satırla
ihlal ediliyor, dolayısıyla seçilen algoritma **kaydırmalı dilim**dir.

#### Dağıtık Sayacın Sapması

Seçilen algoritma tek düğümde doğru çalışıyor. Sekiz düğüm aynı istemciyi sayınca soru
değişir. Üç düzen karşılaştırılır: her isteğin ortak sayaca dokunduğu **merkezî** düzen, sınırın
düğüm sayısına bölündüğü **bölünmüş kota** ve düğümlerin yerel sayıp aralıkla eşitlendiği
**gecikmeli eşitleme**.

```js
// hiz/dagitik.mjs — dagitik sayacin surec ici modeli. Gercek dugum, ag ya da depo yoktur:
// dugum atamasi tohumlu bir uretecle, esitleme model saatiyle yapilir.
const SINIR = 600, PENCERE = 60, DUGUM = 8;          // HS3, HS4

function uretec(tohum) {                              // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

export function kos({ duzen, hiz, aralik = 0.2, tohum }) {
  const rnd = uretec(tohum);
  const n = hiz * PENCERE;
  const yerel = new Array(DUGUM).fill(0);
  let global = 0, sonGlobal = 0, sonEsitleme = 0, kabul = 0, red = 0, dokunus = 0;
  for (let i = 0; i < n; i += 1) {
    const t = (i / hiz), d = Math.floor(rnd() * DUGUM);
    if (duzen === "gecikmeli esitleme" && t - sonEsitleme >= aralik) {
      sonGlobal = global; sonEsitleme = t;
      for (let j = 0; j < DUGUM; j += 1) yerel[j] = 0;
    }
    let izin;
    if (duzen === "merkezi") { dokunus += 1; izin = global < SINIR; }
    else if (duzen === "bolunmus kota") izin = yerel[d] < SINIR / DUGUM;
    else izin = sonGlobal + yerel[d] < SINIR;
    if (izin) { kabul += 1; global += 1; yerel[d] += 1; } else red += 1;
  }
  if (duzen === "gecikmeli esitleme") dokunus = DUGUM * Math.floor(PENCERE / aralik);
  return { istek: n, kabul, red, dokunus, asim: Math.max(0, kabul - SINIR) };
}

const DUZEN = ["merkezi", "bolunmus kota", "gecikmeli esitleme"];
for (const hiz of [10, 90]) {
  console.log(`\nistemci hizi ${hiz}/s -> ${hiz * PENCERE} istek, sinir ${SINIR}, ${DUGUM} dugum, tohum 20260730`);
  console.log(`${"duzen".padEnd(20)}${"kabul".padStart(8)}${"red".padStart(7)}${"asim".padStart(7)}` +
    `${"yanlis ret".padStart(12)}${"sayac dokunusu".padStart(16)}`);
  for (const d of DUZEN) {
    const r = kos({ duzen: d, hiz, tohum: 20260730 });
    const yanlis = hiz * PENCERE <= SINIR ? r.red : 0;
    console.log(`${d.padEnd(20)}${String(r.kabul).padStart(8)}${String(r.red).padStart(7)}` +
      `${String(r.asim).padStart(7)}${String(yanlis).padStart(12)}${String(r.dokunus).padStart(16)}`);
  }
}

console.log(`\ngecikmeli esitleme, istemci hizi 90/s (yigin)`);
console.log(`${"esitleme araligi".padEnd(18)}${"kabul".padStart(8)}${"asim".padStart(7)}` +
  `${"asim orani".padStart(12)}${"dokunus/s".padStart(12)}${"esik 660".padStart(10)}`);
for (const a of [0.2, 1, 2, 5]) {
  const r = kos({ duzen: "gecikmeli esitleme", hiz: 90, aralik: a, tohum: 20260730 });
  console.log(`${`${a} s`.padEnd(18)}${String(r.kabul).padStart(8)}${String(r.asim).padStart(7)}` +
    `${`x${(r.kabul / SINIR).toFixed(3)}`.padStart(12)}${(r.dokunus / PENCERE).toFixed(1).padStart(12)}` +
    `${(r.kabul <= SINIR * 1.1 ? "gecer" : "gecmez").padStart(10)}`);
}
```

```
istemci hizi 10/s -> 600 istek, sinir 600, 8 dugum, tohum 20260730
duzen                  kabul    red   asim  yanlis ret  sayac dokunusu
merkezi                  600      0      0           0             600
bolunmus kota            575     25      0          25               0
gecikmeli esitleme       600      0      0           0            2400

istemci hizi 90/s -> 5400 istek, sinir 600, 8 dugum, tohum 20260730
duzen                  kabul    red   asim  yanlis ret  sayac dokunusu
merkezi                  600   4800      0           0            5400
bolunmus kota            600   4800      0           0               0
gecikmeli esitleme       609   4791      9           0            2400

gecikmeli esitleme, istemci hizi 90/s (yigin)
esitleme araligi     kabul   asim  asim orani   dokunus/s  esik 660
0.2 s                  609      9      x1.015        40.0     gecer
1 s                    630     30      x1.050         8.0     gecer
2 s                    720    120      x1.200         4.0    gecmez
5 s                    900    300      x1.500         1.6    gecmez
```

İlk tablo bölünmüş kotayı eliyor. İstemci sınırının tam üstünde, 600 istek gönderiyor ve
hiçbirini aşmıyor; buna karşın 25 isteği reddediliyor, yüzde 4,17. Sebep dağılım
dengesizliğidir: istekler sekiz düğüme eşit dağılmaz ve payını dolduran düğüm, öteki düğümlerde
kullanılmamış kota dururken reddeder. Kotayı bölmek sınırı bölmez, **hakkı** böler.

İkinci tablo merkezî sayacı fiyatlandırıyor. Aşımı sıfırdır, bu doğrudur; bedeli tepe uçta
saniyede 40.000 sayaç dokunuşudur ve tek istemcilik bu modelde bile 5400 dokunuşla gecikmeli
eşitlemenin 2400'ünün iki katından fazladır. Gecikmeli eşitleme 609 kabul ediyor, aşım 9.

Üçüncü tablo parametreyi seçiyor. Eşitleme aralığı büyüdükçe aşım büyüyor: 0,2 saniyede 1,015
kat, 1 saniyede 1,050 kat, 2 saniyede 1,200 kat. Eşik 1,1 kat olduğuna göre 1 saniye geçiyor,
2 saniye geçmiyor. Sayaç dokunuşu ise ters yönde küçülüyor: saniyede 40'tan 4'e. HS5'in 0,2
saniye seçilmesi bu iki eğrinin kesişme noktasıdır ve eşik 1,05'e sıkılaştırılsaydı aralığın
0,2 saniyede kalması zorunlu olurdu.

#### Tasarım, Arıza Davranışı ve Feda Edilen

Karar noktası ağ geçididir: Trafik Katmanı kursunun Ağ Geçidi Yük Boşaltma dersindeki
taşınabilirlik kuralı burada geçerlidir, çünkü istemci kimliği bir üstveri alanıdır ve karar
alan girdisi okumaz. Sayaç, Veri Katmanı Ölçekleme kursunun Depo Türleri dersindeki
anahtar–değer deposunda tutulur; anahtar `(istemci, dilim)` ikilisi, kayıt 48 bayt, süre sonu
60 saniyedir. Bileşenin kendisi Dayanıklılık ve Güvenilirlik kursunun Kısıtlama ve Yük Boşaltma
dersindeki **kısıtlamadır**: bilinen bir hızın üstünü keser, anlık kapasiteye bakmaz.

**Bilerek kullanılmayan kalıp.** Kuyruk tabanlı yük dengeleme (Uygulama Katmanı ve Servis
Etkileşimi) kullanılmaz: aşan isteği kuyruğa alıp sonra işlemek sınırı bir gecikmeye çevirir,
oysa bileşenin sözleşmesi reddetmektir.

Arıza senaryosu sayaç deposunun düşmesidir. Üçüncü eşik gereği sınırlayıcı açık kalır: düğümler
son bilinen ortak görüntüyle ve yalnız kendi yerel sayacıyla karar verir. Bu, bölünmüş kotanın
davranışına yaklaşmaktır ve arıza süresince istemci başına aşım sekiz kata kadar çıkabilir.
Feda edilen tek cümlededir: bu tasarım aşımı sıfırlamaz, onu 1,015 katta tutmayı 40.000 yerine
40 sayaç dokunuşuyla satın alır.

#### Özet

- Sabit pencere, sınırın iki yanına yığılan istekte kayan 60 saniyelik aralıkta 1200 istek
  geçirdi: ilan edilen sınırın tam iki katı.
- Belirteç kovası düzgün akışta 659 geçirdi (1,098 kat, kova kapasitesi kadar), ama sınırın
  tam üstünde aralıklı çalışan istemcinin 1800 isteğinden 1473'ünü reddetti.
- Kaydırmalı dilim üç desende de 600'de kaldı ve hiçbir geçerli isteği reddetmedi; bedeli 12
  kat bellektir, 9,60 MB yerine 115,20 MB.
- Bölünmüş kota, sınırının tam üstündeki bir istemcinin 25 isteğini (yüzde 4,17) dağılım
  dengesizliği yüzünden reddetti; kotayı bölmek hakkı böler.
- Merkezî sayacın aşımı sıfırdır ama tepe uçta saniyede 40.000 dokunuş ister; gecikmeli
  eşitleme 0,2 saniyelik aralıkta 1,015 kat aşımı saniyede 40 dokunuşla veriyor.
- Eşitleme aralığı eşiği belirliyor: 1 saniyede 1,050 kat (geçer), 2 saniyede 1,200 kat
  (geçmez).

#### Sonraki Adım

Bu vakada sayaç bir eşiği koruyordu ve eşik aşıldığında kaybedilen şey bir istekti; istemci
yeniden dener. Sonraki vaka aynı sayma sorununu, sayılan şeyin **geri getirilemez** olduğu
yerde sorar. Sınırlı sayıda koltuk ya da stok kalemi vardır; sayacın iki fazla saymasının
karşılığı reddedilmiş bir istek değil, satılmış ama var olmayan bir kalemdir. O zaman
ölçülecek sayı çekişme altında üretilen fazla satıştır ve yanında ikinci bir sayı durur:
aynı korumanın reddettiği geçerli istek.

### Bilet ve Stok Sistemi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/bilet-ve-stok-sistemi

Sayılan şeyin geri getirilemez olduğu vaka: kontenjanın tepe hızda 0,179 saniyede tükenmesi, korumasız oku–karar ver–yaz akışının 16 eşzamanlı işçi altında kontenjanın 5,59 katını satması, iyimser ve kötümser kilitlemenin fazla satış ile reddedilen geçerli istek cinsinden ölçülmesi ve rezervasyon penceresinin boşa tutulan kalem-saniyeye etkisi.

Önceki vakada bir sayaç bir eşiği koruyordu ve eşik aşıldığında kaybedilen şey bir istekti;
istemci yeniden dener. Bu vaka aynı sayma sorununu, sayılan şeyin **geri getirilemez** olduğu
yerde sorar. Sınırlı sayıda koltuk vardır ve sayacın iki fazla saymasının karşılığı reddedilmiş
bir istek değil, satılmış ama var olmayan bir kalemdir.

İki fark buradan çıkar. Birincisi, aşımın birimi değişir: hız sınırlayıcıda 1,1 kat kabul
edilebilirdi, burada bir tanenin bile karşılığı yoktur. İkincisi, kaynağı tutmak ile satmak
ayrı işlemlerdir ve aralarında bir pencere vardır.

#### Kısıtlar ve Kapsam

İşlevsel gereksinimler: kalan kontenjanı bildirmek, bir rezervasyon açmak, rezervasyonu satışa
çevirmek, süresi dolan rezervasyonu serbest bırakmak, iptali işlemek.

Tasarlanmayan işler: ödeme yürütümü (sonraki vakanın konusu), koltuk yerleşiminin gösterimi,
fiyatlandırma ve ikincil satış.

İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: **fazla satış sıfırdır** (kaynak:
satılan her kalemin karşılığının bulunması yükümlülüğü), **stok varken reddedilen geçerli istek
sıfırdır** (kaynak: kontenjan bitmeden alıcının eli boş dönmemesi), rezervasyon kararı 100
milisaniyeyi geçmez (kaynak: açılış anında sayfanın yanıtsız kalmaması).

#### Varsayımlar ve Ölçek

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BS1 | kontenjan | 5.000 kalem | tek bir satışa açılan sınırlı kaynak |
| BS2 | açılıştaki istek | 200.000 / 10 saniye | açılış anına yığılan talep |
| BS3 | istek başına adet | 1,4 | tek ve çift alımların ortalaması |
| BS4 | rezervasyon penceresi | 120 saniye | ödeme adımına bırakılan süre |
| BS5 | pencereye göre satışa dönme | 0,45 / 0,65 / 0,72 | 30, 120 ve 300 saniyelik pencerede tamamlanan ödeme payı |
| BS6 | eşzamanlı yazma işçisi | 16 | kontenjan satırına aynı anda dokunan iş sayısı |
| BS7 | stok kaydı | 64 bayt | kalem kimliği, durum, rezervasyon damgası |
| BS8 | iyimser çakışmada deneme bütçesi | 3 | yeniden denemenin üst sınırı |

```js
// bilet/olcek.mjs — BS varsayim tablosundan cikan kabaca buyukluk hesabi
const BS = { kontenjan: 5_000, acilisIstek: 200_000, acilisSaniye: 10, adet: 1.4,
  pencere: 120, donme: { 30: 0.45, 120: 0.65, 300: 0.72 }, isci: 16, kayitBayt: 64 };

const tepe = BS.acilisIstek / BS.acilisSaniye;
const talep = BS.acilisIstek * BS.adet;
const tukenme = BS.kontenjan / (tepe * BS.adet);
console.log(`tepe istek/s                ${tepe}`);
console.log(`acilistaki toplam talep     ${talep} kalem`);
console.log(`talep / kontenjan           ${(talep / BS.kontenjan).toFixed(1)} kat`);
console.log(`kontenjanin tukenme suresi  ${tukenme.toFixed(3)} s (tepe hizda)`);
console.log(`tukenene kadar gelen istek  ${Math.round(tepe * tukenme)}`);
console.log(`stok tablosu                ${((BS.kontenjan * BS.kayitBayt) / 1e3).toFixed(1)} KB`);
console.log(`rezervasyon sayisi          ${(BS.kontenjan / BS.adet).toFixed(0)} (kontenjan / adet)`);

console.log(`\n${"pencere".padStart(9)}${"donme orani".padStart(13)}${"ilk turda satilan".padStart(19)}` +
  `${"geri donen kalem".padStart(18)}${"bosa kalem-saniye".padStart(19)}`);
for (const [p, d] of Object.entries(BS.donme)) {
  const geri = BS.kontenjan * (1 - d);
  console.log(`${`${p} s`.padStart(9)}${d.toFixed(2).padStart(13)}${(BS.kontenjan * d).toFixed(0).padStart(19)}` +
    `${geri.toFixed(0).padStart(18)}${(geri * Number(p)).toFixed(0).padStart(19)}`);
}
const d120 = BS.donme[BS.pencere], d30 = BS.donme[30];
console.log(`\npencere 30 s -> 120 s: ilk turda satilan ${(BS.kontenjan * d30).toFixed(0)} -> ` +
  `${(BS.kontenjan * d120).toFixed(0)} (+${((d120 - d30) * BS.kontenjan).toFixed(0)} kalem)`);
console.log(`ayni degisim bosa kalem-saniyeyi ${(BS.kontenjan * (1 - d30) * 30).toFixed(0)} -> ` +
  `${(BS.kontenjan * (1 - d120) * BS.pencere).toFixed(0)} yapiyor (` +
  `${((BS.kontenjan * (1 - d120) * BS.pencere) / (BS.kontenjan * (1 - d30) * 30)).toFixed(2)} kat)`);
```

```
tepe istek/s                20000
acilistaki toplam talep     280000 kalem
talep / kontenjan           56.0 kat
kontenjanin tukenme suresi  0.179 s (tepe hizda)
tukenene kadar gelen istek  3571
stok tablosu                320.0 KB
rezervasyon sayisi          3571 (kontenjan / adet)

  pencere  donme orani  ilk turda satilan  geri donen kalem  bosa kalem-saniye
     30 s         0.45               2250              2750              82500
    120 s         0.65               3250              1750             210000
    300 s         0.72               3600              1400             420000

pencere 30 s -> 120 s: ilk turda satilan 2250 -> 3250 (+1000 kalem)
ayni degisim bosa kalem-saniyeyi 82500 -> 210000 yapiyor (2.55 kat)
```

Bu sayılar **hesap** sınıfındadır. Üçü tasarımı belirliyor. Birincisi, talep kontenjanın 56
katıdır ve kontenjan tepe hızda **0,179 saniyede** tükenir; tasarımın çözdüğü sorun kapasite
değil, 0,179 saniyelik bir aralıkta doğru sayabilmektir. İkincisi, bu aralıkta yalnız 3571
istek gelir; geri kalan 196.429 isteğin işi reddedilmektir ve bu iş kontenjan satırına hiç
dokunmadan yapılmalıdır. Üçüncüsü, rezervasyon penceresi iki yönlü bir maliyet taşır: 30
saniyeden 120 saniyeye çıkarmak ilk turda satılanı 1000 kalem artırıyor ama boşa tutulan
kalem-saniyeyi 2,55 katına çıkarıyor.

#### Fazla Satış Ölçülüyor

Ölçüm süreç içi bir modeldir: gerçek iş parçacığı, veritabanı ya da kilit yoktur; 16 işçi
tohumlu bir zamanlayıcı tarafından adım adım ilerletilir ve çekişme, bir işçinin okuması ile
yazması arasına başka işçilerin girmesinden doğar. Üç düzen karşılaştırılır: **korumasız**
oku–karar ver–yaz akışı, sürüm sütununa bakan **iyimser** kilitleme ve kararı kilit altında
veren **kötümser** kilitleme. Mekanikleri Veri Erişim Katmanı kursunun İyimser ve Kötümser
Kilitleme dersinde kurulmuştu; burada ölçülen şey bu vakadaki fazla satış sayısıdır.

```js
// bilet/cekisme.mjs — sinirli kaynakta cekismenin surec ici modeli. Gercek is parcacigi,
// veritabani ya da kilit yoktur: 16 isci bir zamanlayici tarafindan adim adim ilerletilir.
const KONTENJAN = 5_000, ISTEK = 20_000, DENEME = 3;   // BS1, BS8

function uretec(tohum) {                     // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

export function kos({ duzen, tohum, ISCI = 16 }) {   // BS6: eszamanli yazma iscisi
  const rnd = uretec(tohum);
  const ortak = { stok: KONTENJAN, surum: 0, kilitli: false };
  const kuyruk = Array.from({ length: ISTEK }, () => (rnd() < 0.6 ? 1 : 2));  // BS3: ortalama 1,4
  const isci = Array.from({ length: ISCI }, () => ({ faz: "bos" }));
  const s = { satilan: 0, red: 0, redGecerli: 0, cakisma: 0, bekleme: 0, adim: 0 };
  let sira = 0;

  while (sira < ISTEK || isci.some((w) => w.faz !== "bos")) {
    const w = isci[Math.floor(rnd() * ISCI)];
    s.adim += 1;
    if (w.faz === "bos") {
      if (sira >= ISTEK) continue;
      w.adet = kuyruk[sira]; sira += 1; w.deneme = 0; w.faz = "oku";
    }
    if (w.faz === "oku") {
      if (duzen === "kotumser") {
        if (ortak.kilitli) { s.bekleme += 1; continue; }
        ortak.kilitli = true;
      }
      w.okunanStok = ortak.stok; w.okunanSurum = ortak.surum; w.faz = "yaz";
      continue;
    }
    if (w.okunanStok < w.adet) {                       // okunan stok yetmiyor
      s.red += 1;
      if (ortak.stok >= w.adet) s.redGecerli += 1;
      if (duzen === "kotumser") ortak.kilitli = false;
      w.faz = "bos"; continue;
    }
    if (duzen === "iyimser" && ortak.surum !== w.okunanSurum) {   // surum degismis
      s.cakisma += 1; w.deneme += 1;
      if (w.deneme >= DENEME) {
        s.red += 1;
        if (ortak.stok >= w.adet) s.redGecerli += 1;
        w.faz = "bos";
      } else w.faz = "oku";
      continue;
    }
    ortak.stok = w.okunanStok - w.adet;
    ortak.surum += 1;
    s.satilan += w.adet;
    if (duzen === "kotumser") ortak.kilitli = false;
    w.faz = "bos";
  }
  s.fazlaSatis = Math.max(0, s.satilan - KONTENJAN);
  s.kalanStok = ortak.stok;
  return s;
}

console.log(`model: kontenjan ${KONTENJAN}, 16 isci, ${ISTEK} istek, deneme butcesi ${DENEME}, tohum 20260730`);
console.log(`\n${"duzen".padEnd(12)}${"satilan".padStart(9)}${"fazla satis".padStart(13)}` +
  `${"kalan stok".padStart(12)}${"cakisma".padStart(9)}${"red (gecerli)".padStart(15)}` +
  `${"bekleme adimi".padStart(15)}${"toplam adim".padStart(13)}`);
const R = {};
for (const d of ["korumasiz", "iyimser", "kotumser"]) {
  const r = (R[d] = kos({ duzen: d, tohum: 20260730 }));
  console.log(`${d.padEnd(12)}${String(r.satilan).padStart(9)}${String(r.fazlaSatis).padStart(13)}` +
    `${String(r.kalanStok).padStart(12)}${String(r.cakisma).padStart(9)}` +
    `${`${r.red} (${r.redGecerli})`.padStart(15)}${String(r.bekleme).padStart(15)}${String(r.adim).padStart(13)}`);
}

const k = R.korumasiz, i = R.iyimser, p = R.kotumser;
console.log(`\nkorumasiz duzen kontenjani ${(k.satilan / KONTENJAN).toFixed(3)} kat satti; ` +
  `fazla satis / kontenjan = %${((100 * k.fazlaSatis) / KONTENJAN).toFixed(2)}`);
console.log(`iyimser: fazla satis ${i.fazlaSatis}, ${i.cakisma} cakisma, ` +
  `${i.redGecerli} gecerli istek deneme butcesi bitince reddedildi`);
console.log(`kotumser: fazla satis ${p.fazlaSatis}, gecerli ret ${p.redGecerli}, ` +
  `adim sayisi iyimserin ${(p.adim / i.adim).toFixed(2)} kati`);

console.log(`\n${"eszamanli isci".padStart(15)}${"iyimser cakisma".padStart(17)}${"gecerli ret".padStart(13)}` +
  `${"kotumser bekleme".padStart(18)}`);
for (const n of [2, 4, 16]) {
  const a = kos({ duzen: "iyimser", tohum: 20260730, ISCI: n });
  const b = kos({ duzen: "kotumser", tohum: 20260730, ISCI: n });
  console.log(`${String(n).padStart(15)}${String(a.cakisma).padStart(17)}${String(a.redGecerli).padStart(13)}` +
    `${String(b.bekleme).padStart(18)}`);
}
```

```
model: kontenjan 5000, 16 isci, 20000 istek, deneme butcesi 3, tohum 20260730

duzen         satilan  fazla satis  kalan stok  cakisma  red (gecerli)  bekleme adimi  toplam adim
korumasiz       27963        22963        1798        0          0 (0)              0        40047
iyimser          5000            0           0    10038   16397 (2409)              0        55267
kotumser         5000            0           0        0      16427 (0)         304276       344446

korumasiz duzen kontenjani 5.593 kat satti; fazla satis / kontenjan = %459.26
iyimser: fazla satis 0, 10038 cakisma, 2409 gecerli istek deneme butcesi bitince reddedildi
kotumser: fazla satis 0, gecerli ret 0, adim sayisi iyimserin 6.23 kati

 eszamanli isci  iyimser cakisma  gecerli ret  kotumser bekleme
              2             1519          130             20043
              4             3577          520             60109
             16            10038         2409            304276
```

Bu sayılar **ölçüm** sınıfındadır; tohum 20260730 ile yeniden üretilir.

Birinci satır korumasız düzenin ölçüsüdür ve iki sayı taşır. Kontenjanın 5,593 katı satıldı,
fazla satış 22.963 kalem. İkinci sayı daha öğreticidir: **kalan stok 1798 görünüyor.** Sayaç
yalnız yanlış değil, yanlış yönde yanlıştır — sistem hâlâ satacak yer olduğunu bildiriyor.
Hiçbir istek reddedilmedi, çünkü hiçbir işçi stoku yetersiz görmedi; kaybedilen yazmalar
sayacı geri yükseltti.

İkinci ve üçüncü satır iki korumanın da fazla satışı sıfırladığını gösteriyor. Ayrım öteki
sütunlardadır. İyimser kilitleme 10.038 çakışma üretiyor ve deneme bütçesi biten **2409 geçerli
isteği** reddediyor: stok dururken eli boş dönen alıcı. Bu, ikinci işlevsel olmayan
gereksinimin ihlalidir. Kötümser kilitleme geçerli ret üretmiyor, karşılığında 304.276 bekleme
adımı ve iyimserin 6,23 katı toplam adım harcıyor.

Dördüncü tablo iki maliyetin de aynı değişkene bağlı olduğunu gösteriyor: eşzamanlı işçi
sayısı. İşçi 2 iken iyimser kilitlemenin geçerli reddi 130'a, kötümserin bekleme adımı
20.043'e iniyor. Çekişme bir sabit değil, bir tasarım parametresidir.

#### Tasarım ve Elenen Alternatif

Kontenjan satırı **kötümser kilitle** korunur; seçim gerekçesi 2409'a karşı 0 geçerli
rettir. Kilidin tutulma süresi bir kira olarak verilir ve Uygulama Katmanı ve Servis Etkileşimi
kursunun Lider Seçimi dersindeki kira mekaniği burada kilit sahipliğine uygulanır; asılı kalan
kilit kira dolunca düşer.

Rezervasyon penceresi 120 saniyedir ve süresi dolan rezervasyonu serbest bırakan denetçi aynı
kursun Zamanlayıcı–Aracı–Denetçi dersindeki denetçidir. Açılış anındaki 20.000 istek/s'nin
kontenjan satırına ulaşmaması için önde iki katman durur: önceki vakanın hız sınırlayıcısı ve
aynı kursun Kuyruk Tabanlı Yük Dengeleme dersindeki kuyruk. Önceki vakada kuyruk **bilerek
kullanılmamıştı**, çünkü orada sözleşme reddetmekti; burada sözleşme sıraya koymaktır ve aynı
kalıp bu yüzden yer değiştirir.

Tekillik anahtarı kapsamı `(alıcı, kontenjan, istemci istek kimliği)` üçlüsüdür; alıcının
yeniden denemesi ikinci bir rezervasyon açmaz.

**Bilerek kullanılmayan kalıp.** Kontenjan sayacının parçalara bölünmesi (Veri Katmanı
Ölçekleme, Parçalama) kullanılmaz: bu, önceki vakada ölçülen bölünmüş kotanın aynısıdır ve
başka parçada kalem dururken ret üretir — orada bu, sınırının tam üstündeki istemcinin yüzde
4,17'sini reddetmişti.

Elenen alternatif iyimser kilitlemedir ve eleme sayısı 2409 geçerli rettir. Kazandığı da
yazılır: 6,23 kat daha az adım, yani kilit tutmayan bir yazma yolu. Hangi kısıt değişirse
kazandığı da bellidir: eşzamanlı işçi 16 yerine 2 olsaydı geçerli ret 130'a, kontenjanın yüzde
2,6'sına inerdi ve deneme bütçesi 3'ten 6'ya çıkarılarak bu da düşürülebilirdi.

#### Arıza Davranışı ve Feda Edilen

Kilidi tutan işçi düşerse kontenjan satırı kira süresi boyunca kilitli kalır ve bu süre
boyunca hiçbir satış yapılamaz; kira 0,179 saniyelik tükenme penceresinden uzunsa açılışın
tamamı durur. Kuyruk tarafında ise Dayanıklılık ve Güvenilirlik kursunun Zarif Bozulma dersi
işler: kontenjan tükendiğinde istekler kontenjan satırına hiç dokunmadan reddedilir ve bu,
196.429 isteğin normal yoludur.

Feda edilen tek cümlededir: bu tasarım fazla satışı sıfırlamak için verimi feda eder — 304.276
bekleme adımı ve 6,23 kat işlem, tek bir kalemin bile fazladan satılmaması karşılığında
ödenir.

#### Özet

- Talep kontenjanın 56 katı ve kontenjan tepe hızda 0,179 saniyede tükeniyor; bu aralıkta
  yalnız 3571 istek geliyor, kalan 196.429 isteğin işi reddedilmektir.
- Korumasız düzen kontenjanın 5,593 katını sattı, fazla satış 22.963; üstelik sayaç sonunda
  1798 kalan stok gösteriyor, yani yanlış yönde yanlış.
- İyimser kilitleme fazla satışı sıfırladı ama 10.038 çakışma üretti ve deneme bütçesi biten
  2409 geçerli isteği reddetti.
- Kötümser kilitleme fazla satışı ve geçerli reddi birden sıfırladı; bedeli 304.276 bekleme
  adımı ve iyimserin 6,23 katı toplam adımdır.
- İki maliyet de eşzamanlı işçi sayısına bağlı: 16 yerine 2 işçide geçerli ret 2409'dan 130'a,
  bekleme adımı 304.276'dan 20.043'e iniyor.
- Rezervasyon penceresini 30 saniyeden 120 saniyeye çıkarmak ilk turda satılanı 1000 kalem
  artırıyor, boşa tutulan kalem-saniyeyi 2,55 katına çıkarıyor.

#### Sonraki Adım

Rezervasyon bir söz verdi ama sözü tutan adım bu vakanın dışında bırakıldı: ödemenin kendisi.
O adım iki tarafı olan bir işlemdir ve iki taraf birbirinin kayıtlarını göremez. Sonraki vaka
bu ayrımı ele alır. Sorulacak sayı iki kaydın birbirinden ne kadar ayrıştığıdır: eşleşmeyen
kayıt sayısı, farkın nereden doğduğu ve geç gelen bir onayın bu farkı ne kadar sürede
kapattığı.

### Ödeme Akışı

Source: https://academia.sh/tr/kurslar/vaka-calismalari/odeme-akisi

İki tarafın kayıtlarının ayrıştığı vaka: mutabakat çevriminde eşleşmeyen kayıt sayısının kaynağına göre ayrılması, yoklamanın farkı binde 59,74'ten binde 9,76'ya indirmesi, geç gelen onayın kalan farkın çoğunu oluşturması, çevrim aralığının fark oranını düşürürken kapanma süresini uzatması ve denetim izinin yedi yıllık hacmi.

Önceki vakada rezervasyon bir söz verdi ve sözü tutan adım kapsam dışında bırakıldı: ödemenin
kendisi. O adım iki taraflıdır; kayıt hem bizde hem dış tarafta tutulur, ikisi birbirinin
defterini göremez ve aralarındaki tek bağ ileti alışverişidir.

Etkisizlik ve tekillik anahtarı Dayanıklılık ve Güvenilirlik kursunun Etkisiz İşlemler
dersinde kuruldu ve tekrarlanmaz; yeniden gönderilen bir istek ikinci bir işlem açmaz. Bu
dersin sorusu başkadır: iletiler kaybolduğunda ya da geç geldiğinde iki defter ne kadar
**ayrışır**, bu fark nasıl sayılır ve ne zaman kapanır.

#### Kısıtlar ve Kapsam

İşlevsel gereksinimler: ödeme başlatmak, dış taraftan gelen durum bildirimini işlemek, durumu
sorgulamak, iade başlatmak ve her durum geçişini denetim izine yazmak.

Tasarlanmayan işler: kimlik doğrulama akışı, dolandırıcılık puanlama, muhasebe defterinin
kapanışı ve iade kurallarının kendisi.

İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: bir işlemin iki taraftaki durumu
en geç **30 dakika** içinde eşleşir (kaynak: bir farkın elle araştırılmadan önce kendi kendine
kapanmasına bırakılan süre), bir mutabakat çevriminde eşleşmeyen kayıt oranı **binde 10'u**
geçmez (kaynak: bir çevrimde elle incelenebilecek kayıt sayısı), her durum geçişi denetim
izinde görünür ve iz kaydı sonradan değişmez (kaynak: bir işlemin geçmişinin yeniden
kurulabilmesi yükümlülüğü).

#### Varsayımlar ve Ölçek

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| OD1 | günlük ödeme işlemi | 1.200.000 | tamamlanan ve reddedilen girişimlerin toplamı |
| OD2 | tepe çarpanı | 4 | akşam saatlerinin gün ortalamasına oranı |
| OD3 | bildirim gecikmesi | 0,92 / 0,06 / 0,02 | beş saniyeden, beş dakikadan ve daha uzun süreden sonra gelen bildirim payları |
| OD4 | bildirim kaybı | 0,03 | dış tarafın bildiriminin hiç ulaşmaması |
| OD5 | yoklama aralığı | 60 saniye | beklemede kalan işlemin sorulma sıklığı |
| OD6 | mutabakat çevrimi | 900 saniye | iki defterin karşılaştırılma sıklığı |
| OD7 | denetim izi | 5 geçiş × 260 bayt | işlem başına durum geçişi sayısı ve kayıt boyu |
| OD8 | saklama süresi | 2555 gün | uyuşmazlıkların yedi yıl geriye açılabilmesi |
| OD9 | dış tarafta sonuçlanma | 0,90 / 0,08 / 0,02 | 3, 3–60 ve 60–900 saniyede sonuçlanan işlem payları; dış tarafın kendi onay adımları kuyruk üretir |
| OD10 | eşzamanlı doğrulama bütçesi | 20 saniye | isteğin yanıt beklerken açık tutulabileceği üst süre |

```js
// odeme/olcek.mjs — OD varsayim tablosundan cikan kabaca buyukluk hesabi
const OD = { gunlukIslem: 1_200_000, tepeCarpani: 4, bildirimKaybi: 0.03, yoklama: 60,
  cevrim: 900, izKaydiBayt: 260, gecisSayisi: 5, saklamaGun: 2555 };   // OD1..OD8
const GUN = 86_400;

const tepe = (OD.gunlukIslem / GUN) * OD.tepeCarpani;
const izGunluk = OD.gunlukIslem * OD.gecisSayisi * OD.izKaydiBayt;
console.log(`tepe islem/s                   ${tepe.toFixed(2)}`);
console.log(`cevrimde karsilastirilan kayit ${Math.round(tepe * OD.cevrim)}`);
console.log(`gunluk kaybolan bildirim       ${OD.gunlukIslem * OD.bildirimKaybi}`);
console.log(`yoklamanin bulacagi kayit/s    ${(tepe * OD.bildirimKaybi).toFixed(2)}`);
console.log(`denetim izi gunluk GB          ${(izGunluk / 1e9).toFixed(2)}`);
console.log(`denetim izi ${OD.saklamaGun} gunde TB      ${((izGunluk * OD.saklamaGun) / 1e12).toFixed(2)}`);
console.log(`iz kaydi / islem kaydi orani   ${OD.gecisSayisi} (her gecis bir satir)`);

console.log(`\n${"mutabakat cevrimi".padStart(19)}${"tepe partide kayit".padStart(20)}` +
  `${"gunluk cevrim".padStart(15)}`);
for (const c of [300, OD.cevrim, 3600])
  console.log(`${`${c / 60} dk`.padStart(19)}${Math.round(tepe * c).toString().padStart(20)}` +
    `${(GUN / c).toFixed(0).padStart(15)}`);
console.log(`\ngunluk karsilastirilan kayit cevrimden bagimsiz: ${OD.gunlukIslem} ` +
  `(her kayit bir kez); cevrim yalnizca parti boyunu ve fark suresini degistirir`);
```

```
tepe islem/s                   55.56
cevrimde karsilastirilan kayit 50000
gunluk kaybolan bildirim       36000
yoklamanin bulacagi kayit/s    1.67
denetim izi gunluk GB          1.56
denetim izi 2555 gunde TB      3.99
iz kaydi / islem kaydi orani   5 (her gecis bir satir)

  mutabakat cevrimi  tepe partide kayit  gunluk cevrim
               5 dk               16667            288
              15 dk               50000             96
              60 dk              200000             24

gunluk karsilastirilan kayit cevrimden bagimsiz: 1200000 (her kayit bir kez); cevrim yalnizca parti boyunu ve fark suresini degistirir
```

Bu sayılar **hesap** sınıfındadır; OD9 ile OD10 yalnız elenen alternatifin modelinde kullanılır.
Üçü tasarımı belirliyor. Birincisi, tepe hız saniyede 55,56 işlemdir; bu vakanın hiçbir kararı
hacimle ilgili değildir. İkincisi, günde 36.000 bildirim hiç ulaşmaz ve bunları bulmanın
maliyeti saniyede 1,67 sorgudur — küçük bir yük, ama olmadığında o kayıtlar ayrışır. Üçüncüsü,
denetim izi asıl hacim kalemidir: günde 1,56 GB, yedi yılda 3,99 TB, işlem kaydının beş katı
satır.

#### Farkın Ölçülmesi

Ölçüm süreç içi bir modeldir: gerçek dış taraf, ağ ya da depo yoktur; gecikme ve kayıp
parametredir, zaman model saatidir. Model bir çevrimin işlemlerini üretir, her biri için dış
tarafta sonuçlanma anıyla yerel tarafın bunu öğrendiği anı hesaplar ve çevrim bitince iki
durumu karşılaştırır. Fark üç kaynağa ayrılır: bildirimi kaybolan, bildirimi geç gelen ve dış
tarafta henüz sonuçlanmamış işlem — sonuncusu fark değildir, iki taraf da aynı şeyi söyler.

```js
// odeme/mutabakat.mjs — iki tarafin kayitlari arasindaki farkin surec ici modeli. Gercek dis
// taraf, ag ya da depo yoktur: gecikme ve kayip parametredir, zaman model saatidir (saniye).
const TEPE = 55.56, YOKLAMA = 60, KAYIP = 0.03;      // hesap; OD5, OD4
const SONUCLANMA = 3;                                 // dis tarafta islemin bitme suresi (s)

function uretec(tohum) {                              // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

// OD3: bildirim gecikmesi uc dilimde - %92 hizli, %6 gecikmeli, %2 gec gelen
const gecikme = (r) => (r < 0.92 ? 5 * (r / 0.92)
  : r < 0.98 ? 5 + 295 * ((r - 0.92) / 0.06)
  : 300 + 1500 * ((r - 0.98) / 0.02));

export function kos({ cevrim, yoklamali, tohum }) {
  const rnd = uretec(tohum);
  const n = Math.round(TEPE * cevrim);
  const s = { islem: n, gecGelen: 0, kaybolan: 0, sonuclanmamis: 0 };
  for (let i = 0; i < n; i += 1) {
    const baslangic = (i / TEPE), biten = baslangic + SONUCLANMA;
    const kayipMi = rnd() < KAYIP, g = gecikme(rnd());
    const bildirim = kayipMi ? Infinity : biten + g;
    const yoklamaAni = yoklamali ? Math.ceil(biten / YOKLAMA) * YOKLAMA : Infinity;
    const bilinen = Math.min(bildirim, yoklamaAni);
    if (biten >= cevrim) { s.sonuclanmamis += 1; continue; }  // dis tarafta da bitmemis
    if (bilinen < cevrim) continue;                           // iki taraf da biliyor
    if (kayipMi) s.kaybolan += 1; else s.gecGelen += 1;
  }
  s.eslesmeyen = s.gecGelen + s.kaybolan;
  s.binde = (1000 * s.eslesmeyen) / s.islem;
  return s;
}

const yaz = (etiket, r) =>
  console.log(`${etiket.padEnd(26)}${String(r.islem).padStart(9)}${String(r.eslesmeyen).padStart(12)}` +
    `${String(r.gecGelen).padStart(11)}${String(r.kaybolan).padStart(10)}` +
    `${String(r.sonuclanmamis).padStart(15)}${r.binde.toFixed(2).padStart(9)}`);

console.log(`model: tepe ${TEPE} islem/s, bildirim kaybi ${KAYIP}, yoklama ${YOKLAMA} s, tohum 20260730`);
console.log(`\n${"duzen (cevrim 900 s)".padEnd(26)}${"islem".padStart(9)}${"eslesmeyen".padStart(12)}` +
  `${"gec gelen".padStart(11)}${"kaybolan".padStart(10)}${"sonuclanmamis".padStart(15)}${"binde".padStart(9)}`);
yaz("yalniz bildirim", kos({ cevrim: 900, yoklamali: false, tohum: 20260730 }));
yaz("bildirim + yoklama", kos({ cevrim: 900, yoklamali: true, tohum: 20260730 }));

console.log(`\n${"mutabakat cevrimi".padStart(19)}${"islem".padStart(9)}${"eslesmeyen".padStart(12)}` +
  `${"binde".padStart(9)}${"esik binde 10".padStart(15)}${"kapanma dk".padStart(12)}${"esik 30 dk".padStart(12)}`);
for (const c of [300, 900, 3600]) {
  const r = kos({ cevrim: c, yoklamali: true, tohum: 20260730 });
  console.log(`${`${c / 60} dk`.padStart(19)}${String(r.islem).padStart(9)}${String(r.eslesmeyen).padStart(12)}` +
    `${r.binde.toFixed(2).padStart(9)}${(r.binde <= 10 ? "gecer" : "gecmez").padStart(15)}` +
    `${String((2 * c) / 60).padStart(12)}${((2 * c) / 60 <= 30 ? "gecer" : "gecmez").padStart(12)}`);
}

const a = kos({ cevrim: 900, yoklamali: false, tohum: 20260730 });
const b = kos({ cevrim: 900, yoklamali: true, tohum: 20260730 });
const c60 = kos({ cevrim: 3600, yoklamali: true, tohum: 20260730 });
console.log(`\nyoklamanin kapattigi fark: ${a.eslesmeyen} -> ${b.eslesmeyen} kayit ` +
  `(binde ${a.binde.toFixed(2)} -> ${b.binde.toFixed(2)})`);
console.log(`kalan farkin ${b.gecGelen}/${b.eslesmeyen} tanesi gec gelen onaydir`);
console.log(`cevrim 15 dk -> 60 dk: eslesmeyen sayisi ${b.eslesmeyen} -> ${c60.eslesmeyen} (neredeyse ayni), ` +
  `binde ${b.binde.toFixed(2)} -> ${c60.binde.toFixed(2)}`);
```

```
model: tepe 55.56 islem/s, bildirim kaybi 0.03, yoklama 60 s, tohum 20260730

duzen (cevrim 900 s)          islem  eslesmeyen  gec gelen  kaybolan  sonuclanmamis    binde
yalniz bildirim               50004        2987       1437      1550            166    59.74
bildirim + yoklama            50004         488        369       119            166     9.76

  mutabakat cevrimi    islem  eslesmeyen    binde  esik binde 10  kapanma dk  esik 30 dk
               5 dk    16668         488    29.28         gecmez          10       gecer
              15 dk    50004         488     9.76          gecer          30       gecer
              60 dk   200016         482     2.41          gecer         120      gecmez

yoklamanin kapattigi fark: 2987 -> 488 kayit (binde 59.74 -> 9.76)
kalan farkin 369/488 tanesi gec gelen onaydir
cevrim 15 dk -> 60 dk: eslesmeyen sayisi 488 -> 482 (neredeyse ayni), binde 9.76 -> 2.41
```

Bu sayılar **ölçüm** sınıfındadır; tohum 20260730 ile yeniden üretilir.

İlk tablo yoklamanın payını ayırıyor. Yalnız bildirime dayanan düzende çevrim başına 2987 kayıt
eşleşmiyor, binde 59,74; eşik binde 10 olduğuna göre bu düzen tek başına geçmiyor. Yoklama
eklendiğinde fark 488'e, binde 9,76'ya iniyor: kaybolan bildirim 1550'den 119'a düşüyor, çünkü
en geç 60 saniye içinde sorgulamayla bulunuyor.

Kalan 488 kaydın 369'u **geç gelen onaydır** ve yoklama bunu kapatamaz: dış taraf işi bitirmiş,
bildirimi henüz göndermemiştir, yoklama da o anda "beklemede" yanıtı alır. Bu bir hata değil,
iki tarafın saatleri arasındaki gerçek bir aralıktır; mutabakatın işi onu **saymak** ve
kapanmasını beklemektir.

İkinci tablo çevrim aralığını seçiyor ve en öğretici satır sayının kendisidir: eşleşmeyen kayıt
5 dakikada 488, 15 dakikada 488, 60 dakikada 482 — neredeyse hiç değişmiyor. Fark bir sınır
olayıdır; çevrimin uzunluğuyla değil, **kenarındaki** işlem sayısıyla belirlenir. Çevrimi
uzatmak farkı çözmez, oranı binde 29,28'den binde 2,41'e seyreltir. Kapanma süresi ise iki
çevrim uzunluğundadır: 10, 30 ve 120 dakika. İki eşik iki yönden bastırıyor ve yalnız 15
dakika ikisini birden geçiyor.

#### Tasarım

**Durum bildirimi ve yoklama.** Dış tarafın bildirimi Uygulama Katmanı ve Servis Etkileşimi
kursunun Eşzamansız İstek–Yanıt dersindeki yapıdır; yoklama ise bu konunun ilk vakasında
**elenmiş** kalıptır. Orada birincil teslim yolu olarak 140,63 kat istek ürettiği için
elenmişti; burada yalnız beklemede kalanı sorduğu için tepe yükü saniyede 1,67 sorgudur. Aynı
kalıp, farklı rol, farklı parametre.

**Denetim izi.** Veri Katmanı Ölçekleme kursunun Olay Kaynaklı Tasarım dersindeki eklemeli
kütük burada denetim izidir; parametre saklama süresi ve geçiş sayısıdır: işlem başına 5 satır,
260 bayt, yedi yılda 3,99 TB. İzin üzerine yazılmaz, yalnız eklenir; işlemin o anki durumu
izden türetilen bir izdüşümdür.

**Fark bulununca yapılan iş.** Eşleşmeyen kayıt Dayanıklılık ve Güvenilirlik kursunun Telafi
Edici İşlemler dersindeki adımı doğurur: yerel taraf "başarılı" derken dış taraf "reddedildi"
diyorsa yerel etki geri alınır. Parametre, telafinin yalnız **mutabakat** tarafından
tetiklenmesidir; hiçbir istek yolu kendi başına telafi başlatmaz.

**Bilerek kullanılmayan kalıp.** Ödeme durumu önbelleğe alınmaz (Veri Katmanı Ölçekleme,
Önbellek Mimarisi): bayat bir durum kullanıcıya kesinleşmiş gibi gösterilir — burada bayatlık
bir pencere değil, bir kusurdur.

#### Elenen Alternatif: Eşzamanlı Doğrulama

Mutabakat çevrimini büsbütün ortadan kaldıran bir tasarım savunulabilir: her ödeme dış tarafa
senkron sorulur ve istek yanıtı bekler, yani ayrışacak iki defter hiç oluşmaz. Bu düzenin
duyarlı olduğu şey yük değil, dış tarafta **sonuçlanma süresidir**. Seçilen tasarımda o süre
uzadıkça işlemler yukarıdaki modelin üçüncü kaynağına, fark saymayan kovaya düşer; eşzamanlı
düzende ise doğrudan yanıtsız isteğe dönüşür. Alternatifin modeli bu yüzden sonuçlanmaya kendi
dağılımını (OD9) ve isteğe bir bekleme bütçesi (OD10) verir; senkron yanıt kayıpsız sayılır,
ölçü alternatife elverişlidir.

```js
// odeme/eszamanli.mjs — elenen alternatifin surec ici modeli. Gercek dis taraf yoktur:
// sonuclanma suresi parametredir, zaman model saatidir (saniye).
const TEPE = 55.56, CEVRIM = 900, N = Math.round(TEPE * CEVRIM);   // hesap; OD6

function uretec(tohum) {                        // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}
const sonuclanma = (r) =>                       // OD9: %90 3 s, %8 3-60 s, %2 60-900 s
  (r < 0.9 ? 3 : r < 0.98 ? 3 + 57 * ((r - 0.9) / 0.08) : 60 + 840 * ((r - 0.98) / 0.02));

function kos(butce) {                           // her odeme yanit gelene dek istegi acik tutar
  const rnd = uretec(20260730);
  let belirsiz = 0, tutulan = 0;
  for (let i = 0; i < N; i += 1) {
    const s = sonuclanma(rnd());
    tutulan += Math.min(s, butce);
    if (s > butce) belirsiz += 1;               // yanitsiz kapandi, kapatacak cevrim de yok
  }
  return { belirsiz, binde: (1000 * belirsiz) / N, ort: tutulan / N };
}

console.log(`model: tepe ${TEPE} islem/s, cevrim ${CEVRIM} s, ${N} islem, tohum 20260730`);
for (const b of [20, 60, 540]) {
  const r = kos(b);
  console.log(`butce ${b} s: belirsiz ${r.belirsiz} (binde ${r.binde.toFixed(2)}, esik ` +
    `${r.binde <= 10 ? "gecer" : "gecmez"}), ortalama tutulma ${r.ort.toFixed(2)} s, ` +
    `tepede acik istek ${(TEPE * r.ort).toFixed(1)}`);
}
```

```
model: tepe 55.56 islem/s, cevrim 900 s, 50004 islem, tohum 20260730
butce 20 s: belirsiz 3789 (binde 75.77, esik gecmez), ortalama tutulma 4.48 s, tepede acik istek 249.1
butce 60 s: belirsiz 995 (binde 19.90, esik gecmez), ortalama tutulma 6.38 s, tepede acik istek 354.4
butce 540 s: belirsiz 421 (binde 8.42, esik gecer), ortalama tutulma 13.06 s, tepede acik istek 725.7
```

Bu sayılar **ölçüm** sınıfındadır; tohum 20260730 ile yeniden üretilir. Alternatifi eleyen sayı
**binde 75,77**tir: 20 saniyelik bütçede çevrim başına 3789 işlem yanıtsız kapanır — eşiğin 7,6
katı — ve bunları kapatacak bir çevrim yoktur; kapanma süresi 30 dakika değil, tanımsızdır.
Bütçeyi büyütmek ucuz değildir: 60 saniyede oran binde 19,90'da kalıyor, eşik ancak 540
saniyede geçiliyor, yani istemci dokuz dakika bekletilerek; o noktada tepede aynı anda 725,7
istek açık duruyor.

Kazandığı da yazılır: bütçe içinde yanıtlanan işlemde iki defter **yanıt anında** eşleşir,
ortalama kapanma 4,48 saniyedir, çevrim ve telafi işi hiç doğmaz. Kısıt şurada dönerdi: dış
taraf işlemlerin yüzde 99'unu bütçe içinde sonuçlandırsaydı belirsiz oran binde 10'a iner,
alternatif eşiği geçer ve mutabakat çevrimi gereksizleşirdi.

#### Arıza Davranışı ve Feda Edilen

Dış tarafın bildirim ucu tümden durursa yoklama tek kanal kalır ve bütün işlemler en geç 60
saniye gecikmeyle öğrenilir; bu tepe yükte saniyede 55,56 sorgudur, normalin 33 katıdır ve
Dayanıklılık ve Güvenilirlik kursunun Kısıtlama ve Yük Boşaltma dersindeki kısıtlama bu hızı
keser. Mutabakat çevrimi kaçırılırsa fark kaybolmaz, sonraki çevrimde iki katı kayıtla görünür;
mutabakatın kendisi durumunu izden yeniden kurduğu için kesintiye dayanıklıdır.

Feda edilen tek cümlededir: bu tasarım anlık tutarlılığı feda eder — iki defter hiçbir zaman
aynı anda aynı şeyi söylemez, yalnız 30 dakika içinde söyleyeceği güvence altına alınır.

#### Özet

- Bu vakada hacim karar vermiyor: tepe 55,56 işlem/s her yapıda taşınır, asıl hacim kalemi
  denetim izidir — günde 1,56 GB, yedi yılda 3,99 TB.
- Yalnız bildirime dayanan düzende çevrim başına 2987 kayıt eşleşmiyor (binde 59,74); yoklama
  bunu 488'e (binde 9,76) indiriyor ve kaybolan bildirimi 1550'den 119'a düşürüyor.
- Kalan 488 farkın 369'u geç gelen onaydır; yoklama bunu kapatamaz, çünkü dış taraf da o anda
  sonucu bildirmemiştir.
- Eşleşmeyen kayıt sayısı çevrim uzunluğundan neredeyse bağımsızdır (488, 488, 482); çevrimi
  uzatmak farkı çözmez, oranı binde 29,28'den binde 2,41'e seyreltir.
- İki eşik çevrimi tek bir değere sıkıştırıyor: binde 10 sınırı 5 dakikayı, 30 dakikalık
  kapanma sınırı 60 dakikayı eliyor ve 15 dakika kalıyor.
- Çevrimsiz eşzamanlı doğrulama alternatifi binde 75,77 belirsiz kayıtla eleniyor; eşiği ancak
  540 saniyelik bekleme bütçesiyle, tepede 725,7 açık istekle geçiyor.

#### Sonraki Adım

Beş vaka aynı ailedendi: yazma hâkimdi ve bir kaydın yanlış olması kabul edilmiyordu. Kararlar
teslim garantisinden, kanal sırasından, pencere biçiminden, kilit türünden ve mutabakat
çevriminden çıktı. Beşinde de gözden kaçan ortak bir özellik var: veri **küçüktü**. Bir ileti
220 bayt, bir bildirim kaydı 300 bayt, bir sayaç 48 bayt, bir stok satırı 64 bayt, bir iz
kaydı 260 bayttı; hiçbir tasarım kararı baytın kendisinden çıkmadı, hepsi kayıt başına anlamdan
çıktı. Kaydın tek başına anlamsız olduğu, değerinin ancak yığın hâlinde ortaya çıktığı ve tek
bir nesnenin bir sunucunun belleğine sığmadığı sistemlerde bu sıralama tersine döner. Sonraki
konu o tersine dönmüş sıralamayı ele alır.

## Veri Yoğun Sistemler

Verinin hacminin tasarımı belirlediği beş vaka: parçalı yükleme ve dayanıklılık hesabıyla nesne depolama servisi, kodlama boru hattı ve uyarlanabilir akışla video yayın servisi, zaman serisi yazma yolu ve toplulaştırmayla ölçüt toplama sistemi, uzamsal dizinleme ve yakınlık sorgularıyla konum tabanlı servis ve iki işleme modelinin birlikte kullanıldığı toplu ile akış işleme hattı.

### Nesne Depolama Servisi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/nesne-depolama-servisi

Hacmin tasarımı belirlediği ilk vaka: parça boyunun kesinti olasılığına göre seçilmesi, fazladan gönderilen baytın süreç içi bir modelde ölçülüp kapalı biçimle doğrulanması, kopya sayısı ile silme kodlamasının saklama katı ve yıllık kayıp üzerinden karşılaştırılması ve küçük nesnede kodlamanın istek sayısıyla elenmesi.

Önceki konu beş vakayı yazma tarafından savundu ve hepsinde ortak bir nokta vardı: veri
**küçüktü** ve kayıt başına anlamlıydı. Tasarımı hacim değil, kaydın doğruluğu, sırası ve teslim
garantisi belirledi.

Bu konu o varsayımı kaldırır. Burada tek bir kayıt megabaytlar tutar ve saklama kalemi ötekileri
gölgeler. İlk vaka bir **nesne deposu** (object storage) servisidir: istemci bir anahtarla ikili bir
gövde yazar, sonra aynı anahtarla okur. Ölçülecek iki karar var — **parçalı yüklemenin** (multipart
upload) fazladan gönderdiği bayt ve verinin kaç kopya tutulacağı. Bu dersin **dayanıklılığı
(veri)** verinin kaybolmamasıdır; Dayanıklılık ve Güvenilirlik kursunun dayanıklılığı hizmetin
ayakta kalmasıydı — bir depo ayakta olup veri kaybedebilir.

#### Kısıtlar

İşlevsel gereksinim: anahtarla nesne yazma, okuma, silme, önek listeleme; büyük nesnelerde parça
parça gönderme, sonda birleştirme, yarım kalanı sürdürme.

İşlevsel olmayan gereksinim, sayıyla: nesne başına fazladan gönderilen bayt — yeniden gönderim ve
parça ek yükü — nesne boyunun yüzde **2**'sini geçmez; yıllık kaybolan nesne **500**'ün altında
kalır; saklanan bayt ham verinin **1,5** katını aşmaz; küçük nesne okuması **tek** istekle
karşılanır.

Kapsam daraltması: erişim denetimi ve imza doğrulama (Backend müfredatının Kimlik Doğrulama ve Yetkilendirme kursu),
sürümleme, yaşam döngüsü, çapraz bölge kopya ve içerikte arama tasarlanmaz.

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| ND1 | günlük yüklenen nesne | 1.200.000 | uçların günlük toplamı |
| ND2 | küçük nesnelerin payı | 0,92 | küçük ekler sayıca hâkim |
| ND3 | küçük nesne ortalama boyu | 180 KB | belge ve küçük görsel |
| ND4 | büyük nesne ortalama boyu | 90 MB | kayıt ve yedek dosyası |
| ND5 | nesne başına ömür boyu okuma | 4 | nesne birkaç kez okunur |
| ND6 | tepe çarpanı | 3 | tepe saatin gün ortalamasına oranı |
| ND7 | kesintiler arası gönderilen bayt | 250 MB | uçlar hareketli ağlarda |
| ND8 | parça başına ek yük | 8 KB | başlık, imza, parça defteri kaydı |
| ND9 | yıllık düğüm kayıp olasılığı | 0,01 | donanım ve işletim kaynaklı |
| ND10 | saklama süresi | 365 gün | sözleşme yıllık dönem üzerinden |
| ND11 | nesne üstverisi kaydı | 400 bayt | anahtar, boy, sağlama, yerleşim |
| ND12 | en yavaş yükleme ucu | 1 Mbit/s | vale anahtarı ömrünün alt sınırı |

#### Ölçek

```js
// nesne-olcek.mjs — ND tablosundan cikan olcek hesabi; hepsi aritmetiktir
const ND = { nesne: 1_200_000, kucukPay: 0.92, kucukBayt: 180e3, buyukBayt: 90e6, okuma: 4,
  tepe: 3, kesintiBayt: 250e6, parcaEkYuk: 8192, saklamaGun: 365, ustveriBayt: 400,
  parcaBoyu: 2e6, yavasUcMbit: 1 };
const GUN = 86_400;
const ortBayt = ND.kucukPay * ND.kucukBayt + (1 - ND.kucukPay) * ND.buyukBayt;
const gunlukBayt = ND.nesne * ortBayt, nesneSn = ND.nesne / GUN;
const parcaSn = nesneSn * ((1 - ND.kucukPay) * Math.ceil(ND.buyukBayt / ND.parcaBoyu) + ND.kucukPay);

const r = {
  "buyuk nesnelerin bayt payi": ((1 - ND.kucukPay) * ND.buyukBayt) / ortBayt,
  "gunluk yuklenen TB": gunlukBayt / 1e12,
  "ham saklanan PB": (gunlukBayt * ND.saklamaGun) / 1e15,
  "tepe yukleme nesne/s": nesneSn * ND.tepe,
  "tepe parca istegi/s": parcaSn * ND.tepe,
  "parcalamanin istek carpani": parcaSn / nesneSn,
  "tepe yukleme Mbit/s": (gunlukBayt / GUN) * ND.tepe * 8 / 1e6,
  "tepe okuma Mbit/s": (gunlukBayt / GUN) * ND.okuma * ND.tepe * 8 / 1e6,
  "ustveri saklanan GB": (ND.nesne * ND.saklamaGun * ND.ustveriBayt) / 1e9,
  "nesne/ustveri hacim orani": ortBayt / ND.ustveriBayt,
};
for (const [ad, d] of Object.entries(r))
  console.log(ad.padEnd(28) + (Number.isInteger(d) ? String(d) : d.toFixed(2)).padStart(10));

const en = (k) => Math.sqrt(2 * ND.parcaEkYuk * ND.kesintiBayt * k);   // en dusuk fazladan bayt
console.log(`\nen iyi parca boyu = karekok(2 * ek yuk * kesinti araligi) = ${(en(1) / 1e6).toFixed(2)} MB` +
  `; ND7 x2 -> ${(en(2) / 1e6).toFixed(2)} MB, ND7 x0.5 -> ${(en(0.5) / 1e6).toFixed(2)} MB`);
console.log(`en yavas ucta 2 MB parca ${(ND.parcaBoyu * 8) / (ND.yavasUcMbit * 1e6)} s -> ` +
  `vale anahtari omru 60 s (x${(60 / ((ND.parcaBoyu * 8) / (ND.yavasUcMbit * 1e6))).toFixed(2)} pay)`);
```

```
buyuk nesnelerin bayt payi        0.98
gunluk yuklenen TB                8.84
ham saklanan PB                   3.23
tepe yukleme nesne/s             41.67
tepe parca istegi/s             188.33
parcalamanin istek carpani        4.52
tepe yukleme Mbit/s            2455.20
tepe okuma Mbit/s              9820.80
ustveri saklanan GB             175.20
nesne/ustveri hacim orani     18414.00

en iyi parca boyu = karekok(2 * ek yuk * kesinti araligi) = 2.02 MB; ND7 x2 -> 2.86 MB, ND7 x0.5 -> 1.43 MB
en yavas ucta 2 MB parca 16 s -> vale anahtari omru 60 s (x3.75 pay)
```

Bu sayılar **hesap** sınıfındadır ve dördü tasarımı belirliyor. Nesnelerin yüzde 8'i baytların
yüzde 98'ini taşıyor, bu yüzden iki boy sınıfı ayrı tasarlanır. Üstveri hacmi nesne hacminin
18.414'te biri: 3,23 PB'a karşılık 175,20 GB. Parçalama istek sayısını 4,52 katına çıkarıyor:
41,67 nesne yüklemesi tepe uçta 188,33 parça isteği. Tepe okuma çıkışı 9820,80 Mbit/s — baskın
kalem istek değil, bant genişliği ve saklamadır.

#### Parça Boyu

Kesinti bir akışı kestiğinde o ana kadar gönderilmiş baytlar boşa gitmiştir. Parça küçüldükçe boşa
giden bayt azalır, ama ek yük (ND8) her parçada ödenir. İki eğrinin toplamı ölçülür.

```js
// parca-boyu.mjs — surec ici model: 90 MB'lik nesnenin parcali yuklenmesi. Ag yok, dosya yok;
// kesinti, gonderilen bayt uzerinde ustel araliklarla uretilir. Tohum gorunur, uretec kendi yazilir.
const NESNE = 90e6, KESINTI = 250e6, EK = 8192, KOSU = 5000, TOHUM = 20260730, L = 1 / KESINTI;
let durum = TOHUM;                                  // dogrusal eslesik uretec
const rast = () => { durum = (durum * 1103515245 + 12345) % 2147483648; return durum / 2147483648; };
const aralik = () => -Math.log(1 - rast()) / L;     // kesintiye kadar gonderilen bayt

function yukle(boy) {
  let gonderilen = 0;
  for (let kalan = NESNE; kalan > 0; kalan -= boy) {
    const p = Math.min(boy, kalan);
    for (;;) {
      gonderilen += EK;                             // her deneme bir istek turu
      const g = aralik();
      if (g >= p) { gonderilen += p; break; }
      gonderilen += g;                              // kesildi: bu parca bastan gonderilir
    }
  }
  return gonderilen;
}

function beklenen(boy) {                            // ayni bolumleme uzerinde kapali bicim
  let t = 0;
  for (let kalan = NESNE; kalan > 0; kalan -= boy) {
    const p = Math.min(boy, kalan);
    t += (Math.exp(L * p) - 1) / L + EK * Math.exp(L * p);
  }
  return t;
}

console.log(`model: ${KOSU} yukleme, nesne ${NESNE / 1e6} MB, kesinti araligi ${KESINTI / 1e6} MB, ` +
  `parca ek yuku ${EK} bayt, tohum ${TOHUM}\n\n` + "parca".padStart(9) + "parca sayisi".padStart(14) +
  "gonderilen MB".padStart(15) + "olculen oran".padStart(14) + "beklenen oran".padStart(15));
for (const b of [256e3, 1e6, 2e6, 4e6, 16e6, NESNE]) {
  let t = 0;
  for (let i = 0; i < KOSU; i += 1) t += yukle(b);
  const ort = t / KOSU;
  console.log((b === NESNE ? "tek akis" : `${(b / 1e6).toFixed(2)} MB`).padStart(9) +
    String(Math.ceil(NESNE / b)).padStart(14) + (ort / 1e6).toFixed(2).padStart(15) +
    (ort / NESNE).toFixed(4).padStart(14) + (beklenen(b) / NESNE).toFixed(4).padStart(15));
}
```

```
model: 5000 yukleme, nesne 90 MB, kesinti araligi 250 MB, parca ek yuku 8192 bayt, tohum 20260730

    parca  parca sayisi  gonderilen MB  olculen oran  beklenen oran
  0.26 MB           352          92.94        1.0326         1.0326
  1.00 MB            90          90.91        1.0101         1.0102
  2.00 MB            45          90.68        1.0075         1.0081
  4.00 MB            23          90.90        1.0100         1.0101
 16.00 MB             6          93.11        1.0346         1.0319
 tek akis             1         108.14        1.2015         1.2038
```

Ölçülen oran bu koşumda geçerli bir **ölçüm**, beklenen oran koşumdan bağımsız bir **hesaptır**;
ikisi dört basamağın üçünde örtüşüyor. Yüzde 2 kısıtı eğrinin iki ucunu kesiyor: 256 KB'da
fazladan bayt yüzde 3,26 ve sebebi yeniden gönderim değil, 352 parçanın ek yüküdür; 16 MB'da yüzde
3,46 ve sebep yeniden gönderimdir. Kısıtı yalnız 1 MB ile 4 MB arası karşılıyor, en iyi nokta 2
MB'da ve kapalı çözümle aynı yerde. Tek akışta 90 MB için ortalama 108,14 MB gidiyor; bu oran nesne
büyüdükçe kötüleşir çünkü beklenen bayt nesne boyuyla üstel büyür.

**Duyarlılık:** ND7 iki katına çıkarsa en iyi parça boyu 2,86 MB'a, yarıya inerse 1,43 MB'a kayar;
ND7 dört kat değiştiğinde parça boyu iki kat. Karar ND7'ye zayıf bağlı, 2 MB geniş bir aralıkta
savunulabilir.

#### Tasarım

Yükleme yolu **vale anahtarı kalıbına** dayanır (Dayanıklılık ve Güvenilirlik kursunun Dağıtık
Doğruluk konusu). Servis baytları taşımaz; istemciye tek bir parçaya yazma yetkisi veren süreli bir
anahtar döner. Anahtarın kapsamı bir yükleme kimliği ile bir parça numarası, ömrü 60 saniyedir — en
yavaş uçta 2 MB'lık parça 16 saniye sürdüğü için 3,75 kat pay kalır. Bu seçim 2455,20 Mbit/s
yükleme trafiğini servis düğümlerinin dışında tutar. Parça yazma **etkisizdir** (aynı konu, Etkisiz
İşlemler); tekillik anahtarı aynı ikilidir, dolayısıyla istemci hangi parçanın gittiğinden emin
olmadan gönderebilir.

Üstveri deposu **anahtar–değer deposudur** (Veri Katmanı Ölçekleme kursunun Veri Dağıtımı konusu,
Depo Türleri) ve nesne anahtarının karmasına göre **parçalanır** (aynı konu, Parçalama). Aralık
değil karma seçilir: ortak önek altında toplanan anahtarlar aralık bölümlemesinde tek parçayı
ısıtır. Yarım kalan yüklemelerin parçalarını **zamanlayıcı–aracı–denetçi** kalıbı toplar (Uygulama
Katmanı ve Servis Etkileşimi kursunun Kuyruklar ve İş Akışları konusu); penceresi 7 gün.

**Bilerek kullanılmayan kalıp: önbellek.** Veri Katmanı Ölçekleme kursunun Önbellek Mimarisi
konusundaki yanında okuma buraya konmaz: bir nesne ömrü boyunca yalnız 4 kez okunuyor (ND5) ve
günlük 8,84 TB'ın kesrini tutacak katmanın maliyeti dört okumaya değmez. İkincisi **oturum
yapışkanlığıdır** (Trafik Katmanı kursunun Yük Dengeleme konusu): parça yazmaları durumsuzdur.

#### Elenen Alternatif: Her Nesne Üç Kopya

En basit tasarım her nesneyi üç kopya tutmaktır. Alternatif **silme kodlamasıdır** (erasure
coding): nesne k veri parçasına bölünür, m eşlik parçası hesaplanır, k+m parçadan herhangi k tanesi
nesneyi geri verir.

```js
// dayaniklilik.mjs — kopya sayisi ile silme kodlamasinin saklama, kayip ve istek maliyeti.
// Kayiplar bagimsiz varsayilir (ND9); bu bir model kabuludur, olcum degil.
let P = 0.01;                                             // ND9
const N = 1_200_000 * 365, KUCUK = 180e3, EK = 8192;      // ND1xND10, ND3, ND8
const TEPE = (1_200_000 * 4 / 86_400) * 3 * 0.92;         // nesne-olcek.mjs: kucuk nesne okuma/s
const bin = (n, i) => { let t = 1; for (let j = 0; j < i; j += 1) t = (t * (n - j)) / (j + 1); return t; };
const kayip = (k, m) => {
  if (m === 0) return P ** k;                             // k kopyanin hepsi kaybolur
  let t = 0;                                              // k+m parcadan m+1 ve fazlasi kaybolur
  for (let i = m + 1; i <= k + m; i += 1) t += bin(k + m, i) * P ** i * (1 - P) ** (k + m - i);
  return t;
};

console.log(`ND9 = ${P}, saklanan nesne ${N.toLocaleString("tr-TR")}\n\n` + "kurulum".padEnd(14) +
  "saklama kati".padStart(13) + "kaybolan nesne/yil".padStart(19) + "okuma istegi".padStart(14) +
  "180 KB'de ek yuk".padStart(18));
for (const [ad, k, m] of [["3 kopya", 3, 0], ["silme (6,3)", 6, 3], ["silme (10,4)", 10, 4],
  ["silme (12,4)", 12, 4]])
  console.log(ad.padEnd(14) + (m === 0 ? k : (k + m) / k).toFixed(2).padStart(13) +
    (kayip(k, m) * N).toFixed(2).padStart(19) + String(m === 0 ? 1 : k).padStart(14) +
    `%${((((m === 0 ? k : k + m) * EK) / KUCUK) * 100).toFixed(1)}`.padStart(18));

const HAM = 3.23, BUYUK_PAY = 0.9775;                     // nesne-olcek.mjs
const karma = BUYUK_PAY * 1.4 + (1 - BUYUK_PAY) * 3;      // buyuk silme (10,4), kucuk 3 kopya
// GB-ay: dogrusal buyumede yil ortalamasi yil sonunun yarisi, 12 ayla carpilir -> x6
console.log(`\nyil sonu saklama: hepsi silme (10,4) ${(HAM * 1.4).toFixed(2)} PB, karma ` +
  `(kat ${karma.toFixed(3)}) ${(HAM * karma).toFixed(2)} PB, hepsi 3 kopya ${(HAM * 3).toFixed(2)} PB`);
console.log(`karmanin fazlasi ${(HAM * (karma - 1.4) * 6e6).toFixed(0)} GB-ay; kucuk nesnede tepe ` +
  `parca istegi/s 3 kopya ${TEPE.toFixed(2)}, silme (10,4) ${(TEPE * 10).toFixed(2)}\n`);
for (const p of [0.005, 0.01, 0.02, 0.04]) {
  P = p;
  console.log(`ND9=${p} -> 3 kopya ${kayip(3, 0).toExponential(2)}, silme (10,4) ` +
    `${kayip(10, 4).toExponential(2)}, oran ${(kayip(3, 0) / kayip(10, 4)).toFixed(2)}`);
}
```

```
ND9 = 0.01, saklanan nesne 438.000.000

kurulum        saklama kati kaybolan nesne/yil  okuma istegi  180 KB'de ek yuk
3 kopya                3.00             438.00             1             %13.7
silme (6,3)            1.50             530.17             6             %41.0
silme (10,4)           1.40              81.33            10             %63.7
silme (12,4)           1.33             174.51            12             %72.8

yil sonu saklama: hepsi silme (10,4) 4.52 PB, karma (kat 1.436) 4.64 PB, hepsi 3 kopya 9.69 PB
karmanin fazlasi 697680 GB-ay; kucuk nesnede tepe parca istegi/s 3 kopya 153.33, silme (10,4) 1533.33

ND9=0.005 -> 3 kopya 1.25e-7, silme (10,4) 6.03e-9, oran 20.74
ND9=0.01 -> 3 kopya 1.00e-6, silme (10,4) 1.86e-7, oran 5.39
ND9=0.02 -> 3 kopya 8.00e-6, silme (10,4) 5.51e-6, oran 1.45
ND9=0.04 -> 3 kopya 6.40e-5, silme (10,4) 1.51e-4, oran 0.42
```

Üç kopya iki sayıda birden eleniyor. Saklama kısıtı 1,50 katken üç kopyanın katı 3,00; silme
(10,4) 1,40 ile altında kalıyor ve dayanıklılıkta da kazanıyor: yılda 438 nesne yerine 81,33.
Silme (6,3) yıllık kaybı 530,17 ile "500'ün altında" kısıtını çiğniyor; silme (12,4) saklamada en
ucuzu ama kaybı (10,4)'ün iki katından fazla.

Alternatifin kazandığı yer küçük nesnededir. Silme (10,4) 180 KB'lık nesneyi 18 KB'lık on dört
parçaya bölüyor: ek yük nesnenin yüzde 63,7'sine çıkıyor, okuma on istek gerektiriyor — tepe uçta
153,33 yerine 1533,33 parça isteği, yani "tek istekle karşılanır" kısıtının çiğnenmesi. Seçim bu
yüzden karmadır: küçük nesne üç kopya, büyük nesne silme (10,4), kat 1,436.

**Hangi kısıt değişirse alternatif kazanır:** ND9 binde beşe inerse kodlamanın üstünlüğü 20,74 kata
çıkar; yüzde 2'de 1,45'e, yüzde 4'te 0,42'ye düşer — düğüm kaybı sıklaştığında üç kopya öne geçer,
çünkü on dört parçanın üçünü değil kopyaların tamamının kaybını gerektirir. Bağımsız kayıp bir
model kabulüdür; kayıplar ilişkiliyse iki sütun da iyimserdir.

#### Arıza Davranışı ve Feda Edilen

Üstveri deposunun bir parçası düştüğünde o karma aralığında yeni yükleme başlatılamaz ve
birleştirme yapılamaz; ama sürmekte olan parça yazmaları vale anahtarıyla doğrudan depoya gittiği
için devam eder. Bu, **zarif bozulmadır** (Dayanıklılık ve Güvenilirlik kursunun Arıza Yalıtımı
konusu): yükleme durmaz, yalnız tamamlanamaz ve istemci 7 günlük pencere içinde tamamlayabilir. Bir
depolama düğümü kaybında silme (10,4) nesnesi 14 parçadan 13'üyle okunur; onarım k = 10 parça
okuduğu için trafiği nesne boyu kadardır. Parça isteği tepe hızı 188,33/s olduğundan yükleme yoluna
**kısıtlama** (aynı konu) bu hızın üstünde bir eşikle konur.

**Feda edilen:** küçük nesnelerde saklama katı 3'te bırakıldı. Her şey kodlansaydı yıl sonu 4,52
PB olurdu, karma seçim 4,64 PB: yılda 697.680 GB-ay fazladan saklama — küçük nesne okumasını
1533,33 yerine 153,33 parça isteğinde tutmanın bedeli.

#### Özet

- Nesnelerin yüzde 8'i baytların yüzde 98'ini taşıyor; iki boy sınıfı ayrı tasarlanır ve parçalı
  yükleme istek sayısını 4,52 katına çıkarır: tepe uçta 188,33 parça isteği.
- Parça boyu bir çukurun dibinden seçilir: fazladan bayt oranı 256 KB'da 1,0326, 2 MB'da 1,0075,
  16 MB'da 1,0346, tek akışta 1,2015 ve en iyi nokta karekök(2 × ek yük × kesinti aralığı)
  formülüyle aynı yerde.
- Üç kopya iki sayıda birden elenir: saklama katı 3,00'e karşı 1,40, yıllık kayıp 438'e karşı
  81,33 nesne; ama küçük nesnede kodlama okumayı 153,33'ten 1533,33 isteğe çıkardığı için orada üç
  kopya kazanır.
- Seçim ND9'a bağlıdır: kayıp olasılığı yüzde 4'e çıkarsa oran 0,42 olur, üç kopya öne geçer.

#### Sonraki Adım

Bu vakada nesne anlamsız bir bayt dizisiydi: yazıldığı gibi saklandı, okunduğu gibi verildi,
içeriğine bakılmadı. Sonraki vaka bu varsayımı kaldırır: yüklenen dosyanın birden çok
temsili üretilir ve hangisinin verileceği isteği yapanın o andaki koşullarına göre belirlenir. Soru
şuna dönüşür: aynı içerik kaç biçimde tutulur, bunları üretmenin işlemci maliyeti ile sakladıkları
bayt nasıl karşılaştırılır ve fazladan bir biçim ne zaman israftır.

### Video Yayın Servisi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/video-yayin-servisi

Aynı içeriğin birden çok temsilinin tutulduğu vaka: kodlama boru hattının işlemci-saniye ile bütçelenmesi, kalite basamaklarının izleyici bant genişliği dağılımı üzerinde ölçülmesi, basamak başına kazanılan bant kullanımının azalması ve üç ayrı kısıtın üç ayrı merdiveni elemesi.

Önceki vakada nesne, servis için anlamsız bir bayt dizisiydi: yazıldığı gibi saklandı, okunduğu
gibi verildi. Bu vaka o varsayımı kaldırır. Yüklenen dosya bir kez saklanmaz; içeriğinden birden
çok temsil üretilir ve hangi temsilin verileceği isteği yapanın o andaki bandına göre belirlenir.
Yeni kalem **kodlama boru hattıdır** (transcoding pipeline) ve yeni ödünleşim **uyarlanabilir
akışın** (adaptive streaming) kaç **kalite basamağı** (bitrate ladder) taşıyacağıdır.

Ölçülecek karar şu: her basamak hem saklanan bayta hem de işlemci-saniyeye mal olur, ama karşılığı
izleyicinin bandına daha iyi uyan bir akıştır. Basamak sayısı bu iki eğrinin kesiştiği yerde
seçilir ve kesişme noktası izleyici bandının dağılımına bağlıdır.

#### Kısıtlar

İşlevsel gereksinim: yüklenen kaynak kopyadan birden çok kalite basamağı üretme, her basamağı
sabit süreli parçalara bölme, parça listesini istemciye verme ve istemcinin basamaklar arasında
geçmesine izin verme.

İşlevsel olmayan gereksinim, sayıyla: bandı en düşük basamağı taşımadığı için oynatamayan izleyici
oranı yüzde **10**'un altında kalır; saklanan bayt kaynak kopyanın **2,5** katını geçmez; kodlamaya
sürekli ayrılan işlemci **1500**'ü geçmez; bir saatlik videonun yüklemeden izlenebilir olmaya
geçmesi **10** dakikayı aşmaz.

Kapsam daraltması: içerik koruma, canlı yayın, öneri, altyazı üretimi ve reklam yerleştirme
tasarlanmaz.

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| VY1 | günlük yüklenen video saati | 4.000 | yükleyenlerin günlük toplamı |
| VY2 | kaynak kopyanın bit hızı | 20.000 kbit/s | kodlanmamış yükleme |
| VY3 | günlük izlenen saat | 900.000 | izleme yüklemeden çok daha yoğun |
| VY4 | tepe çarpanı | 3 | tepe saatin gün ortalamasına oranı |
| VY5 | saklama süresi | 365 gün | katalog bir yıl tutulur |
| VY6 | kodlama maliyeti | 0,4 + 0,25 × Mbit/s | video-saniye başına işlemci-saniye |
| VY7 | izleyici bandı dağılımı | 0,25/1,5 + 0,50/6 + 0,25/20 | üç sınıflı karışım, üstel, Mbit/s |
| VY8 | bandın kullanılabilir payı | 0,8 | protokol yükü ve dalgalanma payı |
| VY9 | parça süresi | 4 s | basamak değişimi bu tanecikte olur |
| VY10 | kenar önbelleği isabet oranı | 0,95 | parçalar değişmez, uzun tutulur |

VY6 ve VY7 bu tasarımın iki taşıyıcı varsayımıdır: biri üretim maliyetini, öteki isabeti
belirliyor. İkisi de gerekçelidir, ölçüm değildir.

#### Ölçek

```js
// video-olcek.mjs — VY tablosundan cikan olcek hesabi; hepsi aritmetiktir
const VY = { yuklenenSaat: 4000, kaynakKbit: 20_000, izlenenSaat: 900_000, saklamaGun: 365,
  sabitCpu: 0.4, kbitBasinaCpu: 0.25, parcaSn: 4 };
const BASAMAK = [400, 1200, 3000, 6000, 12_000];          // secilen kalite basamaklari, kbit/s
const toplam = BASAMAK.reduce((a, b) => a + b, 0);
const cpu = BASAMAK.reduce((a, k) => a + VY.sabitCpu + VY.kbitBasinaCpu * (k / 1000), 0);
const videoSn = VY.yuklenenSaat * 3600;
for (const [ad, d] of [
  ["izlenen / yuklenen saat", VY.izlenenSaat / VY.yuklenenSaat],
  ["basamak toplami kbit/s", toplam],
  ["kodlama islemci-sn / video-sn", cpu],
  ["gunluk saklanan TB", (videoSn * (VY.kaynakKbit + toplam) * 1000) / 8 / 1e12],
  ["yil sonu saklanan PB", (videoSn * (VY.kaynakKbit + toplam) * 1000 * VY.saklamaGun) / 8 / 1e15],
  ["gunluk uretilen parca", (videoSn / VY.parcaSn) * BASAMAK.length],
]) console.log(ad.padEnd(30) + d.toFixed(2).padStart(14));

console.log(`\nbir saatlik video ${(3600 * cpu).toFixed(0)} islemci-sn ister:`);
for (const n of [30, 60, 120])
  console.log(`  ${n} isci -> yukleme-izlenebilirlik ${((3600 * cpu) / n / 60).toFixed(2)} dk`);
```

```
izlenen / yuklenen saat               225.00
basamak toplami kbit/s              22600.00
kodlama islemci-sn / video-sn           7.65
gunluk saklanan TB                     76.68
yil sonu saklanan PB                   27.99
gunluk uretilen parca            18000000.00

bir saatlik video 27540 islemci-sn ister:
  30 isci -> yukleme-izlenebilirlik 15.30 dk
  60 isci -> yukleme-izlenebilirlik 7.65 dk
  120 isci -> yukleme-izlenebilirlik 3.83 dk
```

Bu sayılar **hesap** sınıfındadır. Üçü tasarımı belirliyor. Her yüklenen saat 225 saat izleniyor:
bir kez ödenen kodlama maliyeti iki yüz yirmi beş kez okunuyor, bu yüzden içeriği izleme anında
değil yükleme anında hazırlamak baştan makul görünüyor. Günde 18.000.000 parça üretiliyor —
dağıtım katmanının gördüğü nesne sayısı budur, video sayısı değil. Ve son satır bir kısıtı doğrudan
karşılıyor: 10 dakikalık gecikme kısıtı en az 46 işçi ister; 30 işçiyle 15,30 dakikaya çıkıp
kısıt çiğneniyor, 60 işçiyle 7,65 dakika kalıyor.

#### Basamak Seçimi

Basamak sayısı ancak izleyici bandının dağılımı verildiğinde savunulabilir. Aşağıdaki model bir ağ
kurmaz, kodlayıcı çalıştırmaz: VY7'nin üç sınıflı karışımından bant çeker, her izleyicinin
kullanılabilir bandına sığan en yüksek basamağı seçer ve dört merdiveni aynı izleyici kümesi
üzerinde karşılaştırır.

```js
// basamak.mjs — surec ici model: izleyici bant genisligi dagilimindan basamak seciminin
// isabeti. Ag yok, kodlayici yok; dagilim uc sinifli bir karisimdir ve model oldugu yazilir.
const IZLEYICI = 200_000, TOHUM = 20260731, PAY = 0.8;       // VY8: bandin kullanilabilir payi
const SINIF = [[0.25, 1500], [0.50, 6000], [0.25, 20_000]];  // VY7: pay, ortalama kbit/s
const KAYNAK = 20_000, SABIT = 0.4, KBIT_CPU = 0.25;         // VY2, VY6
const IZLENEN_SAAT = 900_000, TEPE = 3, ISABET = 0.95;       // VY3, VY4, VY10
const YUKLENEN_SN = 4000 * 3600;                             // VY1: gunluk kodlanan video-saniye

let durum = TOHUM;
const rast = () => { durum = (durum * 1103515245 + 12345) % 2147483648; return durum / 2147483648; };
function bant() {                                            // sinif sec, ustel dagilimdan cek
  let u = rast(), i = 0;
  while (i < SINIF.length - 1 && u > SINIF[i][0]) { u -= SINIF[i][0]; i += 1; }
  return -Math.log(1 - rast()) * SINIF[i][1];
}
const bantlar = Array.from({ length: IZLEYICI }, bant);

const MERDIVEN = { "1 basamak": [3000], "3 basamak": [800, 3000, 9000],
  "5 basamak": [400, 1200, 3000, 6000, 12_000],
  "8 basamak": [300, 600, 1000, 1800, 3000, 5000, 8000, 12_000] };

function olc(m) {
  let oynamayan = 0, secilenToplam = 0, kullanimToplam = 0;
  for (const b of bantlar) {
    const kullanilabilir = b * PAY;
    const s = m.filter((k) => k <= kullanilabilir).pop();
    if (s === undefined) { oynamayan += 1; continue; }
    secilenToplam += s; kullanimToplam += s / kullanilabilir;
  }
  const oynayan = IZLEYICI - oynamayan;
  const toplamKbit = m.reduce((a, b) => a + b, 0);
  return { oynamayan: oynamayan / IZLEYICI, ortSecilen: secilenToplam / oynayan,
    kullanim: kullanimToplam / oynayan, saklama: (KAYNAK + toplamKbit) / KAYNAK,
    cpu: m.reduce((a, k) => a + SABIT + KBIT_CPU * (k / 1000), 0) };
}

console.log(`model: ${IZLEYICI} izleyici, tohum ${TOHUM}, sinif paylari ` +
  `${SINIF.map(([p, o]) => `${p}/${o}`).join(" ")} kbit/s (ustel)\n`);
console.log("merdiven".padEnd(11) + "oynamayan".padStart(11) + "ort secilen".padStart(13) +
  "bant kullanimi".padStart(16) + "saklama kati".padStart(14) + "surekli islemci".padStart(17) +
  "tepe uc Gbit/s".padStart(16) + "koken Gbit/s".padStart(14));
const R = {};
for (const [ad, m] of Object.entries(MERDIVEN)) {
  const r = olc(m);
  R[ad] = r;
  const uc = (IZLENEN_SAAT * 3600 * r.ortSecilen * 1000 / 86_400) * TEPE / 1e9;
  console.log(ad.padEnd(11) + `%${(r.oynamayan * 100).toFixed(2)}`.padStart(11) +
    r.ortSecilen.toFixed(0).padStart(13) + `%${(r.kullanim * 100).toFixed(1)}`.padStart(16) +
    r.saklama.toFixed(2).padStart(14) + ((YUKLENEN_SN * r.cpu) / 86_400).toFixed(0).padStart(17) +
    uc.toFixed(2).padStart(16) + (uc * (1 - ISABET)).toFixed(2).padStart(14));
}

const k = Object.keys(MERDIVEN);
console.log("\nbasamak basina kazanilan bant kullanimi:");
for (let i = 1; i < k.length; i += 1) {
  const d = MERDIVEN[k[i]].length - MERDIVEN[k[i - 1]].length;
  console.log(`  ${k[i - 1]} -> ${k[i]}: ${(((R[k[i]].kullanim - R[k[i - 1]].kullanim) * 100) / d).toFixed(2)} puan/basamak`);
}
const kapali = olc([400, 1200, 3000, 6000]);                         // tepe basamak kapatilirsa
console.log(`tepe basamak kapali: ort secilen ${kapali.ortSecilen.toFixed(0)} kbit/s, uc cikisi ` +
  `x${(kapali.ortSecilen / R["5 basamak"].ortSecilen).toFixed(2)}, oynamayan ayni %${(kapali.oynamayan * 100).toFixed(2)}`);
const ozelCpu = IZLENEN_SAAT * 3600 * (SABIT + KBIT_CPU * 6);        // izleyici basina tek akis
const merdivenCpu = YUKLENEN_SN * R["5 basamak"].cpu;
console.log(`izleyiciye ozel kodlama: ${ozelCpu.toExponential(2)} islemci-sn/gun, 5 basamagin ` +
  `${(ozelCpu / merdivenCpu).toFixed(1)} kati (${(merdivenCpu).toExponential(2)})`);
```

```
model: 200000 izleyici, tohum 20260731, sinif paylari 0.25/1500 0.5/6000 0.25/20000 kbit/s (ustel)

merdiven     oynamayan  ort secilen  bant kullanimi  saklama kati  surekli islemci  tepe uc Gbit/s  koken Gbit/s
1 basamak       %45.75         3000           %43.2          1.15              192          337.50         16.88
3 basamak       %13.77         3846           %56.5          1.64              733          432.71         21.64
5 basamak        %8.91         4442           %64.9          2.13             1275          499.72         24.99
8 basamak        %6.08         4607           %73.8          2.58             1854          518.24         25.91

basamak basina kazanilan bant kullanimi:
  1 basamak -> 3 basamak: 6.66 puan/basamak
  3 basamak -> 5 basamak: 4.22 puan/basamak
  5 basamak -> 8 basamak: 2.96 puan/basamak
tepe basamak kapali: ort secilen 3242 kbit/s, uc cikisi x0.73, oynamayan ayni %8.91
izleyiciye ozel kodlama: 6.16e+9 islemci-sn/gun, 5 basamagin 55.9 kati (1.10e+8)
```

Tablodaki oranlar **ölçüm**, maliyet sütunları **hesap** sınıfındadır; ölçülen oranlar bu koşumun
tohumuna bağlıdır, kısıtları ayıran farklar ise tohumdan bağımsız büyüklüktedir. Basamak başına
kazanılan bant kullanımı 6,66'dan 4,22'ye, oradan 2,96 puana düşüyor: **her yeni basamak bir
öncekinden daha az kazandırıyor, ama maliyeti daha az düşmüyor.**

#### Tasarım

Ölçüm beş basamaklı merdiveni işaret ediyor; tasarım onun üstüne kurulur. Kodlama boru hattı bir
**mesaj kuyruğu** ve **rekabet eden tüketiciler** üzerine oturur (Uygulama Katmanı ve Servis
Etkileşimi kursunun Kuyruklar ve İş Akışları konusu). Parametre işçi sayısıdır:
60 işçi bir saatlik videoyu 7,65 dakikada izlenebilir yapar. Aynı konudaki **öncelikli kuyruk**
yeni yüklemeyi geri dolduran yeniden kodlama işlerinin önüne alır; bu, gecikme kısıtının yalnız yeni
yüklemeler için geçerli olmasını sağlar. İş iletisi baytları taşımaz, kaynak nesnenin anahtarını
taşır — **talep fişi kalıbı** (Dayanıklılık ve Güvenilirlik kursunun Dağıtık Doğruluk konusu).
Kodlama işi **etkisizdir** (aynı konu); tekillik anahtarı video kimliği ile basamağın ikilisidir,
böylece yarıda kalan iş yeniden sürüldüğünde ikinci bir çıktı üretmez.

Dağıtım tarafı **içerik dağıtım ağı** ve **kenar önbelleği** üzerine kurulur (Trafik Katmanı
kursunun Giriş Noktaları konusu). Parametre isabet oranıdır: VY10'un yüzde 95'i tepe uçtaki 499,72
Gbit/s'yi kökende 24,99 Gbit/s'ye indiriyor. Parçalar üretildikten sonra hiç değişmediği için
bayatlık penceresi diye bir soru yok; aynı konudaki **itme tabanlı dağıtım** yeni yüklenen içeriğin
ilk parçalarını izlenmeden önce kenara taşır, gerisi çekmeyle gelir. Parça listesi ve parçalar
statik dosyalardır (aynı konu, Statik İçerik Barındırma), bu yüzden okuma yolunda uygulama kodu yok.

**Bilerek kullanılmayan kalıp: devre kesici.** Dayanıklılık ve Güvenilirlik kursunun Arıza Yalıtımı
konusundaki devre kesici kodlama katmanına konmaz, çünkü işçiler senkron çağrı yapmaz; kuyruktan
çeker ve başarısız işi yeniden görünür kılar — kesilecek bir çağrı yolu yoktur. İkincisi **komut ve
sorgu sorumluluğu ayrımıdır** (Veri Katmanı Ölçekleme kursunun Okuma–Yazma Ayrımı konusu): okuma
yolu bir sorgu değil, adı önceden bilinen bir dosyanın getirilmesidir ve ayrı bir okuma modeli
türetmenin karşılığı yoktur.

#### Elenen Alternatifler

Üç alternatif üç ayrı sayıyla eleniyor ve her biri farklı bir kısıta takılıyor.

**Tek basamak** en ucuzudur: saklama katı 1,15, sürekli 192 işlemci. Ama izleyicilerin yüzde
45,75'i o tek basamağı taşıyamıyor ve hiç oynatamıyor — yüzde 10 kısıtının dört buçuk katı. Üç
basamak da yüzde 13,77 ile kısıtın üstünde kalıyor.

**Sekiz basamak** isabet tarafında en iyisidir: bant kullanımı yüzde 73,8, oynatamayan yüzde 6,08.
İki kısıtı birden çiğniyor: saklama katı 2,58 (sınır 2,5) ve sürekli 1854 işlemci (sınır 1500).
Kazandığı 8,9 puanlık kullanım, saklamada yüzde 21, işlemcide yüzde 45 fazla ödemeye mal oluyor.

**İzleyiciye özel kodlama** — her akışı izleyicinin bandına tam uyduracak biçimde anlık üretmek —
isabet sorununu tamamen çözer, çünkü kullanım yüzde 100 olur ve fazladan basamak saklanmaz. Günde
6,16 milyar işlemci-saniye ister; beş basamaklı merdivenin 110 milyon işlemci-saniyesinin 55,9
katı. Sebep ölçek hesabındaki ilk satırdır: kodlama yükleme başına bir kez değil, izleme başına bir
kez ödenir ve izleme yüklemenin 225 katıdır.

Geriye **beş basamak** kalıyor: oynatamayan yüzde 8,91, saklama katı 2,13, sürekli 1275 işlemci —
üç kısıt da karşılanıyor. **Hangi kısıt değişirse alternatif kazanır:** saklama sınırı 2,6'ya
çıkarılırsa sekiz basamak girer ve bant kullanımını 8,9 puan artırır; izleyici bandı dağılımı
daralırsa (üç sınıf birbirine yaklaşırsa) az basamak yeter ve üç basamak kısıtı karşılamaya başlar.

#### Arıza Davranışı ve Feda Edilen

Kodlama katmanı tümüyle düştüğünde katalog izlenmeye devam eder; yalnız yeni yüklemeler izlenebilir
olmaz ve kuyrukta birikir. Bu **zarif bozulmadır** (Arıza Yalıtımı konusu) ve maliyeti gecikme
kısıtının askıya alınmasıdır. Köken düştüğünde kenar önbelleği isteklerin yüzde 95'ini karşılamayı
sürdürür, çünkü parçalar değişmezdir ve bayat sunum diye bir risk yoktur. Çıkış tarafında tepe yük
sınırı zorladığında en üst basamak kapatılır: ortalama seçilen bit hızı 4442'den 3242 kbit/s'ye,
uçtaki çıkış 0,73 katına iner ve oynatamayan izleyici oranı yüzde 8,91'de değişmeden kalır — yani
bozulma kaliteyi düşürür, kimseyi dışarıda bırakmaz.

**Feda edilen:** bant kullanımı yüzde 64,9'da bırakıldı. İzleyicinin kullanılabilir bandının üçte
biri boşta duruyor ve bu, saklamayı 2,5 katın, kodlamayı 1500 işlemcinin altında tutmanın bedelidir.

#### Özet

- Yüklenen her saat 225 saat izleniyor; kodlama maliyetinin izleme anında değil yükleme anında
  ödenmesinin gerekçesi bu orandır ve izleyiciye özel kodlamayı 55,9 kat pahalı yapan da odur.
- Kalite basamaklarının isabeti ancak izleyici bandı dağılımıyla birlikte ölçülebilir: beş
  basamakta bant kullanımı yüzde 64,9, oynatamayan izleyici yüzde 8,91.
- Basamak başına kazanılan kullanım 6,66'dan 4,22'ye, oradan 2,96 puana düşerken maliyet basamak
  başına düşmüyor; merdivenin sonu bu yüzden vardır.
- Üç alternatif üç ayrı kısıtla elenir: tek basamak oynatamayan oranıyla (yüzde 45,75), sekiz
  basamak saklama (2,58) ve işlemci (1854) sınırlarıyla, izleyiciye özel kodlama işlemci-saniyeyle.
- Kenar önbelleği çıkışı 499,72 Gbit/s'den 24,99 Gbit/s'ye indiriyor ve parçalar değişmez olduğu
  için bayatlık penceresi tartışması hiç açılmıyor.

#### Sonraki Adım

Bu vakada da veri yazıldığı gibi saklandı: bir parça üretildi, bir yere kondu ve sonra aynen
okundu. Üretilen her bayt saklanmaya değerdi. Sonraki vaka bu varsayımı kaldırır. Orada kayıtlar
saniyede yüz binlerce üretilir, tek tek hiçbiri anlamlı değildir ve hepsini saklamak imkânsızdır.
Soru şuna dönüşür: hangi kayıt ne kadar süreyle ham tutulur, ne zaman toplulaştırılır ve
toplulaştırılmış veriden okunan yanıt ham veriden okunandan ne kadar sapar.

### Ölçüt Toplama Sistemi

Source: https://academia.sh/tr/kurslar/vaka-calismalari/olcut-toplama-sistemi

Tek tek hiçbir kaydın anlamlı olmadığı vaka: zaman serisi yazma yolunun örnek hızıyla bütçelenmesi, saklama basamaklarının toplam bayta etkisi ve seyreltmenin hangi soruyu bozmadan, hangisini yüzde yüz yanlış yanıtladığının gerçek bir motorda ölçülmesi.

Önceki iki vakada üretilen her bayt saklanmaya değerdi: bir nesne yazıldığı gibi tutuldu, bir video
parçası üretildikten sonra defalarca okundu. Bu vaka o varsayımı kaldırır. Burada kayıtlar saniyede
yüz binlerce üretilir, tek tek hiçbiri anlamlı değildir ve hepsini süresiz saklamak ne mümkündür ne
de gereklidir.

Sistem bir **ölçüt toplama** sistemidir: düğümler düzenli aralıklarla sayısal örnek gönderir,
örnekler **zaman serisi** olarak yazılır ve sorular hep bir zaman aralığı üzerinden sorulur.
Ölçülecek karar **seyreltme** (downsampling) ile **toplulaştırmanın** (rollup) ikili etkisidir:
saklanan bayt ve taranan satır düşer, ama toplulaştırılmış veriden okunan yanıt ham veriden okunana
eşit değildir. Bu fark soru türüne göre değişir ve tasarım o farkı bilerek seçer.

Hangi ölçütün toplanmaya değer olduğu bu vakanın konusu değil; o soru Başarım Karşıt Kalıpları ve
İzleme kursunun Enstrümantasyon Tasarımı dersinde kuruldu. Burada ölçüt kümesi verilidir ve tasarım
onu taşıma işidir.

#### Kısıtlar

İşlevsel gereksinim: örnek kabul etme, seri ve zaman aralığına göre okuma, seri üzerinde ortalama /
en büyük / yüzdelik / eşik aşımı sorularını yanıtlama ve saklama süresi dolan veriyi düşürme.

İşlevsel olmayan gereksinim, sayıyla: yazma yolu tepe **480.000** örnek/s'yi kayıpsız kabul eder;
toplam saklanan bayt **40** TB'ı geçmez; altı saatlik bir gösterge tablosu sorgusu seri başına
**400**'den az satır tarar; son **7** gün içinde yüzdelik ve eşik aşımı soruları **hatasız**
yanıtlanır.

Kapsam daraltması: uyarı kuralları, gösterge tablosu arayüzü, izleme kategorileri ve dağıtık iz
toplama tasarlanmaz — bunlar Başarım Karşıt Kalıpları ve İzleme kursunun konusudur.

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| OT1 | izlenen düğüm | 20.000 | işletilen süreçlerin toplamı |
| OT2 | düğüm başına seri | 120 | süreç, kaynak ve uç başına birkaç ölçüt |
| OT3 | örnekleme aralığı | 10 s | ajanın gönderme sıklığı |
| OT4 | ham örnek kaydı | 26 bayt | seri kimliği, zaman ve değer |
| OT5 | özet kaydı | 34 bayt | kova, adet, toplam ve en büyük |
| OT6 | tepe çarpanı | 2 | ölçüt akışı düzenlidir, tepesi sığdır |
| OT7 | ham saklama | 7 gün | olay incelemesi bir haftaya bakar |
| OT8 | bir dakikalık özet saklama | 90 gün | çeyrek dönem karşılaştırması |
| OT9 | beş dakikalık özet saklama | 730 gün | kapasite eğilimi iki yıl geriye gider |
| OT10 | gösterge tablosu aralığı | 6 saat | bir vardiyanın penceresi |

OT6 bu vakanın ayırt edici varsayımıdır: tepe çarpanı önceki vakalarda 3'tü, burada 2. Ölçüt akışı
kullanıcı davranışına değil ajanın zamanlayıcısına bağlıdır, bu yüzden tepesi sığdır.

#### Ölçek

```js
// olcut-olcek.mjs — OT tablosundan cikan olcek hesabi ve saklama basamaklarinin toplami
const OT = { dugum: 20_000, seriBasinaDugum: 120, aralikSn: 10, hamBayt: 26, ozetBayt: 34,
  tepe: 2, sorguAralikSaat: 6, hamGun: 7, dakikaGun: 90, besDakikaGun: 730 };
const seri = OT.dugum * OT.seriBasinaDugum;
const ornekSn = seri / OT.aralikSn;
const gunlukHam = ornekSn * 86_400 * OT.hamBayt;
const ozetGunluk = (tanecikSn) => (seri * (86_400 / tanecikSn)) * OT.ozetBayt;

for (const [ad, d] of [
  ["seri sayisi", seri],
  ["ortalama ornek/s", ornekSn],
  ["tepe ornek/s", ornekSn * OT.tepe],
  ["ham gunluk GB", gunlukHam / 1e9],
  ["1 dk ozet gunluk GB", ozetGunluk(60) / 1e9],
  ["5 dk ozet gunluk GB", ozetGunluk(300) / 1e9],
]) console.log(ad.padEnd(24) + d.toFixed(2).padStart(12));

const basamak = [["ham", gunlukHam, OT.hamGun], ["1 dk ozet", ozetGunluk(60), OT.dakikaGun],
  ["5 dk ozet", ozetGunluk(300), OT.besDakikaGun]];
let toplam = 0;
console.log(`\n${"basamak".padEnd(12)}${"gun".padStart(6)}${"saklanan TB".padStart(14)}`);
for (const [ad, gun, sure] of basamak) {
  toplam += (gun * sure) / 1e12;
  console.log(ad.padEnd(12) + String(sure).padStart(6) + ((gun * sure) / 1e12).toFixed(2).padStart(14));
}
const hepsiHam = (gunlukHam * OT.besDakikaGun) / 1e12;
console.log(`toplam ${toplam.toFixed(2)} TB; hepsi ham ve ${OT.besDakikaGun} gun tutulsaydi ` +
  `${hepsiHam.toFixed(2)} TB (x${(hepsiHam / toplam).toFixed(2)})`);
console.log(`${OT.sorguAralikSaat} saatlik sorgu seri basina okunan satir: ham ` +
  `${(OT.sorguAralikSaat * 3600) / OT.aralikSn}, 1 dk ${OT.sorguAralikSaat * 60}, ` +
  `5 dk ${(OT.sorguAralikSaat * 3600) / 300}`);
```

```
seri sayisi               2400000.00
ortalama ornek/s           240000.00
tepe ornek/s               480000.00
ham gunluk GB                 539.14
1 dk ozet gunluk GB           117.50
5 dk ozet gunluk GB            23.50

basamak        gun   saklanan TB
ham              7          3.77
1 dk ozet       90         10.58
5 dk ozet      730         17.16
toplam 31.50 TB; hepsi ham ve 730 gun tutulsaydi 393.57 TB (x12.49)
6 saatlik sorgu seri basina okunan satir: ham 2160, 1 dk 360, 5 dk 72
```

Bu sayılar **hesap** sınıfındadır. Üçü tasarımı belirliyor. Yazma yolu saniyede 240.000, tepede
480.000 örnek görüyor; bu, bir isteğin bir kayda karşılık geldiği önceki vakalardan iki mertebe
yüksektir ve yazma yolunun her adımının örnek başına maliyetini belirleyici yapar. Saklama
basamakları toplamı 31,50 TB ile 40 TB kısıtının altında; aynı veri ham tutulsaydı 393,57 TB, yani
12,49 katı olurdu ve kısıt çiğnenirdi. Altı saatlik sorgu bir dakikalık özetten 360 satır okuyor,
ham basamaktan 2160; 400 satır kısıtını yalnız özet basamakları karşılıyor.

#### Seyreltmenin Bedeli

Yukarıdaki tablo yalnız kazancı gösteriyor. Bedel yanıtın kendisindedir ve gerçek bir motorda
ölçülür. Aşağıdaki model bir küme kurmaz; tek bir serinin altı saatini `node:sqlite` içinde üretir,
iki tanecikte toplulaştırır ve aynı dört soruyu üç biçime birden sorar.

```js
// seyreltme.mjs — node:sqlite ile gercek bir motorda tek serinin ham ve toplulastirilmis
// bicimleri. Uretec kendi yazilmistir, tohum gorunur; olculen sey saklanan bayt, taranan satir
// ve ayni dort sorunun iki bicimden aldigi yanitin farkidir. Model oldugu yazilir.
import { DatabaseSync } from "node:sqlite";

const SURE = 21_600, TOHUM = 20260801, ESIK = 200;        // 6 saat, saniyede bir ornek
let durum = TOHUM;
const rast = () => { durum = (durum * 1103515245 + 12345) % 2147483648; return durum / 2147483648; };
const uret = (t) => {                                      // gunluk dalga + gurultu + ani sicrama
  const taban = 100 + 40 * Math.sin((2 * Math.PI * t) / 86_400) + (rast() - 0.5) * 30;
  return taban + (rast() < 0.002 ? 300 : 0);
};

const db = new DatabaseSync(":memory:");
db.exec(`CREATE TABLE ham(t INTEGER PRIMARY KEY, deger REAL);
CREATE TABLE ozet60(kova INTEGER PRIMARY KEY, adet INTEGER, toplam REAL, enBuyuk REAL);
CREATE TABLE ozet300(kova INTEGER PRIMARY KEY, adet INTEGER, toplam REAL, enBuyuk REAL);`);
db.exec("BEGIN");
const ekle = db.prepare("INSERT INTO ham VALUES(?,?)");
for (let t = 0; t < SURE; t += 1) ekle.run(t, uret(t));
db.exec("COMMIT");
for (const g of [60, 300]) db.exec(`INSERT INTO ozet${g}
  SELECT t / ${g}, count(*), sum(deger), max(deger) FROM ham GROUP BY t / ${g}`);

const yuzdelik = (a, p) => { const s = [...a].sort((x, y) => x - y); return s[Math.floor(s.length * p)]; };
const hamDeger = db.prepare("SELECT deger FROM ham").all().map((r) => r.deger);
const bayt = (t) => db.prepare("SELECT sum(pgsize) AS b FROM dbstat WHERE name = ?").get(t).b;
const satir = (t) => db.prepare(`SELECT count(*) AS n FROM ${t}`).get().n;

const HAM = { ort: hamDeger.reduce((a, b) => a + b) / SURE, enBuyuk: Math.max(...hamDeger),
  p95: yuzdelik(hamDeger, 0.95), asan: hamDeger.filter((d) => d > ESIK).length };
console.log(`model: ${SURE} ornek (saniyede bir), tohum ${TOHUM}, esik ${ESIK}\n`);
console.log("bicim".padEnd(10) + "satir".padStart(7) + "bayt".padStart(9) + "bayt/satir".padStart(12) +
  "ortalama".padStart(10) + "en buyuk".padStart(10) + "p95".padStart(9) + "esik ustu sn".padStart(14));
const yaz = (ad, n, b, o, e, p, a) => console.log(ad.padEnd(10) + String(n).padStart(7) +
  String(b).padStart(9) + (b / n).toFixed(1).padStart(12) + o.toFixed(2).padStart(10) +
  e.toFixed(2).padStart(10) + p.toFixed(2).padStart(9) + String(a).padStart(14));
yaz("ham", satir("ham"), bayt("ham"), HAM.ort, HAM.enBuyuk, HAM.p95, HAM.asan);

const OZET = {};
for (const g of [60, 300]) {
  const k = db.prepare(`SELECT adet, toplam, enBuyuk FROM ozet${g}`).all();
  const kovaOrt = k.map((r) => r.toplam / r.adet);
  const o = { ort: k.reduce((a, r) => a + r.toplam, 0) / SURE, enBuyuk: Math.max(...k.map((r) => r.enBuyuk)),
    p95: yuzdelik(kovaOrt, 0.95), asan: kovaOrt.filter((d) => d > ESIK).length * g };
  OZET[g] = o;
  yaz(`${g} sn ozet`, satir(`ozet${g}`), bayt(`ozet${g}`), o.ort, o.enBuyuk, o.p95, o.asan);
}

console.log(`\nham yanittan sapma (yuzde):`);
for (const g of [60, 300]) {
  const s = (a, b) => (((a - b) / b) * 100).toFixed(2);
  console.log(`  ${g} sn ozet -> ortalama ${s(OZET[g].ort, HAM.ort)}, en buyuk ` +
    `${s(OZET[g].enBuyuk, HAM.enBuyuk)}, p95 ${s(OZET[g].p95, HAM.p95)}, ` +
    `esik ustu sure ${s(OZET[g].asan, HAM.asan)}`);
}
for (const g of [60, 300]) {                                // ayni soru enBuyuk sutunuyla
  const n = db.prepare(`SELECT count(*) AS n FROM ozet${g} WHERE enBuyuk > ?`).get(ESIK).n;
  console.log(`  ${g} sn ozet, esik ustu sure enBuyuk ile: ${n * g} sn (ham ${HAM.asan} sn, ` +
    `x${((n * g) / HAM.asan).toFixed(1)})`);
}
console.log(`taranan satir orani: 60 sn ozet 1/${SURE / satir("ozet60")}, ` +
  `300 sn ozet 1/${SURE / satir("ozet300")}; bayt orani 1/` +
  `${(bayt("ham") / bayt("ozet60")).toFixed(1)} ve 1/${(bayt("ham") / bayt("ozet300")).toFixed(1)}`);
```

```
model: 21600 ornek (saniyede bir), tohum 20260801, esik 200

bicim       satir     bayt  bayt/satir  ortalama  en buyuk      p95  esik ustu sn
ham         21600   356352        16.5    125.84    450.17   148.84            41
60 sn ozet    360    16384        45.5    125.84    450.17   140.74             0
300 sn ozet     72     4096        56.9    125.84    450.17   139.88             0

ham yanittan sapma (yuzde):
  60 sn ozet -> ortalama 0.00, en buyuk 0.00, p95 -5.44, esik ustu sure -100.00
  300 sn ozet -> ortalama 0.00, en buyuk 0.00, p95 -6.02, esik ustu sure -100.00
  60 sn ozet, esik ustu sure enBuyuk ile: 2400 sn (ham 41 sn, x58.5)
  300 sn ozet, esik ustu sure enBuyuk ile: 9600 sn (ham 41 sn, x234.1)
taranan satir orani: 60 sn ozet 1/60, 300 sn ozet 1/300; bayt orani 1/21.8 ve 1/87.0
```

Bu sayılar **ölçüm** sınıfındadır; değerler bu koşumun tohumuna bağlıdır, sapmaların işareti ve
mertebesi bağlı değildir. Bayt sütunu sayfa taneciğine bağlı olduğu için az satırlı tabloda satır
başına bayt büyük görünüyor; kıyas için toplam bayt oranına bakılır.

Dört soru üç ayrı davranış gösteriyor. **Ortalama ve en büyük hiç bozulmuyor:** sapma her iki
tanecikte de yüzde 0,00, çünkü özet satırı toplamı, adedi ve en büyüğü taşıyor — bu üçünden
ortalama ile en büyük tam olarak yeniden kurulabiliyor. **Yüzdelik bozuluyor ama az:** p95 bir
dakikalık tanecikte yüzde 5,44, beş dakikalıkta yüzde 6,02 düşük çıkıyor, çünkü kova ortalaması
kova içindeki dağılımı düzleştiriyor. **Eşik aşımı tamamen kayboluyor:** kova ortalamasıyla
bakıldığında 41 saniyelik aşım 0 saniye görünüyor, yani yüzde 100 hata.

Aynı soruyu en büyük sütunuyla sormak da işe yaramıyor, ters yöne kaçırıyor: bir dakikalık
tanecikte 2400 saniye, beş dakikalıkta 9600 saniye çıkıyor — ham yanıtın 58,5 ve 234,1 katı, çünkü
kovanın herhangi bir saniyesindeki aşım bütün kovayı aşmış sayıyor. **Bir özet satırı, taşıdığı
sütunların yanıtladığı soruları yanıtlar; ötekiler için alt sınır ile üst sınır arasında bir
belirsizlik bırakır.**

#### Tasarım

Yazma yolu bir **ileti aracısı** ve **rekabet eden tüketiciler** üzerine kurulur (Uygulama Katmanı
ve Servis Etkileşimi kursunun Kuyruklar ve İş Akışları konusu); aynı konudaki **kuyruk tabanlı yük
dengeleme** tepedeki 480.000 örnek/s'yi yazıcıların gördüğü sabit bir hıza düzleştirir. Ajan
tarafında **geri basınç** (Dayanıklılık ve Güvenilirlik kursunun Arıza Yalıtımı konusu) tampon
doluluğuyla ölçülür ve tampon dolduğunda en eski örnek atılır.

Depo bir **geniş sütun deposudur** (Veri Katmanı Ölçekleme kursunun Veri Dağıtımı konusu, Depo
Türleri): satır anahtarı seri kimliği, sütunlar zaman sıralıdır. **Parçalama** anahtarı seri
kimliğidir, düğüm adı değil — düğüm adına göre parçalansaydı 120 serisi tek parçaya düşen bir düğüm
sıcak parça yaratırdı. **Bölümleme** zamana göredir (aynı konu, Bölümleme Stratejileri) ve saklama
politikası bölüm düşürmeyle uygulanır: 7 günden eski ham bölüm tek işlemde silinir, satır satır
değil.

Toplulaştırma iki **somutlaştırılmış görünümdür** (aynı konu): tazeleme artımlıdır ve yalnız
kapanan kovayı yazar. Parametreler taneciklerdir — 60 saniye ve 300 saniye — ve gerekçeleri OT8 ile
OT9'un saklama süreleridir. Kapasite tahmini için okunan basamak beş dakikalıktır; zaman serisinden
kaynak tahminine geçişin yöntemi Başarım Karşıt Kalıpları ve İzleme kursunun Kapasite Planlama
dersinde kuruldu ve burada tekrarlanmaz, yalnız girdisi bu basamaktan gelir.

**Bilerek kullanılmayan kalıp: tekillik anahtarı defteri.** Dayanıklılık ve Güvenilirlik kursunun
Etkisiz İşlemler dersindeki defter buraya konmaz, çünkü örnek zaten (seri, zaman) çiftiyle
adreslenir ve yinelenen yazma aynı hücrenin üstüne aynı değeri yazar; ayrı bir defter örnek başına
26 baytlık kaydın yanına aynı mertebede bir kayıt daha ekler ve saniyede 240.000 kez ödenirdi.
İkincisi **federasyondur** (Veri Dağıtımı konusu): bölünecek ayrı bir işlevsel alan yok, bütün veri
tek biçimde ve tek erişim örüntüsüne sahip.

#### Elenen Alternatifler

**Her şeyi ham tutmak** yüzdelik ve eşik sorularını her tarihte hatasız yanıtlar. 393,57 TB ister,
40 TB kısıtının on katı; ayrıca altı saatlik sorgu seri başına 2160 satır tarar ve 400 satır
kısıtını da çiğner. İki kısıtla birden eleniyor.

**Her şeyi beş dakikalık özet tutmak** en ucuzudur: toplam 17,16 TB ve sorgu başına 72 satır. Eşik
aşımı sorusunu yüzde 100 hatayla (0 saniye) ya da 234,1 kat şişirerek (9600 saniye) yanıtlar,
yüzdelikte 6,02 puan sapar. "Son 7 gün içinde hatasız" kısıtıyla eleniyor.

Seçilen üç basamaklı düzen 31,50 TB'da kalıyor, sorgu başına 360 satır tarıyor ve son yedi günde
ham veri durduğu için hassas soruları hatasız yanıtlıyor. **Hangi kısıt değişirse alternatif
kazanır:** eşik aşımı ve yüzdelik soruları hiç sorulmuyorsa ham basamağın varlık nedeni kalmaz, 3,77
TB'lik kalem düşer ve yazma yolu doğrudan özete yazabilir; tersine, olay incelemesi bir haftadan
eskiye bakıyorsa ham saklama uzatılır ve toplam hızla 40 TB'ı zorlar.

#### Arıza Davranışı ve Feda Edilen

İleti aracısı düştüğünde ajanlar tamponlarında biriktirir; tampon dolduğunda en eski örneği atmak
**zarif bozulmadır** (Arıza Yalıtımı konusu), çünkü seri kesintisiz kalır ama tanecik geçici olarak
kabalaşır — ortalama ve en büyük sorusu yanıtlanmaya devam eder, eşik aşımı yanıtlanamaz. İşin
tersi de olur: toplulaştırma işi geri kalırsa gösterge tablosu ham basamaktan okur ve seri başına
taranan satır 360'tan 2160'a, yani altı kat artar. Bir parça düğümü düştüğünde yalnız o parçadaki
serilerin okuması etkilenir; sorular seri bazında sorulduğu için bulaşma kapsamı bir parçadır.

**Feda edilen:** yedi günden eski veride yüzdelik ve eşik aşımı soruları güvenilir yanıt almaz. Bir
ay önceki bir dalgalanmanın kaç saniye sürdüğü bu tasarımda sorulamaz; alınabilecek yanıt ya 0 ya
da gerçeğin 58,5 katıdır.

#### Özet

- Ölçüt akışı örnek başına maliyeti belirleyici yapar: 2.400.000 seri, saniyede 240.000 örnek,
  tepede 480.000; tepe çarpanı önceki vakalardaki 3 değil 2, çünkü akış ajanın zamanlayıcısına
  bağlıdır.
- Saklama basamakları toplamı 31,50 TB; aynı veri ham tutulsaydı 393,57 TB, yani 12,49 katı.
- Toplulaştırma taranan satırı 60 ve 300 kat, saklanan baytı 21,8 ve 87,0 kat düşürüyor.
- Sapma soru türüne bağlıdır: ortalama ve en büyük yüzde 0,00, p95 yüzde 5,44 ile 6,02, eşik aşımı
  süresi yüzde 100 — aynı soru en büyük sütunuyla sorulduğunda 58,5 ve 234,1 kat şişiyor.
- Bu yüzden ham basamak bir başarım kalemi değil, bir **yanıtlanabilirlik** kalemidir: yedi günlük
  ham veri 3,77 TB'a mal olur ve karşılığında hassas soruların yanıtlanabildiği pencereyi tanımlar.

#### Sonraki Adım

Bu vakada bir kaydı bulmak kolaydı: seri kimliği ile zaman aralığı verildiğinde okunacak satırlar
tek bir yerde ve sıralı duruyordu. Sonraki vaka bu kolaylığı kaldırır. Orada kayıtlar iki boyutlu
bir düzlemde durur ve soru "şu noktaya yakın olanlar" biçiminde gelir; yakınlığın sıralı bir
anahtarı yoktur, çünkü iki boyutu tek bir eksende sıralamanın her yolu bazı komşuları birbirinden
uzağa düşürür. Soru şuna dönüşür: böyle bir sorguyu yanıtlamak için kaç aday kayda bakmak gerekir
ve bu sayı seçilen hücre boyuna nasıl bağlıdır.

### Konum Tabanlı Servis

Source: https://academia.sh/tr/kurslar/vaka-calismalari/konum-tabanli-servis

Sıralanabilir bir anahtarı olmayan sorgunun vakası: yakınlık sorgusunun düz taramayla karşılaştırılması, uzamsal anahtarın taradığı aday sayısının gerçek veri üzerinde ölçülmesi, hücre boyunun aday sayısı ile dizin araması arasındaki ödünleşimi belirlemesi ve komşu hücre sorgulanmadığında kaçırılan sonucun sayılması.

Önceki vakada bir kaydı bulmak kolaydı: seri kimliği ile zaman aralığı verildiğinde okunacak
satırlar tek bir yerde ve sıralı duruyordu. Bu vaka o kolaylığı kaldırır. Kayıtlar iki boyutlu bir
düzlemde durur ve soru "şu noktaya yakın olanlar" biçiminde gelir. Yakınlığın sıralı bir anahtarı
yoktur: iki boyutu tek bir eksende sıralamanın her yolu bazı komşuları birbirinden uzağa düşürür,
çünkü bir eksende ardışık olan iki nokta düzlemde uzak, düzlemde komşu olan iki nokta eksende uzak
olabilir.

Çözüm düzlemi hücrelere bölmek ve her kayda bir **uzamsal anahtar** (geohash) yazmaktır: aynı
hücredeki kayıtlar aynı anahtarı taşır ve **yakınlık sorgusu** önce hücreleri, sonra hücre içindeki
adayları tarar. Ölçülecek karar hücre boyudur ve iki yönlü bir ödünleşim taşır: hücre büyüdükçe
taranan aday artar, hücre küçüldükçe sorgulanması gereken hücre sayısı artar.

#### Kısıtlar

İşlevsel gereksinim: yer kaydetme, hareketli nesnenin konumunu güncelleme, verilen bir noktanın
belirli yarıçapı içindeki yerleri döndürme ve sonucu uzaklığa göre sıralama.

İşlevsel olmayan gereksinim, sayıyla: yakınlık sorgusu yarıçap içindeki kayıtların **tamamını**
döndürür, kaçırma oranı **sıfırdır**; sorgu başına taranan aday **1000**'i geçmez; tasarım tepe
**1666,67** sorgu/s ve **24.000** konum güncellemesi/s taşır.

Kapsam daraltması: rota bulma, tahmini varış süresi, harita çizimi, yer sıralaması ve coğrafi
projeksiyon dönüşümleri tasarlanmaz; bu vakada bölge küçük olduğu için düzlem yaklaşımı kullanılır
ve bu bir model basitleştirmesidir.

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| KT1 | kayıtlı yer | 40.000.000 | hizmet alanındaki toplam nokta |
| KT2 | günlük etkin kullanıcı | 6.000.000 | yakınlık sorgusu yapanlar |
| KT3 | kullanıcı başına günlük sorgu | 8 | arama ve harita gezinmesi |
| KT4 | tepe çarpanı | 3 | tepe saatin gün ortalamasına oranı |
| KT5 | yer kaydı | 320 bayt | ad, konum, kategori, çalışma saatleri |
| KT6 | hareketli nesne | 120.000 | konumu sürekli değişen taşıyıcılar |
| KT7 | konum güncelleme aralığı | 15 s | taşıyıcının bildirim sıklığı |
| KT8 | yerleşim bölgesinde yoğunluk | 20 yer/km² | model bölgenin ortalaması |
| KT9 | bir hücre aramasının bedeli | 20 aday | dizin araması ile satır taramasının oranı |
| KT10 | yakınlık yarıçapı | 1 km | yürüme mesafesi sorgusu |

KT9 bu vakanın karar verdiren varsayımıdır: hücre boyu seçimi bir dizin aramasının kaç satır
taramasına denk olduğuna bağlıdır ve bu oran depo motoruna göre değişir.

#### Ölçek

```js
// konum-olcek.mjs — KT tablosundan cikan olcek hesabi; hepsi aritmetiktir
const KT = { yer: 40_000_000, kullanici: 6_000_000, sorgu: 8, tepe: 3, yerBayt: 320,
  hareketli: 120_000, guncellemeSn: 15, yogunluk: 20, yaricapKm: 1, adayButce: 1000 };
const GUN = 86_400;
const sorguSn = (KT.kullanici * KT.sorgu) / GUN;
const guncellemeSn = KT.hareketli / KT.guncellemeSn;

for (const [ad, d] of [
  ["tepe yakinlik sorgusu/s", sorguSn * KT.tepe],
  ["tepe konum guncellemesi/s", guncellemeSn * KT.tepe],
  ["guncelleme / sorgu orani", guncellemeSn / sorguSn],
  ["yer verisi GB", (KT.yer * KT.yerBayt) / 1e9],
  ["yaricap alani km2", Math.PI * KT.yaricapKm ** 2],
  ["alandaki beklenen yer", Math.PI * KT.yaricapKm ** 2 * KT.yogunluk],
]) console.log(ad.padEnd(26) + d.toFixed(2).padStart(14));

const duz = sorguSn * KT.tepe * KT.yer;
console.log(`\nduz tarama: sorgu basina ${KT.yer.toLocaleString("tr-TR")} kayit -> tepede ` +
  `${duz.toExponential(2)} kayit/s`);
console.log(`aday butcesi ${KT.adayButce} olursa tepede ${(sorguSn * KT.tepe * KT.adayButce)
  .toLocaleString("tr-TR", { maximumFractionDigits: 0 })} kayit/s (duz taramanin 1/${KT.yer / KT.adayButce}'i)`);
```

```
tepe yakinlik sorgusu/s          1666.67
tepe konum guncellemesi/s       24000.00
guncelleme / sorgu orani           14.40
yer verisi GB                      12.80
yaricap alani km2                   3.14
alandaki beklenen yer              62.83

duz tarama: sorgu basina 40.000.000 kayit -> tepede 6.67e+10 kayit/s
aday butcesi 1000 olursa tepede 1.666.667 kayit/s (duz taramanin 1/40000'i)
```

Bu sayılar **hesap** sınıfındadır ve üçü tasarımı belirliyor. Birincisi, bu vakada veri hacmi küçük:
40.000.000 yer kaydı 12,80 GB tutuyor, önceki üç vakanın terabaytlarının yanında hiç. Yoğunluk
buradaki veri hacminde değil, **sorgu başına dokunulan kayıt sayısında**. Düz tarama tepede saniyede
66,7 milyar kayıt okumak demek; aday sayısı 1000'de tutulursa aynı yük 1.666.667 kayıt/s'ye, yani
kırk binde birine iniyor. İkincisi, yazma okumadan baskın: konum güncellemesi sorgunun 14,40
katı ve tepede saniyede 24.000. Üçüncüsü, bir sorgunun gerçek yanıtı ortalama 62,83 kayıt; aday
bütçesi bu sayının on altı katı, yani tasarım baştan bir israfa razı oluyor.

#### Aday Sayısı

Hücre boyu ancak gerçek bir dağılım üzerinde savunulabilir, çünkü noktalar düzgün dağılmaz. Aşağıdaki
model 100 × 100 km'lik bir düzleme kümelenmiş 200.000 nokta koyar, beş hücre boyu için uzamsal
anahtarı bir dizine yazar ve aynı 200 sorguyu hepsine birden sorar.

```js
// uzamsal.mjs — node:sqlite ile gercek veri uzerinde uzamsal anahtarin taradigi aday sayisi.
// Bolge 100x100 km'lik bir duzlem olarak modellenir (kucuk bolgede duzlem yaklasimi); noktalar
// kumelenmis, uretec kendi yazilmis ve tohum gorunur. Model oldugu yazilir.
import { DatabaseSync } from "node:sqlite";

const N = 200_000, KENAR = 100, R = 1, SORGU = 200, TOHUM = 20260802, KUME = 20, ARAMA = 20;
const HUCRE = [0.5, 1, 2, 4, 8];                     // hucre kenar uzunlugu, km
let durum = TOHUM;
const rast = () => { durum = (durum * 1103515245 + 12345) % 2147483648; return durum / 2147483648; };
const merkez = Array.from({ length: KUME }, () => [rast() * KENAR, rast() * KENAR]);
function nokta() {                                    // %60 kume cevresinde, %40 duzgun dagilim
  if (rast() < 0.4) return [rast() * KENAR, rast() * KENAR];
  const [mx, my] = merkez[Math.floor(rast() * KUME)];
  const a = rast() * 2 * Math.PI, d = -Math.log(1 - rast()) * 3;
  return [Math.min(KENAR, Math.max(0, mx + d * Math.cos(a))), Math.min(KENAR, Math.max(0, my + d * Math.sin(a)))];
}
const kod = (x, y, c) => Math.floor(y / c) * 10_000 + Math.floor(x / c);

const db = new DatabaseSync(":memory:");
db.exec(`CREATE TABLE yer(id INTEGER PRIMARY KEY, x REAL, y REAL, ${HUCRE.map((_, i) => `h${i} INTEGER`).join(", ")});
${HUCRE.map((_, i) => `CREATE INDEX yer_h${i} ON yer(h${i});`).join("\n")}`);
db.exec("BEGIN");
const ekle = db.prepare(`INSERT INTO yer VALUES(?,?,?,${HUCRE.map(() => "?").join(",")})`);
const noktalar = [];
for (let i = 0; i < N; i += 1) {
  const [x, y] = nokta();
  noktalar.push([x, y]);
  ekle.run(i, x, y, ...HUCRE.map((c) => kod(x, y, c)));
}
db.exec("COMMIT");
const sorgular = Array.from({ length: SORGU }, nokta);
const yakin = (q, x, y) => (x - q[0]) ** 2 + (y - q[1]) ** 2 <= R * R;

let duzEslesen = 0;                                   // duz tarama: dogrulugun olcusu
for (const q of sorgular) duzEslesen += noktalar.filter(([x, y]) => yakin(q, x, y)).length;

console.log(`model: ${N} yer, ${KENAR}x${KENAR} km, ${KUME} kume, yaricap ${R} km, ` +
  `${SORGU} sorgu, tohum ${TOHUM}`);
console.log(`duz tarama: sorgu basina ${N} aday, ${(duzEslesen / SORGU).toFixed(2)} eslesme\n`);
console.log("hucre km".padEnd(10) + "sorgulanan".padStart(12) + "aday".padStart(10) +
  "isabet".padStart(9) + "esdeger is".padStart(12) + "kendi hucresi".padStart(15) +
  "kacirilan".padStart(11));
const IS = {};
for (const c of HUCRE) {
  const i = HUCRE.indexOf(c), k = Math.ceil(R / c);
  let aday = 0, eslesen = 0, hucre = 0, kendi = 0;
  for (const q of sorgular) {
    const cx = Math.floor(q[0] / c), cy = Math.floor(q[1] / c), liste = [];
    for (let dy = -k; dy <= k; dy += 1) for (let dx = -k; dx <= k; dx += 1) liste.push((cy + dy) * 10_000 + cx + dx);
    hucre += liste.length;
    const s = db.prepare(`SELECT x, y FROM yer WHERE h${i} IN (${liste.map(() => "?").join(",")})`).all(...liste);
    aday += s.length;
    for (const r of s) if (yakin(q, r.x, r.y)) {
      eslesen += 1;
      if (Math.floor(r.y / c) === cy && Math.floor(r.x / c) === cx) kendi += 1;
    }
  }
  IS[c] = [aday / SORGU, hucre / SORGU];
  console.log(`${c}`.padEnd(10) + (hucre / SORGU).toFixed(0).padStart(12) + (aday / SORGU).toFixed(0).padStart(10) +
    `%${((eslesen / aday) * 100).toFixed(1)}`.padStart(9) +
    (aday / SORGU + (hucre / SORGU) * ARAMA).toFixed(0).padStart(12) +
    (kendi / SORGU).toFixed(2).padStart(15) +
    `%${(((eslesen - kendi) / eslesen) * 100).toFixed(1)}`.padStart(11));
}
console.log(`\neslesme her hucre boyunda duz taramayla ayni (${(duzEslesen / SORGU).toFixed(2)}): ` +
  `komsu hucreler sorgulandigi icin kacirma yok`);
console.log(`esdeger is = aday + sorgulanan hucre x ${ARAMA} (KT9: bir hucre aramasinin bedeli)`);
const [a1, h1] = IS[0.5], [a2, h2] = IS[1];
console.log(`0.5 km ile 1 km esitlendigi nokta: hucre aramasi = ${((a2 - a1) / (h1 - h2)).toFixed(2)} ` +
  `aday taramasi; bunun altinda 0.5 km, ustunde 1 km kazanir`);
```

```
model: 200000 yer, 100x100 km, 20 kume, yaricap 1 km, 200 sorgu, tohum 20260802
duz tarama: sorgu basina 200000 aday, 443.95 eslesme

hucre km    sorgulanan      aday   isabet  esdeger is  kendi hucresi  kacirilan
0.5                 25       700    %63.4        1200         105.33      %76.3
1                    9       864    %51.4        1044         181.41      %59.1
2                    9      2123    %20.9        2303         322.27      %27.4
4                    9      5251     %8.5        5431         381.24      %14.1
8                    9     13611     %3.3       13791         415.85       %6.3

eslesme her hucre boyunda duz taramayla ayni (443.95): komsu hucreler sorgulandigi icin kacirma yok
esdeger is = aday + sorgulanan hucre x 20 (KT9: bir hucre aramasinin bedeli)
0.5 km ile 1 km esitlendigi nokta: hucre aramasi = 10.25 aday taramasi; bunun altinda 0.5 km, ustunde 1 km kazanir
```

Bu sayılar **ölçüm** sınıfındadır; değerler bu koşumun tohumuna ve kümelenme parametrelerine bağlı,
sütunlar arasındaki eğilim bağlı değil. Model kümelenmiş olduğu için sorgu başına 443,95 eşleşme
çıkıyor — ölçek hesabındaki 62,83'ün yedi katı, çünkü sorgular da noktalarla aynı dağılımdan
çekiliyor ve yoğun bölgelerde toplanıyor.

Üç şey okunuyor. Birincisi **dizin doğruluğu bozmuyor:** her hücre boyunda eşleşme sayısı düz
taramanınkiyle aynı, 443,95. Uzamsal anahtar bir yaklaşıklık değil, bir ön elemedir; kesin uzaklık
her adayda ayrıca hesaplanır. İkincisi **isabet hücre boyuyla çöküyor:** 0,5 km'de taranan adayın
yüzde 63,4'ü gerçek eşleşme, 8 km'de yalnız yüzde 3,3. Sekiz kilometrelik hücrede 13.611 aday
taranıyor; aday bütçesi 1000 olan kısıt 2 km ve üstünü doğrudan eliyor.

Üçüncüsü ödünleşimin şekli. Hücre küçüldükçe aday azalıyor ama sorgulanan hücre sayısı artıyor: 0,5
km'de 25 hücre ve 700 aday, 1 km'de 9 hücre ve 864 aday. KT9'un oranıyla eşdeğer iş 1200'e karşı
1044 çıkıyor ve **1 km kazanıyor**. Kazanan tarafın oranı da yazılabilir: iki boy tam olarak bir
hücre araması 10,25 aday taramasına denk geldiğinde eşitleniyor. Bir dizin araması bundan ucuzsa 0,5
km, pahalıysa 1 km doğru seçimdir — yani karar depo motorunun bir özelliğine bağlıdır, coğrafyaya
değil.

#### Tasarım

Uzamsal anahtar kayda **denormalize** edilerek yazılır (Veri Katmanı Ölçekleme kursunun Veri
Dağıtımı konusu) ve üzerine bir **dizin** konur (Veritabanları müfredatının Veritabanı Yönetimi
kursu, Dizin Türleri); ikisi birlikte **uzamsal dizini** oluşturur. Dizin mekaniği orada kuruldu,
burada seçilen tek şey anahtarın taneciğidir — 1 km. Kategori süzgeci gerektiğinde anahtar bir **bileşik dizine** girer (aynı kurs, Bileşik ve
Kısmi Dizinler): sıralama (hücre, kategori) olur, çünkü hücre her sorguda eşitlikle verilir.

**Parçalama** anahtarı uzamsal anahtarın kendisi değil, karmasıdır (Veri Dağıtımı konusu,
Parçalama). Uzamsal anahtara göre parçalanırsa yoğun bir bölgenin bütün hücreleri aynı parçaya
düşer ve modeldeki kümelenme doğrudan sıcak parçaya dönüşür; karma parçalamada bir sorgunun 9
hücresi farklı parçalara dağılır ve dokuz paralel arama olur. Bu, gecikme için ödenen bir bedeldir
ve tercih edilmesinin nedeni tepe 1666,67 sorgunun tek parçada toplanmamasıdır.

Hareketli nesnelerin konumu ayrı bir **anahtar–değer deposunda** tutulur (aynı konu, Depo Türleri):
anahtar nesne kimliği, değer son konum ve hücre. Saniyede 24.000 güncelleme yer tablosuna
yazılmaz, çünkü yer kayıtları neredeyse hiç değişmez ve iki iş yükünün aynı dizini paylaşması
dizin bakımını güncelleme hızına bağlardı. Yer sorgusu için hücre sonucu **yanında okuma** ile
önbelleğe alınır (Önbellek Mimarisi konusu); bayatlık penceresi burada güvenlidir çünkü yerler
değişmez, hareketli nesneler ise önbelleğe hiç girmez.

**Bilerek kullanılmayan kalıp: somutlaştırılmış görünüm.** Her hücre için komşularıyla birlikte
önceden hesaplanmış bir aday listesi tutmak sorguyu tek okumaya indirirdi, ama her kaydın dokuz
hücrede birden görünmesi demektir; hareketli nesnelerin saniyede 8000 güncellemesi 72.000 yazmaya
çıkar ve tepe yazma hızı üç katına çıkarken kazanç bir dizin aramasıdır. İkincisi **talep fişi
kalıbıdır** (Dayanıklılık ve Güvenilirlik kursunun Dağıtık Doğruluk konusu): en kötü durumda yanıt
gövdesi 1000 adayın süzülmüş halidir ve 320 baytlık kayıtlarla yüz kilobaytın altında kalır —
gövdeyi bir depoya koyup fiş döndürmek yanıta bir tur daha ekler.

#### Elenen Alternatifler

**Düz tarama** kaçırma kısıtını kusursuz karşılar ve hiçbir dizin bakımı istemez; sorgu başına
40.000.000 kayıt okur ve tepede saniyede 66,7 milyar kayda çıkar — aday bütçesinin kırk bin katı.
**Kaba hücre**, örneğin 8 km, dizin aramasını en aza indirir (9 hücre) ama 13.611 aday tarar ve 1000
kısıtını on üç kat aşar; 2 km bile 2123 adayla kısıtın iki katındadır.

**Tek hücre sorgusu** üçüncü alternatiftir ve dokuz arama yerine bir arama ister. Onu tablonun son
iki sütunu eliyor: yalnız sorgu noktasının kendi hücresi taransaydı 1 km hücrede 443,95 eşleşmenin
yalnız 181,41'i bulunurdu, yani **yüzde 59,1 kaçırma**. Oran hücre büyüdükçe düşüyor (8 km'de yüzde
6,3) ama sıfıra inmiyor, çünkü sorgu noktası hücrenin kenarına ne kadar yakınsa daire o kadar
dışarı taşar.

Tek hücre sorgusu hiçbir hücre boyunda kaçırma kısıtını karşılamıyor; komşu hücrelerin taranması
bir iyileştirme değil, bir gerekliliktir. **Hangi kısıt değişirse alternatif kazanır:** kaçırma
kısıtı gevşetilip "sonuçların yüzde 90'ı yeter" denseydi 8 km hücrede tek hücre sorgusu yüzde 93,7
ile geçerdi ve dokuz arama bire inerdi.

#### Arıza Davranışı ve Feda Edilen

Bir parça düğümü düştüğünde sorgunun dokuz hücresinden bir kısmı yanıtsız kalır. Burada iki seçenek
vardır ve tasarım ikincisini seçer: eksik yanıtı hata saymak ya da elde olan hücrelerle yanıt verip
sonucun kısmi olduğunu belirtmek. İkincisi **zarif bozulmadır** (Dayanıklılık ve Güvenilirlik
kursunun Arıza Yalıtımı konusu) ve bedeli ölçülmüştür: bir hücre eksik kalırsa kaçırma oranı sıfır
kısıtı çiğnenir, ama tek hücre sorgusunun yüzde 59,1'lik kaçırmasının yanında dokuzda bir eksik
tarama çok daha küçük bir bozulmadır. Önbellek düştüğünde yük doğrudan dizine biner ve tepe
1666,67 sorgu tamamen depoya ulaşır; bu, aday bütçesinin neden yanıt boyutuna değil tepe hıza göre
seçildiğinin gerekçesidir.

**Feda edilen:** sorgu başına 864 aday taranıyor, gerçek yanıt ise 443,95 kayıt — isabet yüzde 51,4
ve tasarım her sorguda yaklaşık yarısı boşa giden bir tarama kabul ediyor. Bu israfın karşılığı,
kaçırma oranının sıfır kalması ve hücre boyunun tek bir sayıya sabitlenebilmesidir.

#### Özet

- Bu vakada hacim küçük (12,80 GB) ama sorgu pahalı: düz tarama tepede saniyede 66,7 milyar kayıt
  okumak demek, aday bütçesi 1000 olduğunda aynı yük kırk binde birine iniyor.
- Uzamsal anahtar doğruluğu bozmuyor: her hücre boyunda eşleşme sayısı düz taramayla aynı (443,95),
  çünkü hücre bir ön elemedir ve kesin uzaklık her adayda ayrıca hesaplanır.
- Aday sayısı hücre boyuyla hızla büyüyor: 0,5 km'de 700, 1 km'de 864, 8 km'de 13.611; isabet yüzde
  63,4'ten yüzde 3,3'e düşüyor ve 1000 aday kısıtı 2 km ve üstünü eliyor.
- Küçük hücre adayı azaltır ama hücre sayısını artırır: 0,5 km 25 hücre ve 700 aday, 1 km 9 hücre ve
  864 aday; eşdeğer iş 1200'e karşı 1044 ve iki boy bir hücre araması 10,25 aday taramasına denk
  geldiğinde eşitlenir.
- Komşu hücre sorgulamak bir iyileştirme değil zorunluluktur: yalnız kendi hücresi taransa 1 km'de
  kaçırma yüzde 59,1, 8 km'de yüzde 6,3 olur ve hiçbir boyda sıfıra inmez.

#### Sonraki Adım

Bu konudaki dört vaka aynı ayrımın iki yakasında durdu. Nesne deposu ile video servisi işi
**yazıldığı anda** yaptı: parça yerleştirildi, kalite basamakları üretildi ve okuma yalnız hazır
olanı aldı. Ölçüt sistemi ile konum servisi işin bir kısmını **sorulduğu anda** yaptı:
toplulaştırma önceden hesaplansa da yüzdelik sorusu okuma anında yanıtlandı, uzamsal anahtar
önceden yazılsa da yakınlık sorgusu her seferinde yeniden tarandı. Hiçbir vakada ikisinin **aynı
sistemde birlikte** gerekmesi ele alınmadı: aynı veri hem geriye dönük olarak eksiksiz işlenmek hem
de geldiği anda saniyeler içinde yanıtlanmak zorunda kalsaydı, iki işleme biçiminin ürettiği
sonuçların birbirini tutması ayrı bir tasarım problemi olurdu. Sonraki ders o problemi ele alır.

### Toplu ve Akış İşleme Hattı

Source: https://academia.sh/tr/kurslar/vaka-calismalari/toplu-ve-akis-isleme-hatti

İki işleme modelinin aynı sistemde birlikte kullanıldığı kapanış vakası: ilerleme damgasının geç gelen veriyi nasıl kestiği, akış yolunun ilk yayımladığı sonucun toplu yolun yeniden hesabından sapması, sapmanın kapanma eğrisi ve iki yolu birden koşturmanın işlem yükü olarak bedeli.

Önceki dört vakanın her biri işi ya **yazıldığı anda** ya da **sorulduğu anda** yaptı; hiçbirinde
ikisi aynı sistemde birlikte gerekmedi. Bu vaka o durumu ele alır. Sistem bir olay akışını dakikalık
pencerelerde sayar ve iki yol kurulur: **akış işleme** saniyeler içinde bir sonuç yayımlar, **toplu
işleme** aynı pencereyi saatler sonra kütükten yeniden hesaplar. İkisi aynı girdiden farklı sonuçlar
üretir, çünkü akış yolu bir pencereyi kapatmak zorundadır ve o andan sonrasını göremez.

Kapatma kararını **ilerleme damgası** (watermark) verir: akışın zaman ekseninde nereye kadar
ilerlediğini söyleyen, olay zamanı cinsinden bir sınırdır ve bu sınırı geçen pencere kapanır. Adı
benzese de Uygulama Katmanı kursunun **sürüm damgasından** ve Dayanıklılık ve Güvenilirlik kursunun
**dağıtım damgasından** ayrıdır: biri kaydın sürümünü, öteki dağıtım biriminin kimliğini gösterir,
bu ise zamandaki ilerlemeyi. Damga geçtikten sonra ulaşan kayıt **geç gelen veridir** (late
arriving data) ve yayımlanan sayıya girmez.

#### Kısıtlar

İşlevsel gereksinim: olay kabul etme, olay zamanına göre 60 saniyelik pencerelerde sayma, sonucu
yayımlama ve geç gelen veriyle düzeltme.

İşlevsel olmayan gereksinim, sayıyla: ilk sonuç pencere kapandıktan en çok **60** saniye sonra
yayımlanır; sapması **binde 30**'u geçmez; sapma **12** saat içinde **binde 0,5**'in altına iner;
iki yolun toplam işlem yükü ham girdinin **3** katını geçmez.

Kapsam daraltması: akışlar arası birleştirme, oturum pencereleri, tam bir kez işleme garantisi ve
sorgu dili tasarlanmaz.

#### Varsayımlar

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| TA1 | günlük olay | 900.000.000 | uçların ürettiği toplam |
| TA2 | olay kaydı | 180 bayt | kimlik, olay zamanı, tür, alan |
| TA3 | pencere boyu | 60 s | raporlamanın en ince taneciği |
| TA4 | gecikme dağılımı | 0,94 düzgün 0–5 s; 0,055 üstel 90 s; 0,005 üstel 4 saat | çoğu uç hızlı, çok azı çevrimdışı |
| TA5 | tepe çarpanı | 3 | tepe saatin gün ortalamasına oranı |
| TA6 | toplu koşum aralığı | 6 saat | günde dört koşum |
| TA7 | kütüğün saklama süresi | 30 gün | yeniden hesabın erişebildiği geçmiş |

TA4 taşıyıcı varsayımdır: iki yol arasındaki farkın tamamı gecikme dağılımının kuyruğundan çıkar.
Kuyruk yoksa iki yol aynı sonucu verir ve ikinci yola gerek kalmaz.

#### Ölçek

```js
// hat-olcek.mjs — TA tablosundan cikan olcek hesabi ve iki yolu birden kosturmanin bedeli
const TA = { gunlukOlay: 900_000_000, olayBayt: 180, pencereSn: 60, tepe: 3, topluAralikSaat: 6,
  saklamaGun: 30 };
const GUN = 86_400;
const olaySn = TA.gunlukOlay / GUN;
for (const [ad, d] of [
  ["ortalama olay/s", olaySn],
  ["tepe olay/s", olaySn * TA.tepe],
  ["gunluk kutuk GB", (TA.gunlukOlay * TA.olayBayt) / 1e9],
  ["kutuk TB (saklama)", (TA.gunlukOlay * TA.olayBayt * TA.saklamaGun) / 1e12],
]) console.log(ad.padEnd(22) + d.toFixed(2).padStart(14));

console.log(`\ntoplu kosum ${TA.topluAralikSaat} saatte bir; tarama penceresi is yukunu belirliyor:`);
console.log("tarama saat".padEnd(13) + "gunluk kosum".padStart(14) + "toplu is kati".padStart(15) +
  "akisla toplam".padStart(15));
for (const W of [6, 12, 24]) {
  const kosum = 24 / TA.topluAralikSaat;
  const kat = (kosum * W) / 24;
  console.log(String(W).padEnd(13) + String(kosum).padStart(14) + kat.toFixed(2).padStart(15) +
    (1 + kat).toFixed(2).padStart(15));
}
```

```
ortalama olay/s             10416.67
tepe olay/s                 31250.00
gunluk kutuk GB               162.00
kutuk TB (saklama)              4.86

toplu kosum 6 saatte bir; tarama penceresi is yukunu belirliyor:
tarama saat    gunluk kosum  toplu is kati  akisla toplam
6                         4           1.00           2.00
12                        4           2.00           3.00
24                        4           4.00           5.00
```

Bu sayılar **hesap** sınıfındadır. Alt tablo son kısıtı bağlıyor: toplu koşum altı saatte bir
çalıştığı için tarama penceresi uzadıkça aynı olay daha çok kez okunur — altı saatte bir, on iki
saatte iki, yirmi dört saatte dört kez. Akış yolunun tek geçişiyle toplam 2,00 / 3,00 / 5,00 kat
oluyor ve üç kat kısıtı yirmi dört saatlik taramayı baştan eliyor.

#### İki Yolun Farkı

Damga gecikmesi hem yayım süresini hem sapmayı belirler ve ikisi ters yönde hareket eder. Aşağıdaki
model küme kurmaz; TA4'ün karışımından gecikme çeker ve akış yolunun saydığını toplu yolun tam
sayımıyla karşılaştırır.

```js
// iki-yol.mjs — surec ici model: ayni olay kumesi uzerinde akis yolu ile toplu yolun urettigi
// sonuc farki. Ag yok, kume yok; gecikme dagilimi TA4'un uc parcali karisimidir, uretec kendi
// yazilmistir ve tohum gorunur. Model oldugu yazilir.
const PENCERE = 60, SAYI = 120, OLAY = 2000, TOHUM = 20260803;
const KARISIM = [[0.94, "duzgun", 5], [0.055, "ustel", 90], [0.005, "ustel", 14_400]];  // TA4

let durum = TOHUM;
const rast = () => { durum = (durum * 1103515245 + 12345) % 2147483648; return durum / 2147483648; };
function gecikme() {
  let u = rast(), i = 0;
  while (i < KARISIM.length - 1 && u > KARISIM[i][0]) { u -= KARISIM[i][0]; i += 1; }
  const [, tur, p] = KARISIM[i];
  return tur === "duzgun" ? rast() * p : -Math.log(1 - rast()) * p;
}
const S = (x) => KARISIM.reduce((a, [w, tur, p]) =>       // kosumdan bagimsiz: kuyruk olasiligi
  a + w * (tur === "duzgun" ? Math.max(0, 1 - x / p) : Math.exp(-x / p)), 0);
const beklenenGec = (L) => {                              // pencere icinde duzgun konum uzerinden
  let t = 0;
  for (let i = 0; i < 6000; i += 1) t += S(PENCERE + L - (i + 0.5) * (PENCERE / 6000));
  return t / 6000;
};

const toplam = SAYI * OLAY;                               // pencere basindan itibaren varis ani
const varis = Array.from({ length: toplam }, () => rast() * PENCERE + gecikme());

console.log(`model: ${SAYI} pencere x ${OLAY} olay = ${toplam}, pencere ${PENCERE} s, tohum ${TOHUM}`);
console.log(`gecikme karisimi: ${KARISIM.map(([w, t, p]) => `${w}/${t}/${p}s`).join(" ")}\n`);
console.log("damga gecikmesi".padEnd(16) + "yayim gecikmesi".padStart(16) +
  "gec olay binde".padStart(16) + "beklenen binde".padStart(16));
for (const L of [0, 15, 60, 120, 300]) {
  const gec = varis.filter((o) => o > PENCERE + L).length;
  console.log(`${L} s`.padEnd(16) + `${PENCERE + L} s`.padStart(16) +
    ((gec / toplam) * 1000).toFixed(2).padStart(16) + (beklenenGec(L) * 1000).toFixed(2).padStart(16));
}

console.log(`\nsapmanin kapanmasi (damga gecikmesi 60 s ile yayimlanan sonuca gore):`);
console.log("pencere kapandiktan".padEnd(21) + "hala eksik olay".padStart(17) + "kalan sapma binde".padStart(19));
for (const [ad, T] of [["1 saat sonra", 3600], ["6 saat sonra", 21_600], ["12 saat sonra", 43_200],
  ["24 saat sonra", 86_400]]) {
  const kalan = varis.filter((o) => o > PENCERE + T).length;
  console.log(ad.padEnd(21) + String(kalan).padStart(17) + ((kalan / toplam) * 1000).toFixed(3).padStart(19));
}
```

```
model: 120 pencere x 2000 olay = 240000, pencere 60 s, tohum 20260803
gecikme karisimi: 0.94/duzgun/5s 0.055/ustel/90s 0.005/ustel/14400s

damga gecikmesi  yayim gecikmesi  gec olay binde  beklenen binde
0 s                         60 s           82.46           84.30
15 s                        75 s           37.42           38.96
60 s                       120 s           24.67           25.58
120 s                      180 s           13.32           15.53
300 s                      360 s            5.37            6.32

sapmanin kapanmasi (damga gecikmesi 60 s ile yayimlanan sonuca gore):
pencere kapandiktan    hala eksik olay  kalan sapma binde
1 saat sonra                       711              2.962
6 saat sonra                       251              1.046
12 saat sonra                       69              0.287
24 saat sonra                        0              0.000
```

Ölçülen binde bu koşuma bağlı bir **ölçüm**, beklenen binde karışımın kuyruk olasılığından çıkan
koşumdan bağımsız bir **hesaptır**; beş satırın hepsinde ikisi aynı mertebede.

Üst tablo iki kısıtı birden bağlıyor. Damga 0 iken sapma binde 82,46, 15 saniyede 37,42 — ikisi de
binde 30 kısıtını kırıyor; 60 saniyede binde 24,67 ile karşılanıyor. Öte yandan yayım gecikmesi
pencere boyu artı damgadır ve "en çok 60 saniye sonra" kısıtı damgayı 60 saniyeyle sınırlıyor. İki
kısıt tek bir değerde kesişiyor: **damga gecikmesi 60 saniye.**

İlk satır bir yanılgıyı da düzeltiyor. Damga sıfırken kaçırılanların çoğu geç gelen veri değil,
pencerenin son saniyelerine düşen ve normal hızda ulaşan olaydır; gecikmesi beş saniyenin altında
olanların bir kısmı bile sınırı aşar. 15 saniyede bu pay tamamen kalkıyor ve binde 82,46'dan
37,42'ye düşüş oradan geliyor. Kalan sapmanın kaynağı gerçekten kuyruktur.

Alt tablo kapanmayı veriyor: bir saat sonra binde 2,962, altı saat sonra 1,046, on iki saat sonra
0,287. "On iki saatte binde 0,5'in altına iner" kısıtı altı saatlik taramayla karşılanmıyor, on iki
saatlikle karşılanıyor. İş yükü kısıtı yirmi dört saati elemişti; kapanma kısıtı altı saati eliyor.
Geriye **on iki saatlik tarama penceresi** kalıyor ve toplam iş ham girdinin tam 3,00 katı oluyor.

#### Tasarım

Tek gerçek kaynak olay kütüğüdür ve iki yol da onun **izdüşümüdür** (Veri Katmanı Ölçekleme
kursunun Okuma–Yazma Ayrımı konusu, Olay Kaynaklı Tasarım). Yazma modeli kütük, okuma modeli pencere
sayılarıdır; ikisinin ayrılması aynı konudaki **komut ve sorgu sorumluluğu ayrımıdır**. Toplu yolun
ürettiği tablo bir **somutlaştırılmış görünümdür** (Veri Dağıtımı konusu) ve tazelemesi artımlı
değil, on iki saatlik pencerenin yeniden hesabıdır.

Yayımlanan değerin üstüne yazma **etkisiz** olmalıdır (Dayanıklılık ve Güvenilirlik kursunun Dağıtık
Doğruluk konusu); tekillik anahtarı pencere ile yolun ikilisidir, böylece aynı koşum iki kez
çalışsa da sonuç değişmez. Alma ucu bir **ileti aracısı** ve **rekabet eden tüketiciler** üzerine
oturur (Uygulama Katmanı ve Servis Etkileşimi kursunun Kuyruklar ve İş Akışları konusu); tepe 31.250
olay/s bu katmanın girdisidir ve tüketici geri kaldığında **geri basınç** (Arıza Yalıtımı konusu)
uca kadar yayılır. Okuyucunun gördüğü değer **sonunda tutarlıdır** (Sistem Tasarımına Giriş kursunun
Temel Nitelikler konusu) ve bayatlık penceresi burada ölçülmüş bir sayıdır: binde 0,287.

**Bilerek kullanılmayan kalıp: kenar önbelleği.** Trafik Katmanı kursunun Giriş Noktaları
konusundaki kenar önbelleği yayımlanan sayıların önüne konmaz, çünkü aynı pencerenin değeri on iki
saat boyunca düzeltiliyor; kenarda ikinci bir bayatlık penceresi açmak tasarımın tek ölçülebilir
güvencesini, kapanma süresini, ölçülemez yapardı. İkincisi **telafi edici işlemdir** (Dağıtık
Doğruluk konusu): toplu yolun yaptığı geri alma değil üzerine yazmadır, çünkü kütük değişmiyor.

#### Elenen Alternatifler

**Yalnız akış yolu** en ucuzudur: bir kat iş, 120 saniyede yayım. Ama binde 24,67'lik sapma hiçbir
zaman kapanmaz; düzeltmeyi yapacak ikinci geçiş yoktur.

**Yalnız toplu yol** doğrulukta kusursuzdur ve iş yükü bir kattır. Bir pencerenin sonucu en kötü
durumda altı saat sonra yayımlanır: yayım kısıtının üç yüz altmış katı.

**Damgayı büyütmek** sapmayı 300 saniyede binde 5,37'ye indirir ve tek yolla binde 30 kısıtını
karşılar; yayım gecikmesi 360 saniyeye çıkıp birinci kısıtı altı kat aşar. **Hangi kısıt değişirse
alternatif kazanır:** yayım kısıtı 6 dakikaya gevşetilirse tek akış yolu yeter ve ikinci yolun 2,00
katlık ek yükü düşer.

#### Arıza Davranışı ve Feda Edilen

Akış yolu tümüyle durduğunda sistem yanıt vermeyi sürdürür: toplu yol altı saatte bir doğru sonucu
üretir, yayım gecikmesi 120 saniyeden altı saate çıkar ve sapma sıfırlanır. Bu, **zarif bozulmanın**
(Arıza Yalıtımı konusu) alışılmadık bir biçimidir — bozulan boyut doğruluk değil tazeliktir. Toplu
yol durduğunda tersi olur: yayım 120 saniyede kalır ama binde 24,67'lik sapma donar. İki arızada da
veri kaybı yok, çünkü iki yol da otuz gün saklanan kütükten türetiliyor.

**Feda edilen:** aynı olay iki kez işleniyor. Toplam iş ham girdinin 3,00 katı ve aynı sayım iki
ayrı kod yolunda tutuluyor; ikisinin aynı sonucu vermesi kod ortaklığıyla değil bir ölçümle güvence
altına alınıyor. Okuyucuya verilen ilk sayı da hiçbir zaman nihai değildir.

#### Özet

- İlerleme damgası bir pencereyi kapatma kararıdır; gecikmesi yayım süresini ve sapmayı ters yönde
  oynattığı için iki kısıt arasında sıkışır.
- Damga 0 / 15 / 60 / 120 / 300 saniyede sapma binde 82,46 / 37,42 / 24,67 / 13,32 / 5,37; binde 30
  kısıtı ile 60 saniyelik yayım kısıtı tek bir değerde, 60 saniyede kesişiyor.
- Damga sıfırken kaçırılanların çoğu geç gelen veri değil, pencerenin son saniyelerine düşen normal
  olaydır; 15 saniyede bu pay tamamen kalkıyor.
- Tarama penceresi iki yandan sıkışıyor: yirmi dört saat 5,00 katla iş kısıtını, altı saat binde
  1,046 ile kapanma kısıtını çiğniyor; on iki saat ikisini de sınırda karşılıyor.

#### Kurs Kapanışı

On dört vaka aynı altı adımı yürüttü ve her biri bir sayıyla savunuldu.

| Vaka | Tasarımı belirleyen sayı | Seçilen kalıplar | Elenen alternatif | Feda edilen |
|---|---|---|---|---|
| Bağlantı Kısaltma | okuma/yazma 100; 7 karakterde 3859,3 tahmin | kenar önbelleği, yanında okuma, parçalama | sayaç tabanlı üretim: 1,0 tahmin/isabet | 300 s bayatlık penceresi |
| Haber Akışı | dağıtma 1691,87 / toplama 56.570,71 iş/s (33,44 kat) | eşikli karma dağıtım, mesaj kuyruğu, geri basınç | saf okumada toplama: 33,44 kat iş | gönderi başına 203,63 kutu yazması |
| Arama Önerisi | sorgu başına 266,67 bayt; derinlik 10'da 4,59 GB | önek ağacı, görev kuyruğu, itme tabanlı dağıtım | düz sıralı liste: p99 6593 birim | 10 karakter sınırı, 600 s tazelik |
| İçerik Dağıtım | istek oranı 3,13, bayt oranı 20,83; gereken isabet 0,7440 | içerik dağıtım ağı, kenar önbelleği, sürümlü ad | kısa ömürlü tek önbellek: 2 GB'de 0,6188 | yayımın atomikliği |
| Sohbet Sistemi | tepe teslim 22.222,22/s; 1.250.000 bağlantı, 25 düğüm | tekillik anahtarı, sıralı konvoy, çevrimdışı kutu | yoklamalı çekme: 140,63 kat istek | sohbet düzeyinde koşutluk |
| Bildirim Sistemi | tek kanalda teslim 0,9590, eşiğin 3,01 puan altı | kademeli sıra, devre kesici, bölme kalıbı | paralel dağıtım: 2,78 kat çağrı | 30–60 s teslim gecikmesi |
| Hız Sınırlayıcı | sabit pencere 1200 (x2,000), kaydırmalı dilim 600 | kaydırmalı dilim, 0,2 s gecikmeli eşitleme | merkezî sayaç: 40.000 dokunuş/s | aşımın 1,015 katta kalması |
| Bilet ve Stok | talep kontenjanın 56 katı; tükenme 0,179 s | kötümser kilitleme, kontenjan satırı | iyimser kilitleme: 2409 geçerli ret | 304.276 bekleme adımı |
| Ödeme Akışı | yalnız bildirimde binde 59,74; yoklamayla binde 9,76 | etkisizlik, 15 dk mutabakat çevrimi, denetim izi | eşzamanlı doğrulama: binde 75,77 | binde 9,76 ve 30 dk kapanma |
| Nesne Depolama | 2 MB parçada 1,0075; silme (10,4) katı 1,40 | vale anahtarı, etkisizlik, parçalama, denetçi | üç kopya: kat 3,00, yılda 438 kayıp | 697.680 GB-ay fazladan saklama |
| Video Yayın | izlenen/yüklenen 225; oynatamayan yüzde 8,91 | öncelikli kuyruk, talep fişi, kenar önbelleği | sekiz basamak: kat 2,58, 1854 işlemci | bant kullanımı yüzde 64,9 |
| Ölçüt Toplama | 240.000 örnek/s; 31,50 TB'a karşı 393,57 TB | geniş sütun deposu, zaman bölümleme, somutlaştırılmış görünüm | yalnız 5 dk özet: eşikte yüzde 100 hata | 7 günden eski yüzdelik soruları |
| Konum Tabanlı | 1 km hücrede 864 aday, isabet yüzde 51,4 | uzamsal dizin, bileşik dizin, karma parçalama | 8 km hücre: 13.611 aday | yarısı boşa giden tarama |
| Toplu ve Akış | 60 s damgada binde 24,67; 12 saatte binde 0,287 | ilerleme damgası, olay kütüğü ve izdüşüm, etkisizlik | yalnız akış: sapma kapanmaz | 3,00 kat iş, iki kod yolu |

Tablonun kuralı bu müfredatın kuralıdır: **bir tasarım kararı ancak bir sayıyla ve bir alternatifle
birlikte savunulabilir.** Sayının sınıfı — varsayım, hesap, ölçüm — yazılmadan verilen karar
tartışılamaz, çünkü karşı taraf neyin gerekçesini soracağını bilemez: varsayım gerekçesiyle ve
duyarlılığıyla, hesap koşturulmuş aritmetiğiyle, ölçüm düzeneğiyle ve tohumuyla savunulur.

Yedi kurs bu kuralı parça parça kurdu. Sistem Tasarımına Giriş nitelikleri sayıya çevirdi ve kabaca
büyüklük hesabını kurdu; Trafik Katmanı isteğin sisteme nasıl girdiğini, Uygulama Katmanı işin
servisler arasında nasıl bölündüğünü, Veri Katmanı Ölçekleme verinin nasıl dağıtıldığını ele aldı.
Dayanıklılık ve Güvenilirlik arızanın nasıl çevrelendiğini, Başarım Karşıt Kalıpları ve İzleme
bunların nasıl görünür kılındığını gösterdi. Vaka Çalışmaları hepsini tek tek problemlerde
birleştirdi ve her seferinde aynı şeyi istedi: kısıtı sayıya çevir, varsayımını yaz, hesabı koştur,
kalıbı adıyla seç, bir alternatifi ele, neyi feda ettiğini söyle.

Bundan sonrası okurun kendi tasarım problemidir. Oradaki kısıtlar bu on dört vakanınkine
benzemeyecek; benzeyen tek şey yöntem olacak. İlk soru hep aynıdır: bu kararı hangi sayı belirliyor,
o sayı hangi sınıftan ve elenen alternatif hangisiydi.

