---
title: 'Bellek İçi Depolar ve Önbellek Sistemleri'
source: 'https://academia.sh/tr/kurslar/bellek-ici-depolar'
language: tr
updated: '2026-08-17T04:40:22+00:00'
license: 'CC BY-SA 4.0'
lessons: 22
outcomes:
    - 'Bellek içi veri yapılarını uygun kullanım senaryolarına eşler'
    - 'Kalıcılık ve bellek tahliye politikalarını yapılandırır'
    - 'Küme ve çoğaltma ile erişilebilirliği artırır'
---

# Bellek İçi Depolar ve Önbellek Sistemleri

Verinin tamamının bellekte tutulduğu ve dayanıklılığın bir ayar hâline geldiği depolar: anahtar–değer temelleri, sayaçlar, listeler, karma yapılar, kümeler, sıralı kümeler, bit eşlemler, olasılıksal yapılar, akışlar ve uzamsal dizinlerin uygun senaryolarla eşlenmesi, anlık görüntü ile ekleme günlüğü kalıcılığının dayanıklılık ve başarım ödünleşimi, bellek sınırı ile tahliye politikaları, süre sonu yönetimi, çoğaltma, otomatik devralma, kümeleme, iyimser kilitleme, yayımla–abone ol ve tipik kullanım senaryoları.

## Veri Yapıları ve Komutlar

Bellek içi deponun sunduğu yapıların senaryolarla eşlenmesi: anahtar ayarlama, okuma ve süre sonu, atomik artırmaya dayanan sayaçlar, kuyruk ile yığıt olarak listeler, nesne alanlarını ayrı yöneten karma yapılar, üyelik ile sıralama senaryolarında kümeler ve sıralı kümeler, bellek verimli sayım için bit eşlemler ile olasılıksal yapılar, sıralı olay günlüğü olarak akışlar ve tüketici grupları, konum tabanlı sorgular için uzamsal dizinler ve atomik çok adımlı işlemler için sunucu tarafı betikleme.

### Anahtar–Değer Temelleri

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/anahtar-deger-temelleri

Bellek içi deponun sözleşmesi ve bedeli: ayarlama, okuma ve süre sonunun kendi yazılan bir depo üzerinde kurulması, giriş başına tutulan baytın anahtar–değer–üstveri olarak sayılması, süre sonu olmayan ile sabit ve kayan süre sonu politikalarının zirve bellek ve yeniden giriş sayısı düzleminde karşılaştırılması, anahtar ve değer biçiminin tutulan bayta etkisi ve üstverinin giriş sayısıyla ölçeklenmesi.

İlişkisel Olmayan Veri Modelleri kursu, ele aldığı bütün depoların ortak bir alışkanlığıyla
kapandı: veri er ya da geç diske yazıldı, dayanıklılık modelin verili bir parçasıydı. Bu kurs
o alışkanlığı bırakan depoları konu ediyor. Bellek içi depoda verinin tamamı süreç belleğinde
durur, disk isteğe bağlı bir ayardır ve asıl kısıt kapasite değil **bellek bütçesidir**.

Bu kursun her dersi aynı üç soruyu sorar: bir yapı bellekte **kaç bayt tutuyor**, karşılığında
**ne satın alıyor**, aynı işi daha az bellekle yapan seçenek **ne kaybediyor**. "Hızlıdır" bu
soruların hiçbirinin yanıtı değildir; bellek içi deponun hızlı olması konu değil varsayımdır.
Bu ders sözleşmenin kendisiyle başlıyor: bir anahtar ayarlanır, okunur ve bir ömrü olur.

#### Sözleşme ve Giriş Başına Bayt

Depo üç işlem tanır. **Ayarlama** bir anahtara bir değer bağlar ve isteğe bağlı bir ömür verir.
**Okuma** anahtarı verir, değeri alır; ömrü dolmuş bir giriş yokmuş gibi davranır. **Ömür uzatma**
son erim damgasını ileri taşır. Anahtar–değer deposunun temel sözleşmesi burada da geçerlidir:
depo değerin içini görmez. Değişen tek şey, kaydın nerede durduğudur — ve bu tek değişiklik
maliyet hesabını baştan yazar, çünkü diskte yer bir kapasite kalemiyken bellekte yer bir bütçedir.

Ölçüm için kütüphanenin oturum kaydı kullanılır. **BY1:** katalog 20.000 üye taşır, gün 12 saattir
(43.200 saniye), her üye gün içinde 1–5 kez sisteme dokunur ve dokunuşlar arası 60–2.459 saniyedir;
dokunuş zamanları tohumu görünür bir doğrusal eşlenik üreticiden gelir. **BY2:** giriş başına
üstveri 56 bayt sayılır — karma kovası göstergesi 8, giriş yapısının göstergeleri 16, uzunluk
alanları 16, son erim damgası 8, hizalama 8. Tutulan bayt her zaman `anahtar + değer + 56`
biçiminde hesaplanır. **BY3:** canlı giriş 300 saniyede bir örneklenir, zirve bu örneklerin en
büyüğüdür; süresi geçmiş girişin bellekten ne zaman silindiği ayrı bir sorudur, burada yalnız
**süresi geçmemiş** giriş sayılır.

```js
// bellek/depo.mjs — kendi yazilan bellek ici depo: anahtar alani, sure sonu ve tutulan
// baytin sayilmasi. Giris basina ustveri BY2'de bildirilir; canli giris 300 sn'de bir sayilir.
const USTVERI = 56;

class BellekIciDepo {
  #tablo = new Map();
  okuma = 0; iska = 0;
  ayarla(anahtar, deger, simdi, omur) {
    const s = JSON.stringify(deger);
    this.#tablo.set(anahtar, { s, sonErim: omur === null ? null : simdi + omur });
  }
  oku(anahtar, simdi) {                       // suresi gecmis giris yok sayilir
    this.okuma += 1;
    const g = this.#tablo.get(anahtar);
    if (g === undefined || (g.sonErim !== null && g.sonErim <= simdi)) { this.iska += 1; return undefined; }
    return JSON.parse(g.s);
  }
  omruUzat(anahtar, simdi, omur) { const g = this.#tablo.get(anahtar); if (g) g.sonErim = simdi + omur; }
  canli(simdi) {                              // giris basina: anahtar + deger + ustveri
    let n = 0, b = 0;
    for (const [a, g] of this.#tablo)
      if (g.sonErim === null || g.sonErim > simdi) { n += 1; b += Buffer.byteLength(a) + Buffer.byteLength(g.s) + USTVERI; }
    return { n, b }; }
}

// --- olay uretimi: 12 saatlik gun, 20.000 uye, dokunuslar tohumlu ureticten ---
const UYE = 20_000, GUN = 43_200, OMUR = 1800, ORNEK = 300, TOHUM = 20240115;
let cekirdek = TOHUM;
const rast = () => (cekirdek = (cekirdek * 1103515245 + 12345) % 2147483648) / 2147483648;
const SUBE = ["merkez", "sahil", "universite", "cocuk", "arsiv"];
const olay = [];
for (let u = 1; u <= UYE; u += 1) {
  let t = Math.floor(rast() * (GUN - 7200));
  const kez = 1 + Math.floor(rast() * 5);
  for (let k = 0; k < kez && t < GUN; k += 1) { olay.push([t, u]); t += 60 + Math.floor(rast() * 2400); }
}
olay.sort((x, y) => x[0] - y[0] || x[1] - y[1]);
const anahtar = (u) => `oturum:${u}:${(u * 2654435761) % 4294967296}`;
const oturum = (u) => ({ uye: u, sube: SUBE[u % 5], rol: u % 97 === 0 ? "gorevli" : "okur" });

function kosum(ad, omur, kayan) {
  const d = new BellekIciDepo();
  let zirveN = 0, zirveB = 0, yeni = 0, sonraki = ORNEK;
  for (const [t, u] of olay) {
    while (t >= sonraki) { const c = d.canli(sonraki); if (c.n > zirveN) { zirveN = c.n; zirveB = c.b; } sonraki += ORNEK; }
    const a = anahtar(u);
    if (d.oku(a, t) === undefined) { d.ayarla(a, oturum(u), t, omur); yeni += 1; }
    else if (kayan) d.omruUzat(a, t, omur);
  }
  const son = d.canli(GUN);
  if (son.n > zirveN) { zirveN = son.n; zirveB = son.b; }
  return [ad, zirveN, zirveB, son.n, son.b, d.iska, yeni];
}

console.log(`tohum ${TOHUM}; ${UYE} uye, ${olay.length} dokunus, gun ${GUN} sn, omur ${OMUR} sn`);
console.log(`ornek oturum degeri ${Buffer.byteLength(JSON.stringify(oturum(4242)))} bayt, ` +
  `anahtar ${Buffer.byteLength(anahtar(4242))} bayt, ustveri ${USTVERI} bayt`);
const satir = [kosum("sure sonu yok", null, false), kosum("sabit sure sonu", OMUR, false),
  kosum("kayan sure sonu", OMUR, true)];
console.log(`\n${"politika".padEnd(16)}${"zirve giris".padStart(12)}${"zirve bayt".padStart(11)}` +
  `${"gun sonu giris".padStart(15)}${"gun sonu bayt".padStart(14)}${"iska".padStart(6)}${"fazladan giris".padStart(15)}`);
for (const [a, zn, zb, sn, sb, i] of satir)
  console.log(a.padEnd(16) + String(zn).padStart(12) + String(zb).padStart(11) +
    String(sn).padStart(15) + String(sb).padStart(14) + String(i).padStart(6) + String(i - UYE).padStart(15));
```

```
tohum 20240115; 20000 uye, 59337 dokunus, gun 43200 sn, omur 1800 sn
ornek oturum degeri 45 bayt, anahtar 22 bayt, ustveri 56 bayt

politika         zirve giris zirve bayt gun sonu giris gun sonu bayt  iska fazladan giris
sure sonu yok          20000    2397230          20000       2397230 20000              0
sabit sure sonu         2193     262936             28          3363 36951          16951
kayan sure sonu         2658     318688             29          3479 31182          11182
```

#### Süre Sonu Bir Bellek Politikasıdır

Üç koşum aynı 59.337 dokunuşu işliyor; ayrım yalnız ömür ayarındadır. Süre sonu verilmediğinde
depo günün sonunda 20.000 giriş ve **2.397.230 bayt** taşıyor ve bu sayı hiç küçülmüyor: her üye
bir kez giriş yaptığında kaydı süreç yaşadıkça durur. Buna karşılık her dokunuş kaydı yerinde
bulur; 20.000 ıska yalnız günün ilk dokunuşlarıdır ve kaçınılmazdır — kimse ilk kez giriş yapmadan
oturum bulamaz. Fazladan yeniden giriş sıfırdır.

Sabit süre sonu aynı işi **2.193 giriş** ve 262.936 bayt zirvesiyle yapıyor: dokuz kat az bellek.
Satın alınan şey açıkça görülüyor — bellek artık üye sayısıyla değil **eşzamanlı etkin üye
sayısıyla** ölçekleniyor, gün sonunda geriye 28 giriş kalıyor. Kaybedilen şey de sayılı: 16.951
fazladan yeniden giriş. Kaydı düşen üye kimliğini yeniden doğrulamak zorunda kalıyor.

Kayan süre sonu ikisinin arasındadır ve **satın alma oranını** görünür kılar. Zirve 318.688 bayta
çıkıyor, sabit politikaya göre 55.752 bayt fazla; karşılığında fazladan yeniden giriş 16.951'den
11.182'ye iniyor. Aradaki 5.769 girişin her biri **9,7 bayt zirve belleğe** mal oluyor. Ders bu
sayıyı öne alır: politika seçimi bir üslup tercihi değil, bayt cinsinden fiyatı olan bir alımdır.
Aynı hesap gün sonu belleğinde bambaşka durur — süre sonu olmayan politika 2,4 MB'ı kalıcı olarak
tutarken, iki süre sonu politikası 3,4 KB civarında kapanır; fark 700 kattır.

#### Anahtarın ve Değerin Biçimi

Giriş sayısı tek kaldıraç değildir. Aynı oturum farklı anahtar ve değer biçimlerinde tutulabilir
ve her biçim tutulan baytı doğrudan değiştirir. İkinci düzenek aynı üyeyi altı biçimde sayar ve
sonunda yapısal sayımı ortama bağlı bir ölçümle karşılaştırır.

```js
// bellek/gosterim.mjs — ayni oturumun anahtar ve deger gosterimleri: bicim basina
// tutulan bayt. USTVERI ayni sabittir; yapisal sayim kosumdan bagimsizdir.
const USTVERI = 56, GIRIS = 20_000, SUBE = ["merkez", "sahil", "universite", "cocuk", "arsiv"];
const oturum = (u) => ({ uye: u, sube: SUBE[u % 5], rol: u % 97 === 0 ? "gorevli" : "okur" });
const uye = 4242, jeton = (uye * 2654435761) % 4294967296, o = oturum(uye);

const bicim = [
  ["oturum:jeton:4242:...", `oturum:jeton:${uye}:${jeton}`],
  ["oturum:4242:...", `oturum:${uye}:${jeton}`],
  ["o:...", `o:${jeton}`],
];
const deger = [["kendini betimleyen", JSON.stringify(o)], ["konumsal", `${o.uye}|${o.sube}|${o.rol}`]];

console.log(`${"anahtar bicimi".padEnd(23)}${"anahtar".padStart(8)}${"deger bicimi".padStart(20)}` +
  `${"deger".padStart(7)}${"giris".padStart(7)}${`${GIRIS} giriste`.padStart(14)}`);
for (const [ad, a] of bicim)
  for (const [dad, d] of deger) {
    const g = Buffer.byteLength(a) + Buffer.byteLength(d) + USTVERI;
    console.log(ad.padEnd(23) + String(Buffer.byteLength(a)).padStart(8) + dad.padStart(20) +
      String(Buffer.byteLength(d)).padStart(7) + String(g).padStart(7) + String(g * GIRIS).padStart(14));
  }

// yeni bir alan (sube_kodu) ikinci siraya eklenirse ESKI girisler nasil okunur
const j = JSON.parse(deger[0][1]), [, k1, k2, k3] = deger[1][1].split("|");
console.log(`\neski giris, yeni sira (uye|sube_kodu|sube|rol) ile okunur:`);
console.log(`  kendini betimleyen -> sube=${j.sube} rol=${j.rol} sube_kodu=${j.sube_kodu}`);
console.log(`  konumsal           -> sube=${k2} rol=${k3} sube_kodu=${k1}`);

const once = process.memoryUsage().heapUsed;                    // ORTAMA BAGLI
const tablo = new Map();
for (let i = 1; i <= GIRIS; i += 1)
  tablo.set(`oturum:${i}:${(i * 2654435761) % 4294967296}`, { s: JSON.stringify(oturum(i)), sonErim: 1800 });
const sonra = process.memoryUsage().heapUsed;
const yapisal = [...tablo].reduce((t, [a, g]) => t + Buffer.byteLength(a) + Buffer.byteLength(g.s) + USTVERI, 0);
console.log(`\n${GIRIS} girisin yapisal sayimi ${yapisal} bayt (kosumdan bagimsiz); ` +
  `gercek yigin farki bunun ${((sonra - once) / yapisal).toFixed(1)} kati (ORTAMA BAGLI, kosumdan kosuma degisir)`);
```

```
anahtar bicimi          anahtar        deger bicimi  deger  giris 20000 giriste
oturum:jeton:4242:...        28  kendini betimleyen     45    129       2580000
oturum:jeton:4242:...        28            konumsal     20    104       2080000
oturum:4242:...              22  kendini betimleyen     45    123       2460000
oturum:4242:...              22            konumsal     20     98       1960000
o:...                        12  kendini betimleyen     45    113       2260000
o:...                        12            konumsal     20     88       1760000

eski giris, yeni sira (uye|sube_kodu|sube|rol) ile okunur:
  kendini betimleyen -> sube=universite rol=okur sube_kodu=undefined
  konumsal           -> sube=okur rol=undefined sube_kodu=universite

20000 girisin yapisal sayimi 2397230 bayt (kosumdan bagimsiz); gercek yigin farki bunun 2.5 kati (ORTAMA BAGLI, kosumdan kosuma degisir)
```

En pahalı biçimle en ucuzu arasında 20.000 girişte 820.000 bayt vardır — %32'lik bir fark, tek
satır kod değişikliğiyle. Anahtar adının 28 baytdan 12 bayta inmesi 320.000 bayt, değerin alan
adlarından arındırılması 500.000 bayt kazandırır.

İki kayıp da somuttur. Kısa anahtar, anahtar alanının okunabilirliğini götürür: `oturum:` öneki
olmadan aynı alanda hangi girişin oturum, hangisinin önbellek kaydı olduğu ayırt edilemez ve iki
farklı iş aynı adı üretirse çakışma sessizce olur. Konumsal değerin kaybı daha serttir. Çıktının
ortasındaki üç satır bunu gösteriyor: `sube_kodu` alanı sonradan ikinci sıraya eklendiğinde,
kendini betimleyen değer eski girişleri hâlâ doğru çözüyor (`sube=universite`, `rol=okur`) ve
yalnız yeni alanı boş buluyor. Konumsal değer aynı girişi **yanlış** çözüyor: şube alanına `okur`,
şube koduna `universite` düşüyor. Kazanılan 500.000 bayt, verinin kendini anlatma yeteneğiyle
ödenmiştir.

Son satır ölçünün sınırını çizer. Yapısal sayım 2.397.230 bayt verir ve ilk düzeneğin gün sonu
sayısıyla birebir aynıdır; gerçek yığın kullanımı ise bunun 2,5 katıdır. Bu ikinci sayı **ortama
bağlıdır** ve koşumdan koşuma değişir — yapısal sayımın bir **alt sınır** olduğunu söyler, o kadar;
kurs boyunca karar verdiren ölçü birincisidir. İkisi arasındaki oran şunu da söyler: 56 baytlık üstveri en ucuz
biçimde girişin **%64'ünü** kaplar. Girişler küçüldükçe biçim iyileştirmesinin getirisi azalır;
asıl kaldıraç girişin biçimi değil, **giriş sayısıdır** — yani süre sonu politikasıdır.

#### Özet

- Bellek içi depo üç işlem tanır: ayarlama, okuma, ömür uzatma. Değer depo için saydam değildir;
  değişen tek şey kaydın diskte değil bellekte durmasıdır ve bu, yeri bir kapasite kalemi olmaktan
  çıkarıp bütçeye çevirir.
- Tutulan bayt `anahtar + değer + 56` olarak sayılır. 20.000 oturum kaydı 2.397.230 bayt tutar;
  giriş başına ortalama 120 bayttır.
- Süre sonu bir bellek politikasıdır: sabit süre sonu zirveyi 20.000 girişten 2.193 girişe ve
  2.397.230 bayttan 262.936 bayta indirir, karşılığında 16.951 fazladan yeniden giriş yazdırır.
- Kayan süre sonu 55.752 bayt fazla tutup 5.769 yeniden girişi önler — önlenen giriş başına
  9,7 bayt. Politika seçimi bayt cinsinden fiyatı olan bir alımdır.
- Anahtar ve değer biçimi 20.000 girişte 820.000 bayt fark ettirir; konumsal değer bu kazancı
  alan sırasını depo dışında tutarak alır ve şema değiştiğinde eski girişleri sessizce yanlış
  çözer.
- Yapısal sayım koşumdan bağımsızdır ve karar verdiren ölçüdür; gerçek yığın kullanımı (yapısal
  sayımın 2,5 katı) ortama bağlıdır ve yalnız yapısal sayımın bir alt sınır olduğunu gösterir.

#### Sonraki Adım

Bu dersteki her yazma bütün değeri baştan yazdı: oturum kaydı okundu, değiştirildi, geri kondu.
Kütüphanenin sayması gereken şeyler bu kalıba uymuyor. Bir kitabın kaç kez ödünç alındığı,
bir şubenin gün içinde kaç ödünç verdiği, bir üyenin gecikme sayısı — hepsi tek bir sayıdır ve
her istekte bir artar. Aynı sayacı iki istek aynı anda okuyup geri yazdığında ne olduğu bu derste
hiç sorulmadı. Sonraki ders bunu ölçüyor: oku-değiştir-yaz ile deponun kendi artırmasının
eşzamanlı istekte ürettiği fark, kaybolan güncelleme sayısı ve tek bir sayacın 200.000 kitaplık
katalogda kaç bayta mal olduğu.

### Dizgiler ve Sayaçlar

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/dizgiler-ve-sayaclar

Atomik artırmanın ne satın aldığı: aynı ödünç sayacının oku-değiştir-yaz ile ve deponun tek adımlık artırmasıyla güncellenmesi, eşzamanlı şube sayısına göre kaybolan güncellemenin ve gidiş sayısının ölçülmesi, bir sayacın bellekteki gerçek fiyatının anahtar–değer–üstveri olarak ayrıştırılması, sayaç tutmayan ve yalnız sıcak kitaplar için sayaç tutan seçeneklerin bayt başına önlenen adım cinsinden karşılaştırılması.

Önceki dersin her yazması bütün değeri baştan yazdı. Kütüphanenin saydığı şeylerin çoğu bu kalıba
uymaz: bir kitabın ödünç sayısı, bir şubenin günlük ödünç toplamı, bir üyenin gecikme sayısı —
hepsi tek bir sayıdır ve her olayda bir artar. Bu ders o sayıyı iki soruyla ele alıyor: sayacı
artırmanın **kim tarafından** yapıldığı neyi değiştirir ve tek bir sayı bellekte gerçekte kaç
bayta mal olur.

Depo değeri bir bayt dizisi olarak tutar; sayı da bir dizgidir. Ama depo o dizgiyi sayı gibi
yorumlayıp **tek adımda** artırabilir. Bu yeteneğin adı atomik artırmadır ve ilk bakışta bir
kolaylık gibi görünür. Ölçüldüğünde bir kolaylık değil, doğruluğun kendisi olduğu çıkar.

#### Sayacı Kim Artırıyor

İki yol vardır. **Oku-değiştir-yaz** üç ayrı adımdır: değer okunur, uygulamada bir artırılır, geri
yazılır. **Atomik artırma** tek adımdır: depo değeri okur, ekler ve yazar; arada başka hiçbir iş
araya giremez. Fark yalnız eşzamanlılık altında görünür.

Ölçüm için popüler bir kitabın ödünç sayacı kullanılır. **BY1:** aynı kitabı 1, 2, 4, 8 ve 16 şube
eşzamanlı olarak ödünç veriyor ve her şube 500 ödünç kaydediyor; beklenen sayaç değeri şube sayısı
× 500'dür. **BY2:** eşzamanlılık üreteçlerle kurulur, her `yield` bir kesilme noktasıdır ve
zamanlayıcı hazır istemciler arasından tohumu görünür bir üreticiyle seçer; koşum belirlenimlidir.

```js
// bellek/sayac.mjs — ayni odunc sayacinin iki yolla artirilmasi. Istemciler uretec,
// zamanlayici tohumlu; adim sirasi kosumdan bagimsizdir.
class BellekIciDepo {
  #tablo = new Map();
  gidis = 0;
  oku(a) { this.gidis += 1; const s = this.#tablo.get(a); return s === undefined ? 0 : Number(s); }
  ayarla(a, v) { this.gidis += 1; this.#tablo.set(a, String(v)); }
  artir(a, d) {                                // tek adimda okur, ekler, yazar
    this.gidis += 1;
    const v = (this.#tablo.has(a) ? Number(this.#tablo.get(a)) : 0) + d;
    this.#tablo.set(a, String(v)); return v; }
}

function* okuDegistirYaz(d, a, kez) {
  for (let i = 0; i < kez; i += 1) { const v = d.oku(a); yield; d.ayarla(a, v + 1); yield; }
}
function* atomik(d, a, kez) {
  for (let i = 0; i < kez; i += 1) { d.artir(a, 1); yield; }
}

const TOHUM = 20240115, KEZ = 500, ANAHTAR = "kitap:100427:odunc";
function zamanla(uretecler) {                  // hazir istemcilerden tohumlu secim
  let c = TOHUM;
  const rast = () => (c = (c * 1103515245 + 12345) % 2147483648) / 2147483648;
  const canli = [...uretecler];
  while (canli.length > 0) {
    const i = Math.floor(rast() * canli.length);
    if (canli[i].next().done) canli.splice(i, 1);
  }
}

console.log(`tohum ${TOHUM}; her sube ${KEZ} odunc kaydeder, anahtar ${ANAHTAR}`);
console.log(`${"sube".padStart(5)}${"beklenen".padStart(10)}${"oku-degistir-yaz".padStart(18)}` +
  `${"kayip".padStart(7)}${"gidis".padStart(8)}${"atomik artirma".padStart(16)}${"kayip".padStart(7)}${"gidis".padStart(7)}`);
for (const sube of [1, 2, 4, 8, 16]) {
  const beklenen = sube * KEZ;
  const d1 = new BellekIciDepo();
  zamanla(Array.from({ length: sube }, () => okuDegistirYaz(d1, ANAHTAR, KEZ)));
  const d2 = new BellekIciDepo();
  zamanla(Array.from({ length: sube }, () => atomik(d2, ANAHTAR, KEZ)));
  const s1 = d1.oku(ANAHTAR), s2 = d2.oku(ANAHTAR);
  console.log(String(sube).padStart(5) + String(beklenen).padStart(10) + String(s1).padStart(18) +
    String(beklenen - s1).padStart(7) + String(d1.gidis - 1).padStart(8) + String(s2).padStart(16) +
    String(beklenen - s2).padStart(7) + String(d2.gidis - 1).padStart(7));
}
```

```
tohum 20240115; her sube 500 odunc kaydeder, anahtar kitap:100427:odunc
 sube  beklenen  oku-degistir-yaz  kayip   gidis  atomik artirma  kayip  gidis
    1       500               500      0    1000             500      0    500
    2      1000               620    380    2000            1000      0   1000
    4      2000               747   1253    4000            2000      0   2000
    8      4000               871   3129    8000            4000      0   4000
   16      8000               938   7062   16000            8000      0   8000
```

Tek şube çalışırken iki yol da doğru sonucu veriyor; ayrım ikinci şubeyle başlıyor. İki şubede
1.000 ödüncün 380'i kayboluyor, on altı şubede 8.000 ödüncün **7.062'si**. Sayaç 938'de kalıyor:
kütüphane sekiz bin ödünç verdi, kaydında dokuz yüz otuz sekiz yazıyor. Kaybolan güncelleme bir
gecikme ya da yavaşlama değil, **yanlış sayıdır** ve hiçbir yerde hata olarak görünmez.

Nedeni okuma ile yazma arasındaki aralıktır. Bir şube 41 değerini okuyup 42 yazana kadar başka
şubeler de 41 okumuş olur; hepsi 42 yazar ve dördü birden tek bir artırma yapmış olur. Atomik
artırmada bu aralık yoktur — depo değeri okur, ekler ve yazar; işlem bölünmez. On altı şubede
kayıp sıfırdır.

Gidiş sütunu ikinci kazancı gösteriyor: oku-değiştir-yaz her artırma için iki gidiş harcarken
atomik artırma bir gidişte biter, 16.000 yerine 8.000. Aynı doğruluğu oku-değiştir-yaz ile elde
etmenin yolu uygulamada bir kilit tutmaktır; o da kilidin kendisini bellekte tutmak ve şubeleri
sıraya dizmek demektir. Atomik artırma **hem bir gidişi hem de kilidin belleğini** ortadan
kaldırır — ve karşılığında hiçbir ek bayt istemez, çünkü artırma deponun kendi işidir.

#### Bir Sayının Bellekteki Fiyatı

Sayaç doğru arttı; şimdi asıl soru gelir. Kataloğun tamamı için ödünç sayacı tutmak bellekte ne
kadar yer kaplar. **BY3:** katalog 200.000 kitap taşır, her kitabın sayaç değeri belirlenimli bir
işlevden gelir ve 1 ile 4.096 arasındadır; ödünç kütüğü 119.993 kayıttır ve bellek içi deponun
dışındadır — taranması adım sayar, bayt tutmaz. **BY4:** sorgu yükü 10.000 "bu kitap kaç kez ödünç
alındı" sorusudur ve popülerliğe eğiktir; eğim aynı tohumlu üreticiden gelir.

```js
// bellek/sayac-butce.mjs — bir sayacin bellekteki fiyati ve sayac tutmayan iki secenek.
// Odunc kutugu bellek ici deponun disindadir; taranmasi adim sayar, bayt tutmaz.
const USTVERI = 56, KITAP = 200_000, KUTUK = 119_993, SORGU = 10_000, SICAK = 1_000, TOHUM = 20240115;
let c = TOHUM;
const rast = () => (c = (c * 1103515245 + 12345) % 2147483648) / 2147483648;

// sayac degeri: kitap i icin gunluk odunc sayisi (belirlenimli)
const sayacDegeri = (i) => 1 + ((i * 7919) % 4096);
const anahtar = (i) => `kitap:${100_000 + i}:odunc`;

let toplamA = 0, toplamD = 0, toplamU = 0;
for (let i = 1; i <= KITAP; i += 1) {
  toplamA += Buffer.byteLength(anahtar(i));
  toplamD += Buffer.byteLength(String(sayacDegeri(i)));
  toplamU += USTVERI;
}
const tam = toplamA + toplamD + toplamU;
console.log(`${KITAP} kitap, ornek giris "${anahtar(427)}" -> "${sayacDegeri(427)}"`);
console.log(`tam sayac: anahtar ${toplamA} + deger ${toplamD} + ustveri ${toplamU} = ${tam} bayt` +
  ` (giris basina ${(tam / KITAP).toFixed(1)}; degerin payi %${(100 * toplamD / tam).toFixed(1)})`);

// sorgu yuku: "bu kitap kac kez odunc alindi" — populerlige egik, tohumlu
const sorgu = Array.from({ length: SORGU }, () => 1 + Math.floor(KITAP * rast() ** 6));
const sicakSorgu = sorgu.filter((i) => i <= SICAK).length;
const kismi = Array.from({ length: SICAK }, (_, j) =>
  Buffer.byteLength(anahtar(j + 1)) + Buffer.byteLength(String(sayacDegeri(j + 1))) + USTVERI)
  .reduce((t, x) => t + x, 0);

const taban = SORGU * KUTUK;                   // sayac tutulmadigindaki adim
const yol = [
  ["sayac yok, kutuk taramasi", 0, 0, taban],
  [`kismi sayac (ust ${SICAK})`, SICAK, kismi, sicakSorgu + (SORGU - sicakSorgu) * KUTUK],
  ["tam sayac", KITAP, tam, SORGU],
];
console.log(`\n${SORGU} sorgu, ${sicakSorgu} tanesi ilk ${SICAK} kitaba (%${(100 * sicakSorgu / SORGU).toFixed(1)})`);
console.log(`${"yol".padEnd(27)}${"giris".padStart(7)}${"tutulan bayt".padStart(14)}` +
  `${"sorgu basina adim".padStart(19)}${"bayt basina onlenen adim".padStart(26)}`);
for (const [ad, n, b, adim] of yol)
  console.log(ad.padEnd(27) + String(n).padStart(7) + String(b).padStart(14) +
    (adim / SORGU).toFixed(1).padStart(19) + (b === 0 ? "-" : ((taban - adim) / b).toFixed(1)).padStart(26));
```

```
200000 kitap, ornek giris "kitap:100427:odunc" -> "2214"
tam sayac: anahtar 3600000 + deger 745901 + ustveri 11200000 = 15545901 bayt (giris basina 77.7; degerin payi %4.8)

10000 sorgu, 4149 tanesi ilk 1000 kitaba (%41.5)
yol                          giris  tutulan bayt  sorgu basina adim  bayt basina onlenen adim
sayac yok, kutuk taramasi        0             0           119993.0                         -
kismi sayac (ust 1000)        1000         77662            70208.3                    6410.4
tam sayac                   200000      15545901                1.0                      77.2
```

İlk satır dersin başlığını taşır: 200.000 sayaç **15.545.901 bayt** tutar, giriş başına 77,7. Oysa
saklanan bilgi dört basamaklı bir sayıdır. Sayının kendisi toplamın yalnız **%4,8'ini** kaplıyor;
anahtar adı %23,2'sini, giriş üstverisi **%72'sini**. Bellek içi depoda bir sayı tutmak dört bayt
değil yetmiş yedi bayttır. Bu oran her küçük değer için geçerlidir ve tek kaldıraç değerin
kendisi değil, **kaç anahtar açıldığıdır**.

Alt tablo üç seçeneği aynı yükte karşılaştırıyor. Sayaç hiç tutulmazsa bellek sıfırdır, her sorgu
119.993 kayıtlık kütüğü tarar. Tam sayaçla bellek 15,5 MB'a çıkar ve sorgu başına adım **1'e**
iner: 15,5 MB, sorgu başına 119.992 adım satın alıyor.

Son sütun kararı asıl kuran sayıdır. Yalnız ilk 1.000 kitap için sayaç tutmak 77.662 bayt ister —
tam sayacın **iki yüzde biri** — ve sorguların %41,5'ini tek adıma indirir. Tuttuğu **her bayt
6.410 adım** önlüyor; tam sayaçta aynı rakam 77,2. Kısmi sayaç bayt başına seksen üç kat verimli.
Ama mutlak sayı hâlâ acımasızdır: sorgu başına 70.208 adım, çünkü ıskalayan %58,5'in her biri
bütün kütüğü tarar. Bellek bütçesi darsa kısmi sayaç en verimli alımdır; sorgu başına sabit adım
gerekiyorsa 15,5 MB'ın alternatifi yoktur. İkisi arasında karar veren şey "hız" değil, ıskalama
oranı ile bütçenin çarpımıdır.

#### Özet

- Sayacın iki güncelleme yolu vardır: oku-değiştir-yaz üç adımdır ve okuma ile yazma arasında
  başka istemcilere açıktır; atomik artırma deponun tek bölünmez adımıdır.
- Eşzamanlılık arttıkça kayıp büyür: 2 şubede 1.000 ödüncün 380'i, 16 şubede 8.000 ödüncün 7.062'si
  kayboluyor ve sayaç 938'de kalıyor. Atomik artırmada kayıp her düzeyde sıfırdır.
- Atomik artırma gidişi de yarıya indirir (16.000 yerine 8.000) ve doğruluk için uygulamada kilit
  tutmayı gereksiz kılar; kilit hem bellek hem bekleme demektir.
- 200.000 ödünç sayacı 15.545.901 bayt tutar. Sayının kendisi bunun %4,8'i, anahtar adı %23,2'si,
  giriş üstverisi %72'sidir: bellek içi depoda bir sayı 77,7 bayttır.
- Üç seçenek aynı yükte: sayaçsız 0 bayt ve sorgu başına 119.993 adım; kısmi sayaç 77.662 bayt ve
  70.208 adım; tam sayaç 15.545.901 bayt ve 1 adım.
- Bayt başına önlenen adım kısmi sayaçta 6.410, tam sayaçta 77,2. Kısmi sayaç bütçe başına çok daha
  verimlidir ama sorgu başına sabit adım güvencesi vermez.

#### Sonraki Adım

Sayaç "kaç" sorusunu tek bayt mertebesinde yanıtlıyor, ama kütüphanenin ikinci sorusunu hiç
karşılamıyor: popüler bir kitabı bekleyenler **kimler** ve **hangi sırada**. Bekleme listesi bir
sayı değil bir dizidir; sıra bilgisinin kendisi bilginin bir parçasıdır ve depo onu koruyacaksa
her üyeyi ayrı ayrı tutmak zorundadır. Sonraki ders listeyi ele alıyor ve üç şeyi ölçüyor: iki
uçtan ekleme ile çıkarmanın adım sayısı, listenin büyümesinin belleğe etkisi ve sınırlı bir
listenin — belli bir uzunlukta kırpılanın — sınırsız olana göre neyi ucuzlattığı ile neyi
sessizce kaybettirdiği.

### Listeler

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/listeler

Sıranın kendisini veri olarak tutmanın bedeli: aynı bekleme listesi iş yükünün bitişik dizi, bağlı düğüm ve halka tampon gerçekleştirimlerinde adım ve giriş başına bayt cinsinden ölçülmesi, iki uçtan sabit adımlı erişimin giriş başına kaç bayta satın alındığı, sıra sorgusunun üç yapıda da tarama olması ve son etkinlik listesinin kırpılmasının tutulan baytı düşürürken kapsanan zaman penceresini ve karşılanan sorgu oranını nasıl daralttığı.

Sayaç "kaç" sorusunu tek bir sayıyla yanıtladı. Kütüphanenin ikinci sorusu bir sayıyla
yanıtlanmaz: popüler bir kitabı bekleyenler kimlerdir ve **hangi sırada**. Bekleme listesinde
sıranın kendisi bilginin bir parçasıdır; depo onu koruyacaksa her üyeyi ayrı ayrı ve konumuyla
birlikte tutmak zorundadır.

Liste tek bir yapıdır, iki ayrı kullanımı vardır. **Kuyruk** bir uçtan eklenip öteki uçtan
çıkarıldığında ortaya çıkar: bekleme listesi böyledir, ilk isteyen ilk alır. **Yığıt** aynı uçtan
eklenip aynı uçtan çıkarıldığında ortaya çıkar: görevlinin son işlemini geri alması böyledir.
Depo açısından fark yapıda değil, hangi ucun seçildiğindedir. Asıl soru şudur: iki uca da sabit
adımda erişmek bellekte kaç bayta mal olur.

#### Aynı İş Yükü, Üç Gerçekleştirim

**BY1:** bekleme listesine 20.000 istek sondan eklenir, her dördüncü istekten sonra baştan bir
karşılama yapılır (5.000 çıkarma) ve her kırkıncı istekte bir öncelikli istek başa alınır (500
ekleme); toplam 25.500 işlem. **BY2:** dizi yuvası 8 bayt, bağlı düğümün ek maliyeti 24 bayttır
(önceki ve sonraki göstergeler ile düğüm başlığı), yapı başına üstveri 56 bayttır. **BY3:** halka
tampon 1.024 yuvayla başlar ve dolduğunda kapasitesi iki katına çıkar; kopyalama adımları
sayılır.

```js
// bellek/liste.mjs — ayni bekleme listesi is yuku uc gerceklestirimde. Adim ve bayt
// sayaclari yapinin kendi icindedir; is yuku belirlenimlidir.
const USTVERI = 56, YUVA = 8, DUGUM_EK = 24;   // BY2

class DiziListe {                              // bitisik dizi: bastan islem kaydirma ister
  #a = []; adim = 0;
  sonaEkle(v) { this.adim += 1; this.#a.push(v); }
  basaEkle(v) { this.adim += this.#a.length + 1; this.#a.unshift(v); }
  bastanCikar() { this.adim += this.#a.length; return this.#a.shift(); }
  get uzunluk() { return this.#a.length; }
  get bayt() { return USTVERI + this.#a.reduce((t, v) => t + Buffer.byteLength(v) + YUVA, 0); }
  sira(v) { let n = 0; for (const x of this.#a) { n += 1; if (x === v) return n; } return -1; }
}

class BagliListe {                             // iki uctan da tek adim; dugum basina ek gosterge
  #bas = null; #son = null; #n = 0; #b = 0; adim = 0;
  #dugum(v) { this.#n += 1; this.#b += Buffer.byteLength(v) + YUVA + DUGUM_EK; return { v, on: null, ar: null }; }
  sonaEkle(v) { this.adim += 1; const d = this.#dugum(v);
    d.on = this.#son; if (this.#son) this.#son.ar = d; else this.#bas = d; this.#son = d; }
  basaEkle(v) { this.adim += 1; const d = this.#dugum(v);
    d.ar = this.#bas; if (this.#bas) this.#bas.on = d; else this.#son = d; this.#bas = d; }
  bastanCikar() { this.adim += 1; const d = this.#bas; if (!d) return undefined;
    this.#bas = d.ar; if (this.#bas) this.#bas.on = null; else this.#son = null;
    this.#n -= 1; this.#b -= Buffer.byteLength(d.v) + YUVA + DUGUM_EK; return d.v; }
  get uzunluk() { return this.#n; }
  get bayt() { return USTVERI + this.#b; }
  sira(v) { let n = 0; for (let d = this.#bas; d; d = d.ar) { n += 1; if (d.v === v) return n; } return -1; }
}

class HalkaTampon {                            // sabit yuva; dolunca kapasite iki katina cikar
  #a; #bas = 0; #n = 0; adim = 0; kopya = 0; buyume = 0;
  constructor(kapasite) { this.#a = new Array(kapasite); }
  #buyut() { if (this.#n < this.#a.length) return;
    const yeni = new Array(this.#a.length * 2);
    for (let i = 0; i < this.#n; i += 1) yeni[i] = this.#a[(this.#bas + i) % this.#a.length];
    this.adim += this.#n; this.kopya += this.#n; this.buyume += 1; this.#a = yeni; this.#bas = 0; }
  sonaEkle(v) { this.#buyut(); this.adim += 1; this.#a[(this.#bas + this.#n) % this.#a.length] = v; this.#n += 1; }
  basaEkle(v) { this.#buyut(); this.adim += 1;
    this.#bas = (this.#bas - 1 + this.#a.length) % this.#a.length; this.#a[this.#bas] = v; this.#n += 1; }
  bastanCikar() { this.adim += 1; if (this.#n === 0) return undefined;
    const v = this.#a[this.#bas]; this.#a[this.#bas] = undefined;
    this.#bas = (this.#bas + 1) % this.#a.length; this.#n -= 1; return v; }
  get uzunluk() { return this.#n; }
  get kapasite() { return this.#a.length; }
  get bayt() { let d = 0;
    for (let i = 0; i < this.#n; i += 1) d += Buffer.byteLength(this.#a[(this.#bas + i) % this.#a.length]);
    return USTVERI + this.#a.length * YUVA + d; }
  sira(v) { for (let i = 0; i < this.#n; i += 1) if (this.#a[(this.#bas + i) % this.#a.length] === v) return i + 1; return -1; }
}

// --- is yuku: 20.000 istek sona, her 4 istekte 1 karsilama bastan, her 40 istekte 1 oncelikli basa
const ISTEK = 20_000;
const deger = (i) => `${10_000 + i}:${40_000 + i * 2}`;
const islem = [];
for (let i = 1; i <= ISTEK; i += 1) {
  islem.push(["sona", deger(i)]);
  if (i % 4 === 0) islem.push(["cikar", null]);
  if (i % 40 === 0) islem.push(["basa", `9${deger(i)}`]);
}
const kosum = (l) => { for (const [k, v] of islem) {
  if (k === "sona") l.sonaEkle(v); else if (k === "basa") l.basaEkle(v); else l.bastanCikar(); } return l; };

const h = new HalkaTampon(1024);
const yapi = [["bitisik dizi", kosum(new DiziListe())], ["bagli dugum", kosum(new BagliListe())],
  ["halka tampon", kosum(h)]];
console.log(`${islem.length} islem (${ISTEK} sona, ${ISTEK / 4} bastan cikarma, ${ISTEK / 40} basa)`);
console.log(`${"yapi".padEnd(15)}${"uzunluk".padStart(9)}${"tutulan bayt".padStart(14)}` +
  `${"giris basina".padStart(13)}${"toplam adim".padStart(13)}${"islem basina".padStart(14)}`);
for (const [ad, l] of yapi)
  console.log(ad.padEnd(15) + String(l.uzunluk).padStart(9) + String(l.bayt).padStart(14) +
    (l.bayt / l.uzunluk).toFixed(1).padStart(13) + String(l.adim).padStart(13) +
    (l.adim / islem.length).toFixed(1).padStart(14));
console.log(`halka tampon: ${h.buyume} buyume, ${h.kopya} kopyalama adimi, kapasite ${h.kapasite}, ` +
  `bos yuva ${h.kapasite - h.uzunluk}`);

const hedef = deger(19_000);
console.log(`\n"${hedef}" kacinci sirada: ` + yapi.map(([ad, l]) => `${ad} -> ${l.sira(hedef)}. sira`).join(", "));
```

```
25500 islem (20000 sona, 5000 bastan cikarma, 500 basa)
yapi             uzunluk  tutulan bayt giris basina  toplam adim  islem basina
bitisik dizi       15500        294557         19.0     42662750        1673.0
bagli dugum        15500        666557         43.0        25500           1.0
halka tampon       15500        301629         19.5        40860           1.6
halka tampon: 4 buyume, 15360 kopyalama adimi, kapasite 16384, bos yuva 884

"29000:78000" kacinci sirada: bitisik dizi -> 14500. sira, bagli dugum -> 14500. sira, halka tampon -> 14500. sira
```

Üç yapı aynı 15.500 kişilik bekleme listesini taşıyor; tuttukları bayt ve harcadıkları adım
birbirinden çok uzak. Bitişik dizi giriş başına **19,0 bayt** ile en ucuzudur ama baştan yapılan
her işlem kalan bütün girişleri kaydırdığı için işlem başına 1.673 adım harcar; 25.500 işlemin
toplamı 42,7 milyon adımdır. Bekleme listesinden birinin çağrılması, listedeki herkesin yerinin
değişmesi demektir.

Bağlı düğüm bunu tersine çevirir: her işlem tam **1 adım**, toplam 25.500. Fiyatı giriş başına
43,0 bayttır — dizi maliyetinin 2,26 katı. Fark yalnız gösterge maliyetidir: her giriş, iki komşu
göstergesi ve düğüm başlığı için fazladan 24 bayt taşır. Bir üye kimliğinin kendisi 11 bayt iken
onu sıraya bağlamak 24 bayt istiyor; **veri kadar yer, verinin yerini tarif etmeye gidiyor.**

Halka tampon üçüncü yolu gösteriyor ve bu iş yükünde en iyi alımdır: giriş başına 19,5 bayt —
diziden yalnız 0,5 bayt fazla — ve işlem başına 1,6 adım. Bir baş göstergesi tutarak baştan
çıkarmayı kaydırma olmaktan çıkarır. Bedeli kapasite yönetimidir: 1.024 yuvayla başlayıp dört kez
büyümüş, 15.360 kopyalama adımı harcamış ve sonunda 884 boş yuvayı elde tutuyor. Bağlı düğümün
halka tampona göre satın aldığı tek şey o 15.360 adımdır ve fiyatı 364.928 bayttır: **adım başına
23,8 bayt.** Bu, bekleme listesi için kötü bir alımdır; kararın yönü yapının adıyla değil bu
oranla verilir.

Son satır üçünün de ortak sınırını gösteriyor. Belirli bir üyenin kaçıncı sırada olduğu üç yapıda
da 14.500. sıradadır ve bulunması için 14.500 giriş gezilir. Liste sırayı **tutar** ama sıraya
göre **arama** vermez; üyelikten konuma gitmek her durumda taramadır.

#### Kırpmanın Fiyatı

İkinci kullanım son etkinlik listesidir: her ödünç, iade, uzatma ve rezerv listenin sonuna
yazılır ve görevli paneli "son m etkinlik" diye sorar. Bu liste hiç çıkarılmazsa gün boyunca
büyür. **BY4:** gün 43.200 saniyedir ve 60.000 etkinlik üretir; giriş ortalama 24 bayttır.
**BY5:** 2.000 sorgu gelir, istenen m küçük değerlere eğiktir ve tohumu görünür bir üreticiden
gelir; kırpılmış liste halka tampon maliyetiyle sayılır.

```js
// bellek/kirpma.mjs — son etkinlik listesi: sinirsiz tutmak ile belli uzunlukta kirpmak.
// Kirpilan liste halka tampon maliyetiyle sayilir: 56 + kapasite*8 + canli deger baytlari.
const USTVERI = 56, YUVA = 8, GUN = 43_200, ETKINLIK = 60_000, SORGU = 2_000, TOHUM = 20240115;
let c = TOHUM;
const rast = () => (c = (c * 1103515245 + 12345) % 2147483648) / 2147483648;

const TUR = ["odunc", "iade", "uzatma", "rezerv"];
const etkinlik = Array.from({ length: ETKINLIK }, (_, i) =>
  `${Math.floor(i * GUN / ETKINLIK)}|${10_000 + (i * 7919) % 20_000}|${TUR[i % 4]}|${100_000 + (i * 4241) % 200_000}`);
const degerBayt = etkinlik.map((e) => Buffer.byteLength(e));
const toplamBayt = degerBayt.reduce((t, x) => t + x, 0);

// sorgu: "son m etkinlik" — kucuk m'ye egik, tohumlu
const sorgu = Array.from({ length: SORGU }, () => 1 + Math.floor(3_000 * rast() ** 2));

console.log(`${ETKINLIK} etkinlik / ${GUN} sn, ornek "${etkinlik[41]}" (${degerBayt[41]} bayt), ` +
  `ortalama ${(toplamBayt / ETKINLIK).toFixed(1)} bayt`);
console.log(`${SORGU} sorgu, en buyuk m ${Math.max(...sorgu)}, ortanca m ${[...sorgu].sort((a, b) => a - b)[SORGU / 2]}`);
console.log(`\n${"sinir".padStart(9)}${"tutulan bayt".padStart(14)}${"dusurulen".padStart(11)}` +
  `${"kapsanan sn".padStart(13)}${"karsilanan sorgu".padStart(18)}${"oran".padStart(8)}`);
for (const k of [100, 1_000, 10_000, ETKINLIK]) {
  const canli = etkinlik.slice(ETKINLIK - k);
  const bayt = USTVERI + k * YUVA + degerBayt.slice(ETKINLIK - k).reduce((t, x) => t + x, 0);
  const kapsam = Number(canli.at(-1).split("|")[0]) - Number(canli[0].split("|")[0]);
  const karsilanan = sorgu.filter((m) => m <= k).length;
  console.log(String(k === ETKINLIK ? "sinirsiz" : k).padStart(9) + String(bayt).padStart(14) +
    String(ETKINLIK - k).padStart(11) + String(kapsam).padStart(13) +
    String(karsilanan).padStart(18) + `%${(100 * karsilanan / SORGU).toFixed(1)}`.padStart(8));
}
```

```
60000 etkinlik / 43200 sn, ornek "29|14679|iade|273881" (20 bayt), ortalama 24.0 bayt
2000 sorgu, en buyuk m 2999, ortanca m 752

    sinir  tutulan bayt  dusurulen  kapsanan sn  karsilanan sorgu    oran
      100          3281      59900           71               368   %18.4
     1000         32306      59000          719              1167   %58.4
    10000        322556      50000         7199              2000  %100.0
 sinirsiz       1919625          0        43199              2000  %100.0
```

Sınırsız liste günü 1.919.625 baytla kapatıyor ve ertesi gün aynı hızla büyümeye devam eder;
bir listenin doğal bir durma noktası yoktur. 10.000'lik kırpma aynı 2.000 sorgunun **tamamını**
karşılıyor ve bunu sınırsız listenin %16,8'i kadar bellekle yapıyor: 1,6 MB tasarrufun sorgu
tarafında bedeli sıfırdır.

Bedelin nerede olduğunu **kapsanan sn** sütunu söylüyor. 10.000 giriş yalnız son 7.199 saniyeyi,
yani iki saati taşır; sınırsız liste on iki saati taşır. Bu iş yükünde kimse iki saatten geriye
sormadığı için kayıp görünmez — ama görünmez olması yok olduğu anlamına gelmez. Sınır 1.000'e
çekildiğinde bellek 32.306 bayta iner ve liste yalnız **719 saniyeyi**, on iki dakikayı kapsar;
sorguların %41,6'sı eksik yanıt alır. 100'lük sınırda kapsam 71 saniyeye, karşılama %18,4'e düşer.

Buradaki asıl uyarı sayıların kendisinde değil, hatanın biçimindedir. Kırpılmış liste "elimde
yok" demez; **elindeki kadarını verir.** 2.400 etkinlik isteyen bir sorgu, 1.000 sınırlı listeden
1.000 kayıt alır ve yanıtı eksik olduğunu bilmeden kullanır. Sınır, bellek bütçesiyle sorgu
dağılımının kesiştiği yerde seçilir ve dağılım değiştiğinde sessizce yanlış yanıt üretmeye
başlar.

#### Özet

- Kuyruk ve yığıt ayrı yapılar değildir; aynı listenin hangi ucundan eklenip çıkarıldığıdır.
  Bekleme listesi kuyruk, görevlinin geri alma dizisi yığıttır.
- Aynı 25.500 işlemde bitişik dizi giriş başına 19,0 bayt tutar ama işlem başına 1.673 adım
  harcar; bağlı düğüm işlem başına 1 adıma iner ve giriş başına 43,0 bayt ister (2,26 kat).
- Halka tampon ikisinin arasını kapatır: 19,5 bayt ve 1,6 adım. Bağlı düğümün ona göre satın
  aldığı 15.360 adımın fiyatı 364.928 bayttır — adım başına 23,8 bayt.
- Sıra sorgusu üç yapıda da taramadır: hedef giriş 14.500. sıradadır ve bulunması 14.500 adım
  ister. Liste sırayı tutar, sıraya göre arama vermez.
- Son etkinlik listesi kırpılmadığında günü 1.919.625 baytla kapatır ve durma noktası yoktur.
  10.000'lik sınır belleği %16,8'e indirir ve bu iş yükündeki 2.000 sorgunun tamamını karşılar.
- Kırpmanın gerçek bedeli kapsanan zaman penceresidir: 10.000 giriş iki saati, 1.000 giriş on iki
  dakikayı taşır. Kırpılmış liste eksik yanıt verdiğini bildirmez, elindeki kadarını verir.

#### Sonraki Adım

Buraya kadar depoya konan her şey tek parça bir değerdi: oturum kaydı bir bütün, sayaç bir sayı,
liste girişi bir dizgi. Kütüphanenin asıl kaydı böyle değildir. Bir kitabın adı, yazarı, raf
kodu, durumu ve ödünç sayısı vardır; durum saatte birkaç kez değişirken ad hiç değişmez. Bütün
kaydı tek değerde tutmak, tek alanı güncellemek için bütün kaydı yazmak demektir. Sonraki ders
bu kaydı iki biçimde kurup üçünü ölçüyor: tek alan güncellemesinde yazılan bayt, tek alan
okumasında okunan bayt ve alanları ayrı ayrı yönetmenin üstveri olarak geri istediği pay.

### Karma Yapılar

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/karma-yapilar

Nesne alanlarını depoya tanıtmanın bedeli ve getirisi: aynı kitap kaydının bütünsel değer, yoğun karma ve alan başına giriş biçimlerinde kurulup günlük ödünç yükü altında tutulan bayt, yazılan bayt, okunan bayt, gidiş ve adım cinsinden ölçülmesi, alan başına üstverinin kayıt maliyetini iki katına çıkarması ve alan sayısı büyüdükçe yoğun gösterimin bellek kazancını tarama adımıyla ödemesi ile eşik politikasının katalog genelindeki sonucu.

Buraya kadar depoya konan her şey tek parçaydı: oturum kaydı bir bütün, sayaç bir sayı, liste
girişi bir dizgi. Kütüphanenin asıl kaydı böyle değildir. Bir kitabın adı, yazarı, raf kodu,
durumu, ödünç sayısı, eklenme tarihi ve numarası vardır. Bu alanların değişme sıklıkları da
birbirinin aynı değil: durum gün içinde defalarca değişir, ad hiç değişmez.

Bütünsel değerde depo alan diye bir kavram tanımaz; tek bir alanı güncellemek bütün kaydı okuyup
geri yazmak demektir — ve önceki derste ölçülen kayıp güncelleme sorunu tam buradan doğar. Karma
yapı bunun alternatifidir: **depo kaydın alanlarını bilir**, adıyla okur, adıyla yazar. Bu ders
o bilginin fiyatını sorar.

#### Aynı Kayıt, Üç Biçim

Üç biçim karşılaştırılır. **Bütünsel değer** kaydı tek bir seri hale getirilmiş dizgide tutar.
**Yoğun karma** alanları ardışık olarak tek bir blokta tutar ama depo alan sınırlarını bilir;
tek alan isteği bloğun taranmasıyla karşılanır. **Karma yapı** her alanı ayrı bir giriş olarak
tutar ve alan adıyla tek adımda erişir.

**BY1:** katalog 200.000 kitap taşır, her kayıt yedi alandır ve alan değerleri belirlenimli
işlevlerden gelir. **BY2:** yapı başına üstveri 56, alan başına üstveri 40 bayttır (karma kovası
göstergesi, alan adı ve değeri için göstergeler, uzunluk alanları). **BY3:** `okunan` ve `yazılan`
deponun o işlem için **dokunduğu** bayttır, `gidiş` istemcinin çağrı sayısıdır, `adım` çözülen ya
da karşılaştırılan alan sayısıdır. **BY4:** gün içinde 119.993 ödünç–iade olayı iki alanı
günceller (durum ve ödünç sayacı), 400.000 durum sorgusu tek alan okur, 60.000 kitap sayfası
bütün kaydı okur.

```js
// bellek/karma.mjs — ayni kitap kaydi uc bicimde. okunan/yazilan = deponun dokundugu bayt,
// gidis = istemcinin cagri sayisi, adim = alan arama karsilastirmasi. Is yuku belirlenimlidir.
const USTVERI = 56, ALAN_USTVERI = 40, KITAP = 200_000;   // BY2
const bl = (x) => Buffer.byteLength(String(x));
const anahtar = (i) => `kitap:${100_000 + i}`;
const kayit = (i) => ({
  ad: `Kitap ${100_000 + i} uzerine notlar`, yazar: `Yazar ${i % 997}`,
  raf: `K-${1 + (i % 20)}-${String(i % 400).padStart(3, "0")}`,
  durum: i % 3 === 0 ? "oduncte" : "raftaki", odunc: String(1 + (i * 7919) % 4096),
  eklenme: `20${10 + (i % 15)}-${String(1 + (i % 12)).padStart(2, "0")}-14`,
  isbn: String(9_780_000_000_000 + i),
});

class ButunselDepo {                            // deger tek parcadir: depo alan tanimaz
  #t = new Map(); okunan = 0; yazilan = 0; gidis = 0; adim = 0;
  koy(a, o) { this.#t.set(a, JSON.stringify(o)); }
  tamOku(a) { this.gidis += 1; const s = this.#t.get(a); this.okunan += bl(s);
    const o = JSON.parse(s); this.adim += Object.keys(o).length; return o; }   // her alan cozulur
  alanOku(a, alan) { return this.tamOku(a)[alan]; }        // butun deger okunur, biri kullanilir
  alanYaz(a, alan, v) { const o = this.tamOku(a); o[alan] = v;                 // oku-degistir-yaz
    const s = JSON.stringify(o); this.gidis += 1; this.yazilan += bl(a) + bl(s); this.#t.set(a, s); }
  bayt() { let b = 0; for (const [a, s] of this.#t) b += bl(a) + bl(s) + USTVERI; return b; }
}

class YogunKarmaDepo {                          // alanlar tek blobda ardisik; depo alan tanir
  #t = new Map(); okunan = 0; yazilan = 0; gidis = 0; adim = 0;
  koy(a, o) { this.#t.set(a, Object.entries(o).map(([k, v]) => `${k}=${v}`).join(";")); }
  #ara(s, alan) { for (const p of s.split(";")) { this.adim += 1; if (p.startsWith(`${alan}=`)) return p; } return null; }
  tamOku(a) { this.gidis += 1; const s = this.#t.get(a); this.okunan += bl(s);
    return Object.fromEntries(s.split(";").map((p) => p.split("="))); }
  alanOku(a, alan) { this.gidis += 1; const s = this.#t.get(a); this.okunan += bl(s);
    const p = this.#ara(s, alan); return p === null ? undefined : p.slice(alan.length + 1); }
  alanYaz(a, alan, v) { this.gidis += 1; const s = this.#t.get(a); this.okunan += bl(s);
    const y = s.split(";").map((p) => { this.adim += 1; return p.startsWith(`${alan}=`) ? `${alan}=${v}` : p; }).join(";");
    this.yazilan += bl(a) + bl(y); this.#t.set(a, y); }                        // blob yeniden yazilir
  bayt() { let b = 0; for (const [a, s] of this.#t) b += bl(a) + bl(s) + USTVERI; return b; }
}

class KarmaDepo {                               // her alan ayri giristir: adiyla erisilir
  #t = new Map(); okunan = 0; yazilan = 0; gidis = 0; adim = 0;
  koy(a, o) { this.#t.set(a, new Map(Object.entries(o).map(([k, v]) => [k, String(v)]))); }
  tamOku(a) { this.gidis += 1; const m = this.#t.get(a);
    for (const [k, v] of m) { this.adim += 1; this.okunan += bl(k) + bl(v); } return Object.fromEntries(m); }
  alanOku(a, alan) { this.gidis += 1; this.adim += 1;
    const v = this.#t.get(a).get(alan); this.okunan += bl(v); return v; }
  alanYaz(a, alan, v) { this.gidis += 1; this.adim += 1; this.yazilan += bl(a) + bl(alan) + bl(v);
    this.#t.get(a).set(alan, String(v)); }
  bayt() { let b = 0;
    for (const [a, m] of this.#t) { b += bl(a) + USTVERI;
      for (const [k, v] of m) b += bl(k) + bl(v) + ALAN_USTVERI; }
    return b; }
}

const bir = kayit(427);
console.log(`ornek ${anahtar(427)}: ${Object.keys(bir).length} alan, JSON ${bl(JSON.stringify(bir))} bayt, ` +
  `alan adlari ${Object.keys(bir).reduce((t, k) => t + bl(k), 0)} bayt, ` +
  `alan degerleri ${Object.values(bir).reduce((t, v) => t + bl(v), 0)} bayt`);

const depo = [["butunsel deger", new ButunselDepo()], ["yogun karma", new YogunKarmaDepo()],
  ["karma yapi", new KarmaDepo()]];
for (const [, d] of depo) for (let i = 1; i <= KITAP; i += 1) d.koy(anahtar(i), kayit(i));

// gunluk yuk: 119.993 odunc/iade iki alani gunceller, 400.000 durum sorgusu, 60.000 tam okuma
const ODUNC = 119_993, DURUM = 400_000, SAYFA = 60_000;
for (const [, d] of depo) {
  for (let j = 0; j < ODUNC; j += 1) { const i = 1 + (j * 4241) % KITAP;
    d.alanYaz(anahtar(i), "durum", j % 2 === 0 ? "oduncte" : "raftaki");
    d.alanYaz(anahtar(i), "odunc", String(1 + (i * 7919) % 4096)); }
  for (let j = 0; j < DURUM; j += 1) d.alanOku(anahtar(1 + (j * 7919) % KITAP), "durum");
  for (let j = 0; j < SAYFA; j += 1) d.tamOku(anahtar(1 + (j * 4241) % KITAP));
}

console.log(`\n${KITAP} kitap; gunluk yuk ${2 * ODUNC} alan yazma, ${DURUM} alan okuma, ${SAYFA} tam okuma`);
console.log(`${"bicim".padEnd(16)}${"tutulan bayt".padStart(14)}${"kayit basina".padStart(13)}` +
  `${"yazilan".padStart(10)}${"okunan".padStart(11)}${"gidis".padStart(9)}${"adim".padStart(9)}`);
for (const [ad, d] of depo)
  console.log(ad.padEnd(16) + String(d.bayt()).padStart(14) + (d.bayt() / KITAP).toFixed(1).padStart(13) +
    String(d.yazilan).padStart(10) + String(d.okunan).padStart(11) + String(d.gidis).padStart(9) +
    String(d.adim).padStart(9));
```

```
ornek kitap:100427: 7 alan, JSON 151 bayt, alan adlari 31 bayt, alan degerleri 77 bayt

200000 kitap; gunluk yuk 239986 alan yazma, 400000 alan okuma, 60000 tam okuma
bicim             tutulan bayt kayit basina   yazilan     okunan    gidis     adim
butunsel deger        43833793        219.2  39158258  105816147   939972  4899902
yogun karma           37833793        189.2  31958678   84816567   699986  3279902
karma yapi            91233793        456.2   5367227    9290135   699986  1059986
```

Karma yapının fiyatı ilk sütunda duruyor: aynı 200.000 kayıt bütünsel değerde 43,8 MB tutarken
karma yapıda **91,2 MB** tutuyor, kayıt başına 219,2 bayta karşılık 456,2 bayt. Fark tek bir
kalemdir: yedi alanın her biri kendi girişidir ve her giriş 40 baytlık üstveri ile alan adının
kopyasını taşır. Kayıt başına 237 baytlık artışın 280 baytı alan üstverisidir — yani **kaydın
kendisinden fazla yer, alanların ayrı ayrı tanınmasına gidiyor.**

Karşılığında alınan şey öteki dört sütundadır. Tek alan güncellemesi bütünsel değerde bütün kaydı
yazdırır; gün boyunca 39,2 MB yazılır. Karma yapıda yalnız anahtar, alan adı ve yeni değer
yazılır: **5,4 MB**, yedi kat az. Okuma tarafındaki fark daha büyüktür. 400.000 durum sorgusunun
her biri bütünsel değerde 151 baytlık kaydın tamamını okutur; toplam 105,8 MB'a karşı karma yapıda
9,3 MB — on bir kat. Gidiş sayısı da düşer: bütünsel değerde alan güncellemesi oku-değiştir-yaz
olduğu için iki çağrı ister, 939.972'ye karşı 699.986. Bu 239.986 fazladan gidiş yalnız bir
trafik kalemi değildir; önceki dersin kaybolan güncellemesi tam o aralıkta doğar. Karma yapıda
alan güncellemesi tek çağrıdır ve bölünmez.

Ortadaki satır dersin sürprizini taşıyor. Yoğun karma alanları tek blokta tutmasına rağmen **hem
bütünsel değerden az yer kaplıyor** (189,2 bayt, çünkü ayraç ve tırnak yükü yok) hem de alan
kavramını koruyor: gidiş 699.986'ya iniyor, güncelleme bölünmez oluyor. Yedi alanlık bir kayıtta
bütünsel değerin yoğun karmaya karşı kazandığı **tek bir sütun yok.** Yoğun karmanın ödediği bedel
adım sütunudur: tek alan bulmak için blok taranır, 3.279.902 adım. Karma yapı aynı işi 1.059.986
adımda bitirir.

#### Eşik Nerede

Yedi alanda yoğun karma iyi bir seçimdir; tarama dört adımdır. Alan sayısı büyüdükçe bu değişir.
**BY5:** karşılaştırma için alan adı 8, alan değeri 7 bayttır ve kayıt türleri katalogda dört
farklı boydadır.

```js
// bellek/esik.mjs — alan sayisi buyudukce yogun gosterim ile karma gosterimin ayrismasi
// ve esik politikasinin katalog genelindeki bedeli. Butun sayilar yapisal.
const USTVERI = 56, ALAN_USTVERI = 40;
const alanAdi = (j) => `alan_${String(j).padStart(3, "0")}`;
const alanDeger = (j) => String(1_000_000 + j * 7919);
const bl = (x) => Buffer.byteLength(String(x));
const ANAHTAR = bl("kitap:100427");

const yogunBayt = (f) => ANAHTAR + USTVERI +
  bl(Array.from({ length: f }, (_, j) => `${alanAdi(j)}=${alanDeger(j)}`).join(";"));
const karmaBayt = (f) => ANAHTAR + USTVERI +
  Array.from({ length: f }, (_, j) => bl(alanAdi(j)) + bl(alanDeger(j)) + ALAN_USTVERI).reduce((t, x) => t + x, 0);

console.log(`alan adi ${bl(alanAdi(0))} bayt, alan degeri ${bl(alanDeger(0))} bayt, ` +
  `alan ustverisi ${ALAN_USTVERI} bayt, anahtar ${ANAHTAR} bayt`);
console.log(`\n${"alan".padStart(6)}${"yogun bayt".padStart(12)}${"karma bayt".padStart(12)}${"oran".padStart(7)}` +
  `${"yogun erisim adimi".padStart(20)}${"yogun alan yazma".padStart(18)}${"karma alan yazma".padStart(18)}`);
for (const f of [4, 7, 16, 64, 256]) {
  const y = yogunBayt(f), k = karmaBayt(f);
  console.log(String(f).padStart(6) + String(y).padStart(12) + String(k).padStart(12) +
    (k / y).toFixed(2).padStart(7) + `${((f + 1) / 2).toFixed(1)} / ${f}`.padStart(20) +
    String(y - USTVERI).padStart(18) +
    String(ANAHTAR + bl(alanAdi(0)) + bl(alanDeger(0))).padStart(18));
}

// katalog: dort kayit turu, uc politika
const tur = [["kitap kaydi", 200_000, 7], ["uye profili", 20_000, 12],
  ["sube gunluk sayac", 5, 24], ["populer kitap etiket sayaci", 2_000, 256]];
console.log(`\n${"kayit turu".padEnd(29)}${"adet".padStart(8)}${"alan".padStart(6)}` +
  `${"hepsi yogun".padStart(13)}${"hepsi karma".padStart(13)}${"esik 64".padStart(13)}`);
let ty = 0, tk = 0, te = 0;
for (const [ad, n, f] of tur) {
  const y = n * yogunBayt(f), k = n * karmaBayt(f), e = f <= 64 ? y : k;
  ty += y; tk += k; te += e;
  console.log(ad.padEnd(29) + String(n).padStart(8) + String(f).padStart(6) +
    String(y).padStart(13) + String(k).padStart(13) + String(e).padStart(13));
}
console.log("toplam".padEnd(29) + "".padStart(8) + "".padStart(6) +
  String(ty).padStart(13) + String(tk).padStart(13) + String(te).padStart(13));
console.log(`esik 64: hepsi karmanin %${(100 * te / tk).toFixed(1)}'i, hepsi yogunun ` +
  `%${(100 * te / ty).toFixed(1)}'i; en kotu alan erisimi ${64} adim`);
```

```
alan adi 8 bayt, alan degeri 7 bayt, alan ustverisi 40 bayt, anahtar 12 bayt

  alan  yogun bayt  karma bayt   oran  yogun erisim adimi  yogun alan yazma  karma alan yazma
     4         135         288   2.13             2.5 / 4                79                27
     7         186         453   2.44             4.0 / 7               130                27
    16         339         948   2.80            8.5 / 16               283                27
    64        1155        3588   3.11           32.5 / 64              1099                27
   256        4419       14148   3.20         128.5 / 256              4363                27

kayit turu                       adet  alan  hepsi yogun  hepsi karma      esik 64
kitap kaydi                    200000     7     37200000     90600000     37200000
uye profili                     20000    12      5420000     14560000      5420000
sube gunluk sayac                   5    24         2375         6940         2375
populer kitap etiket sayaci      2000   256      8838000     28296000     28296000
toplam                                          51460375    133462940     70918375
esik 64: hepsi karmanin %53.1'i, hepsi yogunun %137.8'i; en kotu alan erisimi 64 adim
```

Üst tablo iki eğriyi yan yana koyuyor. Bellek oranı alan sayısıyla birlikte 2,13'ten 3,20'ye
çıkıyor — yoğun gösterimin kazancı büyük kayıtlarda daha da artıyor. Ama iki sütun ters yönde
büyüyor. Yoğun gösterimde bir alan bulmak ortalama `(f+1)/2` adımdır ve 256 alanlı kayıtta 128,5
adıma, en kötü durumda 256 adıma çıkar. Daha da sert olan tek alan yazmadır: yoğun gösterimde
bütün blok yeniden yazılır, 256 alanda **4.363 bayt**. Karma yapıda aynı yazma alan sayısından
bağımsız olarak 27 bayttır. Yani liste dersindeki yazma çarpanı burada geri geliyor ve alan
sayısıyla doğru orantılı büyüyor.

Alt tablo kararı katalog ölçeğinde veriyor. Her şeyi yoğun tutmak 51,5 MB, her şeyi karma tutmak
133,5 MB ister. Alan sayısı 64'ün altındaysa yoğun, üstündeyse karma tutan **eşik politikası**
70,9 MB'da kalır: her şeyi karma tutmanın %53,1'i. Aradaki 62,5 MB'lık tasarrufun bedeli 200.000
kitap kaydında en fazla 7 adımlık taramadır. Aynı politika, 2.000 etiket sayacını karma yapıda
tutarak 256 adımlık taramayı ve 4.363 baytlık yazmayı önler. Eşik politikası, her şeyi yoğun
tutmaya göre %37,8 fazla bellek harcar ve karşılığında **en kötü alan erişimini 256 adımdan 64
adıma bağlar.** Karar bir gösterimi öteki üzerine seçmek değil, sınırın nereden geçtiğini
seçmektir.

#### Özet

- Bütünsel değerde depo alan tanımaz: tek alan güncellemesi kaydın tamamını okutup yazdırır ve
  iki gidiş ister; kaybolan güncelleme tam bu aralıkta doğar.
- 200.000 kayıt bütünsel değerde 43,8 MB, karma yapıda 91,2 MB tutar. Kayıt başına 237 baytlık
  artışın büyük bölümü alan başına 40 baytlık üstveridir.
- Karma yapının satın aldığı şey günlük yazma trafiğinde 39,2 MB'dan 5,4 MB'a, okuma trafiğinde
  105,8 MB'dan 9,3 MB'a, gidişte 939.972'den 699.986'ya iniştir.
- Yoğun karma yedi alanlık kayıtta bütünsel değere göre hiçbir sütunda kaybetmez: 189,2 bayt,
  aynı gidiş sayısı ve bölünmez alan güncellemesi. Ödediği tek bedel tarama adımıdır
  (3.279.902'ye karşı 1.059.986).
- Alan sayısı büyüdükçe yoğun gösterimin bellek kazancı artar (oran 2,13'ten 3,20'ye) ama erişim
  ortalama `(f+1)/2` adıma, tek alan yazması 256 alanda 4.363 bayta çıkar; karma yapıda aynı yazma
  27 bayttır.
- Eşik politikası (64 alana kadar yoğun) katalogda 70,9 MB tutar: her şeyi karma tutmanın %53,1'i,
  her şeyi yoğun tutmanın %137,8'i. Satın alınan şey en kötü erişimin 64 adıma bağlanmasıdır.

#### Sonraki Adım

Karma yapı bir kaydın alanlarını ayırdı ama hepsi hâlâ tek bir kayda aitti. Kütüphanenin bazı
soruları tek kayda ait değildir: "bu kitap şu an ödünçte olanlar arasında mı", "bu üye ceza
listesinde var mı", "en çok ödünç alınan yirmi kitap hangileri". Birincisi bir üyelik sorusudur
ve yanıtı evet ya da hayırdır; sonuncusu bir sıra sorusudur ve yanıtı sıralı bir listedir. Sonraki
ders bu iki yapıyı ele alıyor ve üçünü ölçüyor: üyelik denetiminin adımı, sıralı kümede sıra
sorgusunun adımı ve ikisinin giriş başına bayt maliyeti.

### Kümeler ve Sıralı Kümeler

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/kumeler-ve-sirali-kumeler

Üyelik ve sıra sorularının bellekteki fiyatı: aynı ödünçte olan kitaplar kümesinin liste, sıralı dizi ve karma tabanlı küme olarak kurulup sorgu adımı, ekleme adımı ve giriş başına bayt cinsinden ölçülmesi, popüler kitap sıralamasının üye–puan karması üzerine üç ayrı sıra dizini ile kurulması, atlamalı listenin gösterge maliyetinin sıra sorgusunu kaç adıma indirdiği ve sıralı dizinin güncelleme yükü altında hiçbir şey satın almadığı.

Karma yapı bir kaydın alanlarını ayırdı, ama bütün alanlar hâlâ tek bir kayda aitti. Kütüphanenin
bazı soruları tek kayda ait değildir. "Bu kitap şu an ödünçte olanlar arasında mı" bir **üyelik**
sorusudur ve yanıtı evet ya da hayırdır. "Bu kitap en çok ödünç alınanlar arasında kaçıncı" bir
**sıra** sorusudur ve yanıtı bir sayıdır ama sıralı bir bütünden okunur. İki soru da aynı şeyi
gerektirir: hangi üyelerin bulunduğunun bellekte tutulmasını.

Bu yapıların karmaşıklık çözümlemesi daha önce yapıldı; burada ölçülen şey **tutulan bayt** ile
**depo semantiğidir**.

#### Üyelik: Evet mi Hayır mı

**BY1:** gün içinde 38.000 kitap ödünçtedir, üye kimliği 6 bayttır; 10.000 üyelik sorgusunun yarısı
kümede olan, yarısı olmayan bir kitabı sorar. **BY2:** dizi yuvası 8, yapı üstverisi 56, küme
girişi başına üstveri 48 bayttır (kova göstergesi, giriş yapısı, karma değeri, hizalama).

```js
// bellek/uyelik.mjs — "bu kitap su an oduncte mi" sorusu uc yapida. Adimlar
// gerceklestirimin icinde sayilir; is yuku belirlenimlidir.
const USTVERI = 56, YUVA = 8, KUME_USTVERI = 48;          // BY2
const bl = (x) => Buffer.byteLength(String(x));
const ODUNCTE = 38_000, SORGU = 10_000, KITAP = 200_000;
const uye = (i) => String(100_000 + (i * 4241) % KITAP);
const uyeler = [...new Set(Array.from({ length: ODUNCTE }, (_, i) => uye(i)))];

class ListeUyelik {                             // sira korunur, uyelik taramadir
  #a = []; adim = 0; eklemeAdim = 0;
  ekle(v) { this.eklemeAdim += 1; this.#a.push(v); }
  var_mi(v) { for (const x of this.#a) { this.adim += 1; if (x === v) return true; } return false; }
  bayt() { return USTVERI + this.#a.reduce((t, v) => t + bl(v) + YUVA, 0); }
  get n() { return this.#a.length; }
}
class SiraliDiziUyelik {                        // ikili arama; ekleme kaydirma ister
  #a = []; adim = 0; eklemeAdim = 0;
  #yer(v) { let d = 0, y = this.#a.length;
    while (d < y) { this.adim += 1; const o = (d + y) >> 1; if (this.#a[o] < v) d = o + 1; else y = o; }
    return d; }
  ekle(v) { const i = this.#yer(v); this.eklemeAdim += this.#a.length - i + 1; this.#a.splice(i, 0, v); }
  var_mi(v) { const i = this.#yer(v); return this.#a[i] === v; }
  bayt() { return USTVERI + this.#a.reduce((t, x) => t + bl(x) + YUVA, 0); }
  get n() { return this.#a.length; }
}
class KumeUyelik {                              // karma: tek adim, giris basina ek ustveri
  #s = new Set(); adim = 0; eklemeAdim = 0;
  ekle(v) { this.eklemeAdim += 1; this.#s.add(v); }
  var_mi(v) { this.adim += 1; return this.#s.has(v); }
  bayt() { let b = USTVERI; for (const v of this.#s) b += bl(v) + KUME_USTVERI; return b; }
  get n() { return this.#s.size; }
}

const yapi = [["liste", new ListeUyelik()], ["sirali dizi", new SiraliDiziUyelik()],
  ["kume", new KumeUyelik()]];
for (const [, y] of yapi) for (const v of uyeler) y.ekle(v);

// sorgularin yarisi kumede olan, yarisi olmayan bir kitabi sorar
const sorgu = Array.from({ length: SORGU }, (_, j) =>
  j % 2 === 0 ? uyeler[(j * 7919) % uyeler.length] : String(100_000 + KITAP + j));
let bulunan = 0;
for (const [, y] of yapi) { let b = 0; for (const s of sorgu) if (y.var_mi(s)) b += 1; bulunan = b; }

console.log(`${uyeler.length} kitap oduncte (uye ${bl(uyeler[0])} bayt), ${SORGU} uyelik sorgusu, ` +
  `${bulunan} tanesi bulundu`);
console.log(`${"yapi".padEnd(13)}${"tutulan bayt".padStart(14)}${"giris basina".padStart(13)}` +
  `${"sorgu adimi".padStart(13)}${"sorgu basina".padStart(14)}${"ekleme adimi".padStart(14)}`);
for (const [ad, y] of yapi)
  console.log(ad.padEnd(13) + String(y.bayt()).padStart(14) + (y.bayt() / y.n).toFixed(1).padStart(13) +
    String(y.adim).padStart(13) + (y.adim / SORGU).toFixed(1).padStart(14) +
    String(y.eklemeAdim).padStart(14));
```

```
38000 kitap oduncte (uye 6 bayt), 10000 uyelik sorgusu, 5000 tanesi bulundu
yapi           tutulan bayt giris basina  sorgu adimi  sorgu basina  ekleme adimi
liste                532056         14.0    284642000       28464.2         38000
sirali dizi          532056         14.0       676550          67.7     360742025
kume                2052056         54.0        10000           1.0         38000
```

Liste ve sıralı dizi **aynı 532.056 baytı** tutuyor, giriş başına 14,0. Küme aynı 38.000 üye için
2.052.056 bayt istiyor — giriş başına 54,0, yani 3,86 kat. Fark tek kalemdir: kova göstergesi ve
giriş yapısı için üye başına 40 bayt. Altı baytlık bir kitap kimliğini kümede tutmak, kimliğin
kendisinin dokuz katı yer harcıyor.

Karşılığı sorgu sütununda: liste her soruyu ortalama 28.464 adımda, küme **1 adımda** yanıtlıyor.
Sıralı dizi ilginç bir orta yol gibi görünüyor — listenin belleğiyle 67,7 adım — ama ekleme sütunu
onu bitiriyor. Sıralı dizide her ekleme kalan girişleri kaydırır: 38.000 ekleme 360.742.025 adım,
ekleme başına 9.493. Ödünçte olan kitaplar kümesi durağan değildir; gün içinde her ödünç bir
ekleme, her iade bir çıkarma demektir. Sıralı dizi bu iş yükünde **sorgudan kazandığını eklemede
yüz kat fazlasıyla geri veriyor.**

Kümenin fazladan tuttuğu 1.520.000 bayt hem sorguyu hem eklemeyi tek adıma indiriyor. Kaybettiği
şey sütunlarda görünmez: **küme sırayı tutmaz.** Liste 38.000 kitabı eklenme sırasıyla taşıyordu;
küme aynı kitapları taşır ama hangisinin önce ödünç verildiği bilgisi yoktur. Üyelik yanıtı
alınırken sıra bırakılmıştır.

#### Sıra: Kaçıncı

Sıralama tablosu iki soruyu birden sorar: bir kitabın puanı kaç ve o puan kaçıncı sıraya karşılık
geliyor. Birincisi için üye–puan karması yeterlidir ve üç yolda da ortaktır; ikincisi bir **sıra
dizini** ister. **BY3:** 50.000 kitabın popülerlik puanı vardır, 10.000 ödünç puan artırır, 2.000
sıra sorgusu ve 200 "en üst 20" sorgusu gelir. **BY4:** atlamalı listenin düğüm düzeyleri tohumu
görünür bir üreticiden gelir; düğüm başlığı 24, düzey başına gösterge çifti 16 bayttır. Üç yolun
verdiği sıralar kaba kuvvetle karşılaştırılır.

```js
// bellek/siralama.mjs — populer kitap siralamasi: uye->puan karmasi uzerine uc sira dizini.
// Atlamali listenin duzeyleri tohumlu ureticten gelir; siralar kaba kuvvetle dogrulanir.
const USTVERI = 56, YUVA = 8, KUME_USTVERI = 48, DUGUM = 24, GOSTERGE = 16;   // BY2
const KITAP = 50_000, GUNCELLEME = 10_000, SORGU = 2_000, UST = 20, TOHUM = 20240115;
const bl = (x) => Buffer.byteLength(String(x));
let c = TOHUM;
const rast = () => (c = (c * 1103515245 + 12345) % 2147483648) / 2147483648;

const uye = (i) => String(100_000 + i);
const puan0 = (i) => 1 + (i * 7919) % 4096;
const once = (p1, u1, p2, u2) => p1 > p2 || (p1 === p2 && u1 < u2);   // puan azalan, uye artan

class AtlamaliListe {
  #bas; #duzey = 1; #n = 0; #max; adim = 0; gosterge = 0;
  constructor(max = 16) { this.#max = max;
    this.#bas = { uye: "", puan: 0, ileri: new Array(max).fill(null), acik: new Array(max).fill(0) }; }
  #duzeySec() { let d = 1; while (rast() < 0.5 && d < this.#max) d += 1; return d; }
  #yol(puan, u) {
    const g = new Array(this.#max), s = new Array(this.#max).fill(0);
    let x = this.#bas;
    for (let i = this.#duzey - 1; i >= 0; i -= 1) {
      s[i] = i === this.#duzey - 1 ? 0 : s[i + 1];
      while (x.ileri[i] && once(x.ileri[i].puan, x.ileri[i].uye, puan, u)) {
        this.adim += 1; s[i] += x.acik[i]; x = x.ileri[i]; }
      g[i] = x;
    }
    return [g, s, x.ileri[0]];
  }
  ekle(u, puan) {
    const [g, s] = this.#yol(puan, u);
    const d = this.#duzeySec();
    if (d > this.#duzey) { for (let i = this.#duzey; i < d; i += 1) {
      s[i] = 0; g[i] = this.#bas; this.#bas.acik[i] = this.#n; } this.#duzey = d; }
    const y = { uye: u, puan, ileri: new Array(d), acik: new Array(d) };
    this.gosterge += d;
    for (let i = 0; i < d; i += 1) {
      y.ileri[i] = g[i].ileri[i]; g[i].ileri[i] = y;
      y.acik[i] = g[i].acik[i] - (s[0] - s[i]); g[i].acik[i] = (s[0] - s[i]) + 1;
    }
    for (let i = d; i < this.#duzey; i += 1) g[i].acik[i] += 1;
    this.#n += 1;
  }
  cikar(u, puan) {
    const [g, , x] = this.#yol(puan, u);
    if (!x || x.uye !== u || x.puan !== puan) return false;
    for (let i = 0; i < this.#duzey; i += 1) {
      if (g[i].ileri[i] === x) { g[i].acik[i] += x.acik[i] - 1; g[i].ileri[i] = x.ileri[i]; }
      else g[i].acik[i] -= 1;
    }
    this.gosterge -= x.ileri.length;
    while (this.#duzey > 1 && this.#bas.ileri[this.#duzey - 1] === null) this.#duzey -= 1;
    this.#n -= 1; return true;
  }
  sira(u, puan) { const [, s, x] = this.#yol(puan, u); return x && x.uye === u ? s[0] + 1 : -1; }
  ust(k) { const r = []; let x = this.#bas.ileri[0];
    while (x && r.length < k) { this.adim += 1; r.push(x.uye); x = x.ileri[0]; } return r; }
  bayt() { let b = USTVERI; let x = this.#bas.ileri[0];
    while (x) { b += bl(x.uye) + DUGUM + x.ileri.length * GOSTERGE; x = x.ileri[0]; } return b; }
  get duzey() { return this.#duzey; }
}

const karma = new Map();                        // uye -> puan; uc yolda da ortak
for (let i = 1; i <= KITAP; i += 1) karma.set(uye(i), puan0(i));
const karmaBayt = [...karma].reduce((t, [u]) => t + bl(u) + YUVA + KUME_USTVERI, USTVERI);

const dizi = [...karma].map(([u, p]) => [p, u]).sort((a, b) => (once(a[0], a[1], b[0], b[1]) ? -1 : 1));
const atlama = new AtlamaliListe();
for (const [u, p] of karma) atlama.ekle(u, p);
let diziAdim = 0, dizinsizAdim = 0;
const yer = (p, u) => { let d = 0, y = dizi.length;
  while (d < y) { diziAdim += 1; const o = (d + y) >> 1;
    if (once(dizi[o][0], dizi[o][1], p, u)) d = o + 1; else y = o; } return d; };

// gunluk yuk: puan artirma, sira sorgusu, en ust K
const olay = Array.from({ length: GUNCELLEME }, (_, j) => uye(1 + (j * 4241) % KITAP));
for (const u of olay) {
  const eski = karma.get(u), yeni = eski + 1;
  const i = yer(eski, u); diziAdim += dizi.length - i; dizi.splice(i, 1);        // kaydirma
  const k = yer(yeni, u); diziAdim += dizi.length - k; dizi.splice(k, 0, [yeni, u]);
  atlama.cikar(u, eski); atlama.ekle(u, yeni); karma.set(u, yeni);
}
const sorgu = Array.from({ length: SORGU }, (_, j) => uye(1 + (j * 7919) % KITAP));
let esit = 0;
for (const u of sorgu) {
  const p = karma.get(u);
  let s1 = 1; for (const [v, q] of karma) { dizinsizAdim += 1; if (once(q, v, p, u)) s1 += 1; }
  const s2 = yer(p, u) + 1, s3 = atlama.sira(u, p);
  if (s1 === s2 && s2 === s3) esit += 1;
}
for (let j = 0; j < 200; j += 1) {
  [...karma].sort((a, b) => { dizinsizAdim += 1; return once(a[1], a[0], b[1], b[0]) ? -1 : 1; }).slice(0, UST);
  diziAdim += UST; dizi.slice(0, UST); atlama.ust(UST);
}

console.log(`${KITAP} kitap, ${GUNCELLEME} puan artirma, ${SORGU} sira sorgusu, 200 "en ust ${UST}"`);
console.log(`${esit}/${SORGU} sorguda uc yol ayni sirayi verdi; atlamali liste duzeyi ${atlama.duzey}, ` +
  `gosterge ${atlama.gosterge} (dugum basina ${(atlama.gosterge / KITAP).toFixed(2)}), tohum ${TOHUM}`);
const yol = [
  ["sira dizini yok", karmaBayt, dizinsizAdim],
  ["sirali dizi", karmaBayt + USTVERI + dizi.reduce((t, [, u]) => t + bl(u) + YUVA + YUVA, 0), diziAdim],
  ["atlamali liste", karmaBayt + atlama.bayt(), atlama.adim],
];
console.log(`\n${"sira dizini".padEnd(17)}${"tutulan bayt".padStart(14)}${"dizin payi".padStart(12)}` +
  `${"giris basina".padStart(13)}${"toplam adim".padStart(13)}`);
for (const [ad, b, adim] of yol)
  console.log(ad.padEnd(17) + String(b).padStart(14) + String(b - karmaBayt).padStart(12) +
    (b / KITAP).toFixed(1).padStart(13) + String(adim).padStart(13));
```

```
50000 kitap, 10000 puan artirma, 2000 sira sorgusu, 200 "en ust 20"
2000/2000 sorguda uc yol ayni sirayi verdi; atlamali liste duzeyi 13, gosterge 102228 (dugum basina 2.04), tohum 20240115

sira dizini        tutulan bayt  dizin payi giris basina  toplam adim
sira dizini yok         3100056           0         62.0    212473400
sirali dizi             4200112     1100056         84.0    500247667
atlamali liste          6235760     3135704        124.7      1336074
```

Üç yol da 2.000 sorgunun tamamında **aynı sırayı** veriyor; ayrım yalnız fiyattadır. Sıra dizini
tutulmazsa karma 3.100.056 bayttadır ve her sıra sorgusu bütün girişleri gezer, her "en üst 20"
sorgusu bütün tabloyu sıralar: 212.473.400 adım.

Sıralı dizi bu iş yükünde **hiçbir şey satın almıyor.** 1.100.056 bayt fazla tutuyor ve toplam
adımı 500.247.667'ye çıkarıyor — dizinsiz yoldan da kötü. Nedeni önceki bölümdekiyle aynıdır:
sıra sorgusu ikili aramayla ucuzken, her puan artırması girişi eski yerinden çıkarıp yenisine
sokmak için ortalama elli bin girişi kaydırıyor. Sıralı dizi durağan bir tabloda iyi,
sürekli değişen bir sıralama tablosunda yanlış seçimdir.

Atlamalı liste 3.135.704 bayt fazla tutuyor — giriş başına 124,7 bayt, üyelik kümesinin iki
katından fazla — ve toplam adımı **1.336.074'e** indiriyor. Fazladan tuttuğu her bayt 67,3 adım
önlüyor. Bu baytların yarıdan fazlası ne puan ne kimliktir: 102.228 gösterge çifti, düğüm başına
2,04 düzey, 1.635.648 bayt. Sıra dizininin maliyeti verinin kendisi değil, **veriye sıradan
erişmenin yoludur.** Karşılığında hem sıra sorgusu hem "en üst 20" sorgusu tablonun boyundan
bağımsız hâle gelir; puan güncellemesi de öyle.

#### Özet

- Üyelik sorusu üç yapıda da doğru yanıtlanır: liste 28.464,2, sıralı dizi 67,7, küme 1,0 adımda.
- Küme aynı 38.000 üyeyi 2.052.056 baytta tutar; liste ve sıralı dizi 532.056 baytta. Fark üye
  başına 40 baytlık kova ve giriş üstverisidir — altı baytlık kimliğin dokuz katı.
- Sıralı dizi üyelikte kandırıcıdır: sorgu ucuzdur ama 38.000 ekleme 360.742.025 adım harcar
  (ekleme başına 9.493). Sürekli değişen bir üyelik kümesinde en kötü seçimdir.
- Kümenin kaybettiği şey sıradır: hangi kitabın önce ödünç verildiği bilgisi kümede yoktur.
- Sıralama tablosunda sıra dizini tutmamak 3.100.056 bayt ve 212.473.400 adım demektir; sıralı
  dizi 1.100.056 bayt fazla tutup adımı 500.247.667'ye **çıkarır**, çünkü her puan artırması
  kaydırma ister.
- Atlamalı liste 3.135.704 bayt fazla tutar ve adımı 1.336.074'e indirir: bayt başına 67,3 adım.
  Bu baytların 1.635.648'i saf göstergedir — düğüm başına 2,04 düzey.

#### Sonraki Adım

Bu dersteki iki yapı da doğru yanıt verdi ve ikisi de aynı şeyi yaptı: **saydıkları şeyi tam
tuttular.** Küme 38.000 kitabın kimliğini tek tek taşıdı, sıralı küme 50.000 kitabın hem
kimliğini hem puanını hem de sıradaki yerini. Aynı şey önceki derslerde de geçerliydi — karma
yapı her alanı, liste her girişi, sayaç her anahtarı ayrı ayrı tuttu.

Oysa kütüphanenin sorularının bir bölümü üyelerin kendisini istemiyor. "Bugün kaç farklı üye
sisteme girdi", "bu kitabı daha önce ödünç alan biri mi", "hangi günlerde ödünç verildi" —
bunların yanıtı bir sayı ya da bir evet–hayırdır, üyelerin listesi değil. Yanıt için sayı
yeterken üyelerin tamamını bellekte tutmanın bedeli bu derslerin hiçbirinde sorulmadı. Sonraki
ders bu soruyu açıyor.

### Bit Eşlemler ve Olasılıksal Yapılar

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/bit-eslemler-ve-olasiliksal-yapilar

Sayının kendisi yeterken üyelerin listesini tutmanın bedeli: aynı tekil okur sorusunun tam küme, bit eşlem, olasılıksal üyelik yapısı ve kardinalite kestirimiyle çözülmesi, dört yapının tuttuğu baytın kendi tamponundan okunması, yanlış olumlu oranının ölçülmesi ve kesinlikten vazgeçmenin kaç bayt satın aldığının sayılması.

Buraya kadarki yapılar saydıkları şeyi **tam** tutuyor. Bir kümenin kaç üyesi olduğu bilinsin diye
üye numaralarının hepsi bellekte duruyor; bir karma yapının kaç alanı olduğu bilinsin diye alan
adlarının hepsi bellekte duruyor. Üye geri istendiğinde bu zorunludur. Ama her soru üyeyi geri
istemez: kütüphanenin gün sonu raporundaki "bugün kaç ayrı okur ödünç aldı" satırının yanıtı tek bir
sayıdır ve o sayıyı üretmek için yüz binlerce okur numarasının bellekte tutulması bir karar olarak
hiç sorgulanmadı.

Bu ders o kararı sorgular. İki soru ayrılır: **kardinalite kestirimi** ("kaç ayrı okur") ve
**üyelik** ("şu okur bugün ödünç aldı mı"). İkisi farklı yapılarla, farklı bayt bütçeleriyle ve
farklı kesinlik derecesiyle yanıtlanabilir.

#### İki Soru, Dört Yapı

Kütüphanenin ödünç kaydı gün boyunca bellek içi depoda birikir. Gün sonunda rapor tekil okur sayısını
ister; gün içinde ödünç ekranı "bu okur bugün zaten işlem yaptı mı" sorusunu sorar.

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BY10 | kayıtlı okur | 2.000.000 | üye numarası 1'den başlayan yoğun bir aralık |
| BY11 | günlük ödünç olayı | 900.000 | bütün şubelerin toplamı |
| BY12 | olayların dağılımı | %70'i okurların %15'inden | sık ödünç alan bir çekirdek var |
| BY13 | rapor için kabul edilen sapma | %2 | gün sonu raporu kesin sayı istemiyor |

BY10 bu dersin karar verdiren varsayımıdır: üye numarası **yoğun** bir aralık olduğu için okur
numarası doğrudan bir bit sırası olarak kullanılabilir. Aralık seyrek olsaydı aşağıdaki tablonun
ikinci satırı bambaşka bir sayı gösterirdi; ders bunu sonunda ölçer.

#### Kesin İki Yapı

İlk düzenek iki kesin yapıyı aynı veriyle doldurur. Tam küme açık adreslemeli bir karma tablodur ve
tuttuğu bayt tamponunun boyudur; bit eşlem okur numarasını doğrudan bit sırası olarak kullanır.

```js
// tekil-sayim.mjs — gunun tekil odunc alan okuru: tam kume ile bit eslem.
// Bayt sayilari yapinin kendi tamponundan okunur, tahmin yok; tohum gorunur.
const OKUR = 2_000_000, OLAY = 900_000, TOHUM = 20260731;
let d = TOHUM;                                        // 32 bit tam sayi uretec, tasma yok
const rast = () => { d = (d + 0x6D2B79F5) | 0; let t = Math.imul(d ^ (d >>> 15), 1 | d);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 2 ** 32; };
const okur = () => rast() < 0.7                       // %70 olay, okurlarin %15'inden
  ? 1 + Math.floor(rast() * OKUR * 0.15) : 1 + Math.floor(rast() * OKUR);

class TamKume {                                       // acik adresleme, bos yuva 0
  constructor() { this.k = 1024; this.t = new Int32Array(this.k); this.n = 0; }
  yer(t, k, x) { let i = (Math.imul(x, 2654435761) >>> 0) & (k - 1);
    while (t[i] !== 0 && t[i] !== x + 1) i = (i + 1) & (k - 1); return i; }
  ekle(x) { if ((this.n + 1) * 2 > this.k) this.buyut();
    const i = this.yer(this.t, this.k, x);
    if (this.t[i] === 0) { this.t[i] = x + 1; this.n += 1; } }
  icerir(x) { return this.t[this.yer(this.t, this.k, x)] !== 0; }
  buyut() { const y = new Int32Array(this.k * 2);
    for (const v of this.t) if (v !== 0) y[this.yer(y, this.k * 2, v - 1)] = v;
    this.t = y; this.k *= 2; }
  bayt() { return this.t.byteLength; }
}
class BitEslem {                                      // okur numarasi dogrudan bit sirasidir
  constructor(n) { this.b = new Uint8Array(Math.ceil(n / 8)); }
  ekle(x) { this.b[x >> 3] |= 1 << (x & 7); }
  icerir(x) { return (this.b[x >> 3] & (1 << (x & 7))) !== 0; }
  say() { let s = 0; for (let v of this.b) while (v) { s += v & 1; v >>= 1; } return s; }
  bayt() { return this.b.byteLength; }
}

const kume = new TamKume(), eslem = new BitEslem(OKUR + 1);
for (let i = 0; i < OLAY; i += 1) { const o = okur(); kume.ekle(o); eslem.ekle(o); }
const n = kume.n, tr = (x) => x.toLocaleString("tr-TR");
console.log(`model: ${tr(OKUR)} kayitli okur, ${tr(OLAY)} odunc olayi, tohum ${TOHUM}`);
console.log(`tekil okur: ${tr(n)} (bit eslem ayni sayiyi veriyor: ${tr(eslem.say())})\n`);
console.log("yapi".padEnd(14) + "tutulan bayt".padStart(14) + "tekil basina".padStart(14) +
  "uyelik".padStart(9) + "sayi".padStart(9));
for (const [ad, y] of [["tam kume", kume], ["bit eslem", eslem]])
  console.log(ad.padEnd(14) + tr(y.bayt()).padStart(14) + (y.bayt() / n).toFixed(2).padStart(14) +
    "kesin".padStart(9) + "kesin".padStart(9));

let yanlis = 0;                                       // iki yapi da ayni yaniti veriyor mu
for (let x = 1; x <= OKUR; x += 7) if (kume.icerir(x) !== eslem.icerir(x)) yanlis += 1;
console.log(`\n${tr(Math.ceil(OKUR / 7))} uyelik sorgusunda iki yapinin ayrildigi sorgu: ${yanlis}`);
const SEYREK = 1_000_000_000;                         // 9 haneli uye numarasi kullanilsaydi
console.log(`bit eslem numara araligina baglidir: 1..${tr(OKUR)} icin ${tr(eslem.bayt())} bayt, ` +
  `1..${tr(SEYREK)} icin ${tr(Math.ceil(SEYREK / 8))} bayt (tam kume degismez: ${tr(kume.bayt())})`);
```

```
model: 2.000.000 kayitli okur, 900.000 odunc olayi, tohum 20260731
tekil okur: 482.085 (bit eslem ayni sayiyi veriyor: 482.085)

yapi            tutulan bayt  tekil basina   uyelik     sayi
tam kume           4.194.304          8.70    kesin    kesin
bit eslem            250.001          0.52    kesin    kesin

285.715 uyelik sorgusunda iki yapinin ayrildigi sorgu: 0
bit eslem numara araligina baglidir: 1..2.000.000 icin 250.001 bayt, 1..1.000.000.000 icin 125.000.000 bayt (tam kume degismez: 4.194.304)
```

Bu sayılar **ölçüm** sınıfındadır; tekil okur sayısı tohuma bağlı, yapıların tuttuğu bayt bağlı değil
— o sayı doğrudan tamponun boyudur ve aynı giriş sayısında her koşumda aynıdır.

Bit eşlem tam kümeden **on altı kat** az yer tutuyor (250.001'e karşı 4.194.304 bayt) ve hiçbir şey
kaybetmiyor: tekil sayısı aynı, 285.715 üyelik sorgusunun hiçbirinde iki yapı ayrılmıyor. Bunun
nedeni bit eşlemin okur numarasını **veri olarak değil adres olarak** kullanmasıdır. Tam küme
numarayı saklamak zorundadır, çünkü bir yuvada hangi numaranın durduğu ancak yazılıysa bilinir; bit
eşlemde numaranın nerede duracağı zaten numaranın kendisidir, dolayısıyla saklanacak bir şey kalmaz
ve giriş başına maliyet 8,70 bayttan 0,52 bayta iner.

Kaybedilen şey son satırda: bit eşlemin maliyeti **kaç okur ödünç aldığına değil, numara aralığının
genişliğine** bağlıdır. Kütüphane dokuz haneli üye numarasına geçse aynı 482.085 okur için 250.001
bayt yerine 125.000.000 bayt gerekirdi; tam kümenin maliyeti değişmezdi. Bit eşlem, aralığın yoğun
olduğu yerde bedava, seyrek olduğu yerde ödenemez bir yapıdır.

#### Kesinlikten Vazgeçmek

İki kesin yapı da her okuru ayrı ayrı temsil ediyor. Bir sonraki adım bu temsili bırakmaktır: aynı
soruyu, hangi okurun içeride olduğunu **belirsiz bırakan** yapılarla yanıtlamak.

```js
// olasiliksal-sayim.mjs — ayni gunun ayni iki sorusu, bu kez kesinlikten vazgecerek.
// Uyelik yapisi ve kardinalite kestirimi; dogruluk olcusu icin ayrica tam liste tutulur.
const OKUR = 2_000_000, OLAY = 900_000, TOHUM = 20260731, SORGU = 300_000;
let d = TOHUM;
const rast = () => { d = (d + 0x6D2B79F5) | 0; let t = Math.imul(d ^ (d >>> 15), 1 | d);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 2 ** 32; };
const okur = () => rast() < 0.7
  ? 1 + Math.floor(rast() * OKUR * 0.15) : 1 + Math.floor(rast() * OKUR);
const karma = (x, tohum) => { let h = Math.imul(x ^ tohum, 0x85ebca6b) >>> 0;
  h = Math.imul(h ^ (h >>> 13), 0xc2b2ae35) >>> 0; return (h ^ (h >>> 16)) >>> 0; };

const gercek = new Uint8Array(OKUR + 1);              // yalniz dogruluk olcusu, olculen yapi degil
for (let i = 0; i < OLAY; i += 1) gercek[okur()] = 1;
const uyeler = [], disaridakiler = [];
for (let x = 1; x <= OKUR; x += 1) (gercek[x] ? uyeler : disaridakiler).push(x);
const sorgular = disaridakiler.slice(0, SORGU), n = uyeler.length;
const tr = (x) => x.toLocaleString("tr-TR");
console.log(`model: ${tr(n)} tekil okur, ${tr(SORGU)} olumsuz uyelik sorgusu, tohum ${TOHUM}\n`);

console.log("olasiliksal uyelik yapisi (k = 3 karma)");
console.log("bit".padEnd(10) + "tutulan bayt".padStart(14) + "tekil basina".padStart(14) +
  "yanlis olumlu".padStart(15) + "beklenen".padStart(10) + "kacirilan".padStart(11));
for (const p of [17, 18, 19, 20, 21, 22]) {
  const m = 1 << p, K = 3, b = new Uint8Array(m / 8);
  const yerler = (x) => Array.from({ length: K }, (_, i) => karma(x, 0x1000 + i * 0x9e3779b1) % m);
  for (const x of uyeler) for (const i of yerler(x)) b[i >> 3] |= 1 << (i & 7);
  const icerir = (x) => yerler(x).every((i) => (b[i >> 3] & (1 << (i & 7))) !== 0);
  let yo = 0, kacan = 0;
  for (const x of sorgular) if (icerir(x)) yo += 1;
  for (const x of uyeler) if (!icerir(x)) kacan += 1;
  const beklenen = (1 - Math.exp((-K * n) / m)) ** K;
  console.log(`2^${p}`.padEnd(10) + tr(b.byteLength).padStart(14) +
    (b.byteLength / n).toFixed(2).padStart(14) +
    `%${((yo / SORGU) * 100).toFixed(2)}`.padStart(15) +
    `%${(beklenen * 100).toFixed(2)}`.padStart(10) + `${kacan}`.padStart(11));
}

console.log("\nkardinalite kestirimi (yalniz sayi; uyelik sorusuna yanit vermez)");
console.log("yazmac".padEnd(10) + "tutulan bayt".padStart(14) + "tekil basina".padStart(14) +
  "kestirim".padStart(12) + "sapma".padStart(9) + "kuramsal".padStart(10));
for (const p of [8, 10, 12, 14]) {
  const m = 1 << p, R = new Uint8Array(m);
  for (const x of uyeler) { const h = karma(x, 0x9e37), i = h >>> (32 - p), w = (h << p) >>> 0;
    const r = w === 0 ? 33 - p : Math.clz32(w) + 1; if (r > R[i]) R[i] = r; }
  let z = 0, sifir = 0;
  for (const v of R) { z += 2 ** -v; if (v === 0) sifir += 1; }
  let e = (0.7213 / (1 + 1.079 / m)) * m * m / z;
  if (e <= 2.5 * m && sifir > 0) e = m * Math.log(m / sifir);   // dogrusal sayim duzeltmesi
  console.log(`2^${p}`.padEnd(10) + tr(R.byteLength).padStart(14) +
    (R.byteLength / n).toFixed(4).padStart(14) + tr(Math.round(e)).padStart(12) +
    `%${(((e - n) / n) * 100).toFixed(2)}`.padStart(9) +
    `%${((1.04 / Math.sqrt(m)) * 100).toFixed(2)}`.padStart(10));
}
```

```
model: 482.085 tekil okur, 300.000 olumsuz uyelik sorgusu, tohum 20260731

olasiliksal uyelik yapisi (k = 3 karma)
bit         tutulan bayt  tekil basina  yanlis olumlu  beklenen  kacirilan
2^17              16.384          0.03         %99.99   %100.00          0
2^18              32.768          0.07         %98.83    %98.80          0
2^19              65.536          0.14         %82.26    %82.16          0
2^20             131.072          0.27         %42.09    %41.89          0
2^21             262.144          0.54         %12.41    %12.37          0
2^22             524.288          1.09          %2.53     %2.48          0

kardinalite kestirimi (yalniz sayi; uyelik sorusuna yanit vermez)
yazmac      tutulan bayt  tekil basina    kestirim    sapma  kuramsal
2^8                  256        0.0005     476.215   %-1.22     %6.50
2^10               1.024        0.0021     481.111   %-0.20     %3.25
2^12               4.096        0.0085     474.849   %-1.50     %1.63
2^14              16.384        0.0340     478.877   %-0.67     %0.81
```

Üst tabloda **kaçırılan** sütunu her satırda sıfır: olasılıksal üyelik yapısı bir üyeyi asla
"yok" diye yanıtlamaz, çünkü eklenen her üyenin bitleri kurulmuştur ve hiç silinmez. Hata tek yönlüdür
ve adı **yanlış olumlu**: yapı, hiç eklenmemiş bir okur için de üç bitin üçünü birden kurulmuş
bulabilir. Ölçülen oran kuramsal oranla satır satır örtüşüyor (2^21'de %12,41'e karşı %12,37), yani
bu oran bir sürpriz değil, yapının boyuyla **seçilen** bir niceliktir.

Aynı sözcük çifti yazılım testi bağlamında da geçer; oradaki **yanlış alarm** kusursuz kodu kusurlu
diye işaretleyen bir testin giderilebilir hatasıdır, burada ölçülen **yanlış olumlu oranı** ise
yapının tanımına yazılıdır: bayt eklenerek düşürülebilir, sıfırlanamaz.

Alt tablo takasın en keskin biçimidir. 4.096 bayt tutan yazmaç dizisi 482.085 tekil okuru %1,50
sapmayla sayıyor: tam kümenin **bin yirmi dört katı**, bit eşlemin altmış bir katı az yer tutuyor.
Seçilecek boy son sütundan okunur, ölçülen sapmadan değil. 256 baytlık dizi bu koşumda %1,22
sapmayla çıktı ama kuramsal hata payı %6,50; BY13'ün %2'lik toleransını güvenle karşılayan ilk boy
kuramsal payı %1,63 olan 4.096 bayttır. Karşılığında verdiği şey mutlaktır: bu yapı
"şu okur ödünç aldı mı" sorusuna **hiçbir kesinlik derecesinde** yanıt veremez, çünkü okurun kendisi
hiçbir zaman temsil edilmemiştir; yalnız karmasının baştaki sıfırlarının kaç tane olduğu tutulmuştur.

#### Hangi Yapı Neyi Satın Alır

Dört yapı aynı gün için sırasıyla 4.194.304, 250.001, 524.288 ve 4.096 bayt tutuyor. Sıralama
şaşırtıcı bir yerde kırılıyor: **yoğun bir numara aralığında olasılıksal üyelik yapısı bit eşleme
yenilir.** %2,53 yanlış olumluyla çalışan yapı 524.288 bayt istiyor, kesin bit eşlem 250.001 bayt.
Yani bu koşullarda kesinlikten vazgeçmek bellek kazandırmıyor, tersine kaybettiriyor.

Olasılıksal üyelik yapısının kazandığı yer aralığın **seyrek** olduğu yerdir. Kütüphanenin ikinci
sorusu şudur: bugün hangi kitap kimliği en az bir kez elden geçti? Kitap kimliği yoğun bir sayı
değil, on üç haneli bir dizgi; bit eşlem için adres yok. Orada tam küme dizginin kendisini tutmak
zorundadır, olasılıksal yapı ise dizginin karmasını kullanır ve boyu üye sayısına bağlı kalır.
Seçim kuralı yapının adında değil, **anahtar uzayının yoğunluğundadır**.

Bir de birleştirme var. Haftalık tekil okur sorusu yedi günün sonucunu birleştirmeyi gerektirir. Bit
eşlemler bit düzeyinde "veya" ile birleşir ve sonuç kesin kalır; kardinalite kestirimleri yazmaç
başına en büyüğü alınarak birleşir ve sapma büyümez; tam kümeler birleştirilirken bütün üyeler
yeniden dokunulur ve maliyet gün sayısıyla çarpılır. Günlük 4.096 baytlık yazmaç dizisi bir yıl
saklandığında 1.495.040 bayt tutar — tek bir günün tam kümesinden hâlâ küçüktür ve yıl içindeki her
alt aralık için ayrı yanıt verebilir.

#### Özet

- Tekil sayım ile üyelik iki ayrı sorudur ve ayrı yapılarla yanıtlanır: kardinalite kestirimi sayıyı
  verir ama üyeliği hiçbir kesinlikte yanıtlamaz, çünkü üyeyi temsil etmez.
- Yoğun bir numara aralığında bit eşlem tam kümeden on altı kat az yer tutar (250.001'e karşı
  4.194.304 bayt) ve hiçbir şey kaybetmez; maliyeti giriş sayısına değil aralık genişliğine bağlıdır
  ve dokuz haneli numarada 125.000.000 bayta çıkar.
- Olasılıksal üyelik yapısının hatası tek yönlüdür: kaçırılan üye sıfır, yanlış olumlu oranı ise
  yapının boyuyla seçilir — 131.072 baytta %42,09, 524.288 baytta %2,53 ve ölçülen oran kuramsal
  oranla örtüşür.
- Yoğun aralıkta kesinlikten vazgeçmek bellek kazandırmaz: %2,53 hatalı yapı 524.288 bayt, kesin bit
  eşlem 250.001 bayt ister. Olasılıksal yapı ancak anahtar uzayı seyrekken kazanır.
- Kardinalite kestirimi 4.096 baytla %1,50 sapma verir; tam kümenin bin yirmi dört katı az yer tutar
  ve karşılığında hangi okurun sayıldığı bilgisini geri alınamaz biçimde bırakır.

#### Sonraki Adım

Bu derste ölçülen yapılar zamanı hiç umursamadı. Bit eşlem "bugün ödünç aldı mı" sorusuna yanıt
veriyor ama **ne zaman** aldığını bilmiyor; kardinalite kestirimi gün sonunda bir sayı üretiyor ama
o sayının hangi sırayla toplandığını söyleyemiyor. Aynı gün içinde okunması gereken bir soru daha
var: kütüphanenin raf yerleştirme ekibi, gecikme bildirimi işi ve arama dizini güncellemesi, olayları
**oldukları sırayla ve her biri kendi kaldığı yerden** görmek zorunda. Buraya kadarki hiçbir yapı
bunu vermiyor — küme sıra tutmuyor, sayaç geçmiş tutmuyor, liste ise okuyan her tarafın kendi
konumunu ayrı ayrı hatırlamasına yardım etmiyor. Sonraki ders olayları sırasıyla saklayan ve her
okuyan tarafın konumunu yapının kendisinde tutan yapıyı ele alır.

### Akışlar

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/akislar

Sıralı olay günlüğünün bir veri yapısı olarak ele alınması: giriş kimliğinin zaman ve sıradan kurulması, giriş başına baytın alan alan sayılması, uzunluk ve süre sınırının bir günlük etkinlik akışında karşılaştırılması ve okunmamış payın tutulan baytı belirlemesi.

Önceki yapıların hiçbiri sırayı ve okuma konumunu birlikte tutmuyordu. Küme üyeyi tutar ama girişlerin
hangi sırayla eklendiğini unutur; sayaç toplamı tutar ama geçmişi bırakmaz; liste sırayı tutar ama
okuyan her tarafın nerede kaldığını okuyanın kendisine bırakır. Kütüphanenin üç ayrı işi — raf
yerleştirme, gecikme bildirimi ve arama dizini güncellemesi — aynı olay dizisini **oldukları sırayla**
ve **her biri kendi kaldığı yerden** görmek zorunda.

**Akış** bunu tek bir yapıda birleştirir: girişler eklendikleri sırayla durur, her girişin sıralanabilir
bir kimliği vardır ve her **tüketici grubu** yalnız bir sınır kimliği tutarak nerede kaldığını
hatırlar. Bu ders akışı bir ileti kanalı olarak değil, **bellekte yer tutan bir veri yapısı** olarak
ele alır: giriş kaç bayt, kimlik ne işe yarıyor ve girişler ne zaman atılıyor.

#### Giriş Kimliği

Kimlik iki parçadan kurulur: girişin eklendiği andaki saat ve o saat içindeki sıra. Saat ilerlemediğinde
sıra ilerler, dolayısıyla ayrı bir sayaç yapısına gerek kalmadan iki giriş asla aynı kimliği almaz.

```js
// giris-kimligi.mjs — akis girisinin kimligi ve bayti. Kimlik <zaman>-<sira> biciminde
// uretilir: saat ilerlemediginde sira ilerler, boylece ayni milisaniyede cakisma olmaz.
const uretec = () => { let sonMs = -1, sira = 0;
  return (ms) => { if (ms === sonMs) sira += 1; else { sonMs = ms; sira = 0; } return `${ms}-${sira}`; }; };
const uret = uretec();
const kimlikler = [999, 1000, 1000, 1000, 1001, 1001].map(uret);
console.log("bir yigilmada uretilen kimlikler: " + kimlikler.join("  "));

const cozumle = (k) => k.split("-").map(Number);
const kucukMu = (a, b) => { const [x, y] = cozumle(a), [p, q] = cozumle(b);
  return x !== p ? x < p : y < q; };
console.log(`sayisal karsilastirma  999-0 < 1000-0: ${kucukMu("999-0", "1000-0")}`);
console.log(`dizgi karsilastirmasi  999-0 < 1000-0: ${"999-0" < "1000-0"} (kimlik dizgi olarak siralanmaz)`);
const sinir = "1000-1";                       // tuketici grubunun en son onayladigi kimlik
console.log(`${sinir} sonrasi okunacak giris: ` + kimlikler.filter((k) => kucukMu(sinir, k)).join(" "));

const USTVERI = 24, GOSTERGE = 8;             // kimlik 16 + zincir gostergesi 8; alan basina 8
const g = { tur: "odunc", okur: "418302", kitap: "9780000041173", sube: "sube-3" };
let toplam = USTVERI;
console.log("\nalan".padEnd(8) + "ad".padStart(4) + "deger".padStart(7) + "gosterge".padStart(10) +
  "bayt".padStart(6));
for (const [a, v] of Object.entries(g)) {
  const b = Buffer.byteLength(a) + Buffer.byteLength(v) + GOSTERGE; toplam += b;
  console.log(a.padEnd(8) + String(Buffer.byteLength(a)).padStart(4) +
    String(Buffer.byteLength(v)).padStart(7) + String(GOSTERGE).padStart(10) + String(b).padStart(6));
}
console.log(`ustveri`.padEnd(8) + "".padStart(4) + "".padStart(7) + "".padStart(10) +
  String(USTVERI).padStart(6));
console.log(`giris toplami: ${toplam} bayt; ayni veri alan adlari olmadan ` +
  `${toplam - Object.keys(g).reduce((s, a) => s + Buffer.byteLength(a), 0)} bayt tutardi`);
```

```
bir yigilmada uretilen kimlikler: 999-0  1000-0  1000-1  1000-2  1001-0  1001-1
sayisal karsilastirma  999-0 < 1000-0: true
dizgi karsilastirmasi  999-0 < 1000-0: false (kimlik dizgi olarak siralanmaz)
1000-1 sonrasi okunacak giris: 1000-2 1001-0 1001-1

alan     ad  deger  gosterge  bayt
tur        3      5         8    16
okur       4      6         8    18
kitap      5     13         8    26
sube       4      6         8    18
ustveri                          24
giris toplami: 102 bayt; ayni veri alan adlari olmadan 86 bayt tutardi
```

Kimliğin üç işi var. Birincisi **sıra**: girişler kimliğe göre sıralanır ve bu sıra ekleme sırasıdır.
İkincisi **sınır**: bir tüketici grubunun tuttuğu tek şey en son onayladığı kimliktir; "1000-1
sonrası" ifadesi grubun okuma konumunu on altı bayta indirir. Üçüncüsü **çakışmazlık**: aynı
milisaniyeye düşen üç giriş 1000-0, 1000-1 ve 1000-2 olur.

Karşılaştırmanın sayısal olduğu satır ihmal edilecek bir ayrıntı değil. Kimlik bir dizgi gibi
karşılaştırılırsa 999-0 ile 1000-0 ters sıralanır, çünkü basamak sayıları farklıdır; sıra güvencesi
kimliğin biçiminden değil, **çözümlenerek karşılaştırılmasından** gelir.

Bayt tablosu bir tasarım kararını görünür kılıyor. Girişin 102 baytının 16'sı alan adlarıdır ve bu
adlar her girişte yeniden yazılır. Günde 600.000 giriş için bu, yalnız dört sözcüğün tekrarı olarak
9,15 MiB demektir. Kısa alan adları ya da konuma dayalı bir düzen bu payı düşürür; karşılığında akışı
okuyan tarafın alan sırasını bilmesi gerekir, yani günlüğün kendi kendini açıklaması kaybedilir.

#### Bir Günün Akışı

Akışın bellek maliyeti giriş baytı ile **kaç girişin tutulduğunun** çarpımıdır ve ikinci çarpanı
belirleyen şey kırpma politikasıdır.

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BY14 | günlük etkinlik girişi | 600.000 | bütün şubelerin ödünç, iade, ayırtma ve raf hareketi |
| BY15 | giriş üstverisi | 24 bayt | kimlik 16 bayt, zincir göstergesi 8 bayt |
| BY16 | tüketici grubu | 3 grup, 1500 / 1000 / 700 giriş/dk | üç işin işleme hızı ayrı |
| BY17 | saatlik yoğunluk eğrisi | tepe çarpanı 2,8 | kütüphane gündüz açık, gece neredeyse durgun |

BY16 bu dersin karar verdiren varsayımıdır: üç grubun hızı eşit olsaydı kırpma politikası tek başına
belleği belirlerdi, eşit olmadığı için belleği **en yavaş grup** belirliyor.

```js
// akis.mjs — bir gunluk etkinlik akisi: kirpma politikalari ve okunmamis payin bellege etkisi.
// Gun 1440 dakikalik adima bolunur, varislar saat carpanlarindan uretilir, tohum gorunur.
const OLAY = 600_000, TOHUM = 20260731, DK = 1440, USTVERI = 24, GOSTERGE = 8;
const CARPAN = [0.05, 0.03, 0.02, 0.02, 0.03, 0.08, 0.2, 0.6, 1.6, 2.2, 2.6, 2.4,
                1.8, 2.0, 2.6, 2.8, 2.5, 2.0, 1.4, 0.9, 0.5, 0.3, 0.15, 0.08];
const GRUP = [["raf yerlestirme", 1500], ["gecikme bildirimi", 1000], ["arama dizini", 700]];
let d = TOHUM;
const rast = () => { d = (d + 0x6D2B79F5) | 0; let t = Math.imul(d ^ (d >>> 15), 1 | d);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 2 ** 32; };
const TUR = ["odunc", "iade", "ayirtma", "raf-degisimi"];
const giris = (i) => ({ tur: TUR[Math.floor(rast() * 4)], okur: String(100000 + (i % 900000)),
  kitap: String(9780000000000 + (i % 120000)), sube: `sube-${1 + Math.floor(rast() * 6)}` });
const bayt = (g) => USTVERI + Object.entries(g)
  .reduce((s, [a, v]) => s + Buffer.byteLength(a) + Buffer.byteLength(v) + GOSTERGE, 0);

const olcek = CARPAN.reduce((s, c) => s + c, 0) * 60;
const gb = new Uint16Array(OLAY * 2), gt = new Uint16Array(OLAY * 2);
let n = 0;
for (let t = 0; t < DK; t += 1) {
  const k = Math.round((OLAY * CARPAN[Math.floor(t / 60)]) / olcek);
  for (let i = 0; i < k; i += 1) { gb[n] = bayt(giris(n)); gt[n] = t; n += 1; }
}
const on = new Float64Array(n + 1);                          // kumulatif bayt
for (let i = 0; i < n; i += 1) on[i + 1] = on[i] + gb[i];
const tr = (x) => x.toLocaleString("tr-TR", { maximumFractionDigits: 2 });
const saatDk = (t) => `${Math.floor(t / 60)}:${String(t % 60).padStart(2, "0")}`;
let enAz = gb[0], enCok = gb[0];
for (let j = 1; j < n; j += 1) { if (gb[j] < enAz) enAz = gb[j]; if (gb[j] > enCok) enCok = gb[j]; }
console.log(`model: ${tr(n)} giris/gun, ${DK} dakikalik adim, tohum ${TOHUM}`);
console.log(`giris bayti: ortalama ${tr(on[n] / n)}, en kucuk ${enAz}, en buyuk ${enCok}; ` +
  `gunun tamami ${tr(Math.round(on[n] / 1024 ** 2))} MiB\n`);

function kos(ad, kirp, durakla = null) {
  const imlec = GRUP.map(() => 0);
  let bas = 0, son = 0, i = 0, tepe = 0, tepeBayt = 0, tepeDk = 0, kayip = 0;
  for (let t = 0; t < DK; t += 1) {
    while (i < n && gt[i] === t) { i += 1; son += 1; }
    for (let g = 0; g < GRUP.length; g += 1) {
      const durgun = durakla && g === durakla[0] && t >= durakla[1] && t < durakla[2];
      if (!durgun) imlec[g] = Math.min(son, imlec[g] + GRUP[g][1]);
    }
    bas = Math.max(bas, kirp(son, t, imlec));
    for (let g = 0; g < GRUP.length; g += 1)
      if (imlec[g] < bas) { kayip += bas - imlec[g]; imlec[g] = bas; }
    if (son - bas > tepe) { tepe = son - bas; tepeBayt = on[son] - on[bas]; tepeDk = t; }
  }
  console.log(ad.padEnd(26) + tr(son - bas).padStart(11) + tr(tepe).padStart(11) +
    tr(Math.round(tepeBayt / 1024)).padStart(11) + saatDk(tepeDk).padStart(9) +
    tr(kayip).padStart(11));
}
const ilkAdim = (t, T) => { let l = 0, r = n; while (l < r) {
  const m = (l + r) >> 1; if (gt[m] < t - T) l = m + 1; else r = m; } return l; };
console.log("politika".padEnd(26) + "gun sonu".padStart(11) + "tepe giris".padStart(11) +
  "tepe KiB".padStart(11) + "tepe dk".padStart(9) + "kacirilan".padStart(11));
kos("kirpma yok", () => 0);
kos("uzunluk 50.000", (son) => son - 50_000);
kos("sure 60 dk", (son, t) => ilkAdim(t, 60));
kos("guvenli (en yavas grup)", (son, t, im) => Math.min(...im));
kos("guvenli + 30 dk duraklama", (son, t, im) => Math.min(...im), [2, 1020, 1050]);

console.log("\ngrup".padEnd(26) + "hiz/dk".padStart(9) + "tepe okunmamis".padStart(16) +
  "tepe KiB".padStart(11) + "tepe dk".padStart(9) + "gun sonu".padStart(10));
const imlec = GRUP.map(() => 0), tepe = GRUP.map(() => [0, 0, 0]);
let son = 0, i = 0;
for (let t = 0; t < DK; t += 1) {
  while (i < n && gt[i] === t) { i += 1; son += 1; }
  for (let g = 0; g < GRUP.length; g += 1) {
    imlec[g] = Math.min(son, imlec[g] + GRUP[g][1]);
    if (son - imlec[g] > tepe[g][0]) tepe[g] = [son - imlec[g], on[son] - on[imlec[g]], t];
  }
}
for (let g = 0; g < GRUP.length; g += 1)
  console.log(GRUP[g][0].padEnd(26) + tr(GRUP[g][1]).padStart(9) + tr(tepe[g][0]).padStart(16) +
    tr(Math.round(tepe[g][1] / 1024)).padStart(11) + saatDk(tepe[g][2]).padStart(9) +
    tr(son - imlec[g]).padStart(10));
```

```
model: 600.000 giris/gun, 1440 dakikalik adim, tohum 20260731
giris bayti: ortalama 104, en kucuk 101, en buyuk 109; gunun tamami 60 MiB

politika                     gun sonu tepe giris   tepe KiB  tepe dk  kacirilan
kirpma yok                    600.000    600.000     60.935    23:59          0
uzunluk 50.000                 50.000     50.000      5.078     8:45     38.920
sure 60 dk                      1.856     63.488      6.447    15:59     46.220
guvenli (en yavas grup)             0     88.920      9.031    17:59          0
guvenli + 30 dk duraklama           0    109.920     11.163    17:59          0

grup                        hiz/dk  tepe okunmamis   tepe KiB  tepe dk  gun sonu
raf yerlestirme               1.500               0          0     0:00         0
gecikme bildirimi             1.000           2.520        256    15:59         0
arama dizini                    700          88.920      9.031    17:59         0
```

Bu sayılar **ölçüm** sınıfındadır; girişlerin dağılımı saat eğrisine ve tohuma bağlı, politikalar
arasındaki fark bağlı değil.

#### Okunmamış Pay

Üst tablo dört politikayı aynı gün üzerinde karşılaştırıyor. **Kırpma yok** akışı 60.935 KiB'ye
çıkarıyor ve hiçbir girişi kaçırmıyor: geçmiş sorgulanabilir kalıyor, bedeli günün tamamının bellekte
durması. **Uzunluk 50.000** belleği 5.078 KiB'de sabitliyor — on iki kat az — ama arama dizini 38.920
girişi hiç görmeden kaybediyor. **Süre 60 dk** ilk bakışta daha ölçülü görünüyor, gün sonunda yalnız
1.856 giriş tutuyor; oysa tepede 63.488 girişe çıkıyor ve 46.220 giriş kaçırıyor. Aradaki fark
politikanın neyi sabitlediğidir: uzunluk sınırı **belleği** sabitler, süre sınırı **yaşı** sabitler ve
belleği varış hızına bırakır. Yoğunluk üç katına çıkarsa süre sınırlı akış üç kat yer tutar.

**Güvenli kırpma** dördüncü seçenektir: bir giriş, bütün gruplar onu okuyana kadar silinmez.
Kaçırılan sıfıra iner, tutulan bayt 9.031 KiB'de tepe yapar ve gün sonunda sıfırlanır. Ama tavan
artık operatörün elinde değildir. Alt tablo bunu gösteriyor: raf yerleştirme hiç geride kalmıyor,
gecikme bildirimi tepede 2.520 giriş biriktiriyor, arama dizini 88.920. Akışın tuttuğu 9.031 KiB'nin
tamamı en yavaş grubun okumadığı paydır — akış bir kuyruk olduğu için değil, **en gerideki sınır
kimliğinden öncesini atamadığı** için.

Son satır bunun ne kadar keskin olduğunu ölçüyor. Arama dizini tepe saatte otuz dakika duraksa akış
109.920 girişe çıkıyor: tam 21.000 giriş ve 2.132 KiB fazla, yani duraklama süresi çarpı grubun
hızı. Bellek maliyeti burada akışın değil, **akışı okumayan tarafın** kararıdır.

Bunun karşıtı da aynı ölçüde nettir: okuyan taraf sayısı belleği artırmaz. Üç grup üç sınır kimliği
tutar, on altışar bayttan kırk sekiz bayt; akışa dördüncü, beşinci, onuncu grup eklemek 60 MiB'lik
gövdeye on altı bayt ekler. Akışı bir listeden ayıran şey budur: liste okundukça tüketilir ve ikinci
okuyucu için ikinci bir kopya gerekir, akış bir kez tutulur ve konum okuyanın tarafında durur.

#### Özet

- Giriş kimliği zaman ile sıradan kurulur ve üç iş görür: ekleme sırasını verir, aynı milisaniyedeki
  girişleri ayırır ve bir tüketici grubunun okuma konumunu on altı bayta indirir; karşılaştırma
  sayısal olmak zorundadır, dizgi karşılaştırması 999-0 ile 1000-0'ı ters sıralar.
- Giriş baytının bir payı her seferinde tekrarlanan alan adlarıdır: 102 baytın 16'sı, günde 9,15 MiB.
  Kısaltmak akışın kendi kendini açıklamasını kaybettirir.
- Uzunluk sınırı belleği sabitler (5.078 KiB), süre sınırı yaşı sabitler ve belleği varış hızına
  bırakır (tepede 63.488 giriş); ikisi de en yavaş grubu bilmediği için 38.920 ve 46.220 giriş
  kaçırır.
- Güvenli kırpmada kaçırma sıfırdır ama tavan operatörün elinde değildir: tutulan 9.031 KiB'nin
  tamamı en yavaş grubun okunmamış payıdır ve o grup otuz dakika duraklarsa akış 21.000 giriş
  (2.132 KiB) büyür.
- Okuyan taraf sayısı belleği artırmaz, okumayan taraf artırır: üç grubun toplam defter maliyeti kırk
  sekiz bayttır.

#### Sonraki Adım

Akışın kimliği sıralanabilir olduğu için "şu andan sonrası" sorusu bir arama değil, bir konum
belirlemesiydi. Kütüphanenin bir sorusu daha var ve o sorunun sıralanabilir bir anahtarı yok: okur
bulunduğu noktaya en yakın toplama noktasını istiyor. İki boyutlu bir konumu tek bir eksende sıralamanın
her yolu bazı komşuları birbirinden uzağa düşürür, dolayısıyla ne sıralı küme ne de akış bu soruya
doğrudan yanıt verir. Sonraki ders aynı yakınlık sorgusunu bellek içi bir yapıyla kurar ve düz taramanın
taradığı aday sayısıyla karşılaştırır.

### Uzamsal Dizinler

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/uzamsal-dizinler

Yakınlık sorgusunun bellek içi bir yapıyla kurulması: aynı sorgunun düz tarama, hücre kovası ve uzamsal anahtarlı sıralı kümeyle çözülmesi, üç yapının tuttuğu ek baytın ve sorgu başına taradığı aday sayısının ölçülmesi, tanenin sorgu anında seçilebilmesinin neye mal olduğunun sayılması.

Akışın kimliği sıralanabilir olduğu için "şu andan sonrası" sorusu bir arama değil, bir konum
belirlemesiydi. Kütüphanenin bir sorusu daha var ve o sorunun doğal bir sıralaması yok: okur, durduğu
noktanın bir kilometre çevresindeki ödünç ve iade noktalarını istiyor. İki boyutlu bir konumu tek bir
eksende sıralamanın her yolu bazı komşuları birbirinden uzağa düşürür; ne sıralı küme ne de akış bu
soruya doğrudan yanıt verir.

Çözüm düzlemi hücrelere bölmek ve her noktaya bir **uzamsal anahtar** yazmaktır. Hücre boyunun nasıl
seçileceği bu dersin konusu değildir; o karar Vaka Çalışmaları kursunun Veri Yoğun Sistemler
konusunda, Konum Tabanlı Servis vakasında ölçülmüş ve verilmiştir. Burada tane bir **girdi**dir ve
sorulan şey başkadır: aynı yakınlık sorgusu bellek içi bir depoda hangi yapıyla kurulur, o yapı kaç
bayt fazladan tutar ve karşılığında kaç adayı elemekten kurtarır.

#### Uzamsal Anahtar

Anahtar iki koordinatın bitlerinin sırayla dizilmesiyle üretilir: her eksen on altı bite niceleniyor,
bitler dönüşümlü olarak yerleştiriliyor ve ortaya otuz iki bitlik tek bir sayı çıkıyor. Bu düzenin
tek işe yarar özelliği şudur: bir hücrenin bütün noktaları anahtarın **baştaki bitlerini** paylaşır,
dolayısıyla hücre sıralı bir dizide **bitişik bir aralıktır**. Kaç bitin paylaşıldığı taneyi belirler
— ilk on iki bit 1 km'lik hücreyi, ilk on altı bit 0,25 km'lik hücreyi verir.

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BY18 | ödünç ve iade noktası | 120.000 | şube, iade kutusu, okul ve istasyon noktaları |
| BY19 | bölge | 64 × 64 km | tek büyükşehir; düzlem yaklaşımı bir model basitleştirmesidir |
| BY20 | temel kayıt | 20 bayt | konum çifti 16 bayt, nokta kimliği 4 bayt |
| BY21 | sorgu yarıçapı | 1 km ve 0,25 km | yürüme mesafesi ve bina çevresi |

BY21 bu dersin karar verdiren varsayımıdır: tek bir yarıçap olsaydı üç yapıdan ikisi ayırt
edilemezdi.

#### Üç Yapı

Düzenek aynı 120.000 noktayı üç ayrı yapıya koyar ve aynı 300 sorguyu hepsine sorar. Bayt sayıları
tamponların kendi boyundan, karma tabloların maliyeti ise kodda görünen bir formülden okunur.

```js
// uzamsal.mjs — ayni yakinlik sorgusu uc bellek ici yapiyla: duz tarama, hucre kovasi ve
// uzamsal anahtarli sirali kume. Bayt sayilari tamponlardan ve acik bir formulden okunur.
// Bolge 64x64 km'lik bir duzlem olarak modellenir; noktalar kumelenmis, tohum gorunur.
const N = 120_000, KENAR = 64, SORGU = 300, TOHUM = 20260731, KUME = 24;
let d = TOHUM;
const rast = () => { d = (d + 0x6D2B79F5) | 0; let t = Math.imul(d ^ (d >>> 15), 1 | d);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 2 ** 32; };
const merkez = Array.from({ length: KUME }, () => [rast() * KENAR, rast() * KENAR]);
const nokta = () => {                                  // %35 duzgun, %65 kume cevresinde
  if (rast() < 0.35) return [rast() * KENAR, rast() * KENAR];
  const [mx, my] = merkez[Math.floor(rast() * KUME)];
  const a = rast() * 2 * Math.PI, r = -Math.log(1 - rast()) * 2.5;
  return [Math.min(KENAR - 1e-9, Math.max(0, mx + r * Math.cos(a))),
          Math.min(KENAR - 1e-9, Math.max(0, my + r * Math.sin(a)))];
};

const nx = new Float64Array(N), ny = new Float64Array(N), nid = new Int32Array(N);
for (let i = 0; i < N; i += 1) { const [x, y] = nokta(); nx[i] = x; ny[i] = y; nid[i] = 100000 + i; }
const TEMEL = nx.byteLength + ny.byteLength + nid.byteLength;

const yay = (v) => { v = (v | (v << 8)) & 0x00ff00ff; v = (v | (v << 4)) & 0x0f0f0f0f;
  v = (v | (v << 2)) & 0x33333333; v = (v | (v << 1)) & 0x55555555; return v >>> 0; };
const nicele = (u) => Math.min(65535, Math.floor((u / KENAR) * 65536));
const anahtar = (x, y) => ((yay(nicele(y)) << 1) | yay(nicele(x))) >>> 0;   // 32 bit uzamsal anahtar
const p2 = (n) => { let k = 1; while (k < n) k *= 2; return k; };

const ua = new Uint32Array(N), us = new Int32Array(N);   // sirali kume: anahtar + uye kimligi
for (let i = 0; i < N; i += 1) { ua[i] = anahtar(nx[i], ny[i]); us[i] = i; }
us.sort((a, b) => ua[a] - ua[b]);
const sa = new Uint32Array(N);
for (let i = 0; i < N; i += 1) sa[i] = ua[us[i]];
const SK_TABLO = p2(2 * N) * 8;                          // uye -> puan karma tablosu, yuva 8 bayt
const SK = sa.byteLength + us.byteLength + SK_TABLO;

const BIT = 6, KAYMA = 2 * (16 - BIT);                   // 2^6 = 64 hucre/eksen, hucre 1 km
const kovalar = new Map();
for (let i = 0; i < N; i += 1) {
  const h = Math.floor(ua[i] / 2 ** KAYMA);
  if (kovalar.has(h)) kovalar.get(h).push(i); else kovalar.set(h, [i]);
}
const dolu = kovalar.size;
const KOVA = p2(2 * dolu) * 12 + dolu * 8 + N * 4;       // yuva 12 + dizi ustverisi 8 + uye 4

const sorgular = Array.from({ length: SORGU }, nokta);
const tr = (x) => x.toLocaleString("tr-TR", { maximumFractionDigits: 2 });
const yakin = (q, i, R) => (nx[i] - q[0]) ** 2 + (ny[i] - q[1]) ** 2 <= R * R;
const altSinir = (v) => { let l = 0, r = N; while (l < r) {
  const m = (l + r) >> 1; if (sa[m] < v) l = m + 1; else r = m; } return l; };

function calis(R, bit) {                                 // sorgu kutusunu kaplayan hucreler
  const c = KENAR / 2 ** bit, k = Math.ceil(R / c), kayma = 2 * (16 - bit);
  let duzAday = 0, duzEs = 0, kovaAday = 0, kovaEs = 0, skAday = 0, skEs = 0, aralik = 0;
  for (const q of sorgular) {
    for (let i = 0; i < N; i += 1) { duzAday += 1; if (yakin(q, i, R)) duzEs += 1; }
    const cx = Math.floor(q[0] / c), cy = Math.floor(q[1] / c);
    for (let dy = -k; dy <= k; dy += 1) for (let dx = -k; dx <= k; dx += 1) {
      const x = cx + dx, y = cy + dy;
      if (x < 0 || y < 0 || x >= 2 ** bit || y >= 2 ** bit) continue;
      const h = ((yay(y) << 1) | yay(x)) >>> 0;
      if (bit === BIT) {                                 // hucre kovasi yalniz kendi tanesini bilir
        for (const i of kovalar.get(h) ?? []) { kovaAday += 1; if (yakin(q, i, R)) kovaEs += 1; }
      }
      const alt = h * 2 ** kayma, ust = alt + 2 ** kayma;   // sirali kumede bitisik aralik
      aralik += 1;
      for (let j = altSinir(alt); j < N && sa[j] < ust; j += 1) {
        skAday += 1; if (yakin(q, us[j], R)) skEs += 1;
      }
    }
  }
  return { duzAday: duzAday / SORGU, duzEs: duzEs / SORGU, kovaAday: kovaAday / SORGU,
    kovaEs: kovaEs / SORGU, skAday: skAday / SORGU, skEs: skEs / SORGU, aralik: aralik / SORGU, c };
}

console.log(`model: ${tr(N)} nokta, ${KENAR}x${KENAR} km, ${KUME} kume, ${SORGU} sorgu, tohum ${TOHUM}`);
console.log(`yogunluk ${tr(N / KENAR ** 2)} nokta/km2, dolu hucre ${tr(dolu)} (1 km tanesinde)\n`);
console.log("yapi".padEnd(30) + "temel bayt".padStart(12) + "dizin eki".padStart(12) +
  "uye basina ek".padStart(15) + "toplam".padStart(12));
for (const [ad, ek] of [["duz tarama (dizi)", 0], ["hucre kovasi, 1 km tane", KOVA],
                        ["uzamsal anahtarli sirali kume", SK]])
  console.log(ad.padEnd(30) + tr(TEMEL).padStart(12) + tr(ek).padStart(12) +
    (ek / N).toFixed(2).padStart(15) + tr(TEMEL + ek).padStart(12));

console.log("\nsorgu basina taranan aday (ayni 300 sorgu, ayni yaricap)");
console.log("yaricap".padEnd(10) + "tane".padStart(9) + "hucre".padStart(7) + "duz".padStart(10) +
  "kova".padStart(9) + "s.kume".padStart(9) + "eslesme (duz/kova/s.kume)".padStart(27));
for (const [R, bit] of [[1, BIT], [0.25, BIT], [0.25, 8]]) {
  const s = calis(R, bit), k = bit === BIT;
  console.log(`${R} km`.padEnd(10) + `${KENAR / 2 ** bit} km`.padStart(9) +
    s.aralik.toFixed(0).padStart(7) + tr(s.duzAday).padStart(10) +
    (k ? tr(s.kovaAday) : "-").padStart(9) + tr(s.skAday).padStart(9) +
    `${s.duzEs.toFixed(2)} / ${k ? s.kovaEs.toFixed(2) : "-"} / ${s.skEs.toFixed(2)}`.padStart(27));
}
```

```
model: 120.000 nokta, 64x64 km, 24 kume, 300 sorgu, tohum 20260731
yogunluk 29,3 nokta/km2, dolu hucre 4.096 (1 km tanesinde)

yapi                            temel bayt   dizin eki  uye basina ek      toplam
duz tarama (dizi)                2.400.000           0           0.00   2.400.000
hucre kovasi, 1 km tane          2.400.000     611.072           5.09   3.011.072
uzamsal anahtarli sirali kume    2.400.000   3.057.152          25.48   5.457.152

sorgu basina taranan aday (ayni 300 sorgu, ayni yaricap)
yaricap        tane  hucre       duz     kova   s.kume  eslesme (duz/kova/s.kume)
1 km           1 km      9   120.000   821,22   821,22   372.25 / 372.25 / 372.25
0.25 km        1 km      9   120.000   821,22   821,22      40.90 / 40.90 / 40.90
0.25 km     0.25 km      9   120.000        -    97,39          40.90 / - / 40.90
```

Bu sayılar **ölçüm** sınıfındadır; aday ve eşleşme sayıları tohuma ve kümelenme parametrelerine
bağlı, bayt sayıları bağlı değil — onlar tampon boylarıdır.

Son sütun her satırda üç yapının aynı eşleşmeyi bulduğunu söylüyor: 1 km'de 372,25, 0,25 km'de 40,90.
Dizin bir yaklaşıklık değil, bir **ön eleme**dir; kesin uzaklık her adayda ayrıca hesaplanır ve
hiçbir yapı bir sonucu kaçırmaz.

#### Ek Baytın Karşılığı

Üç yapı da aynı 2.400.000 baytlık temel kaydı taşıyor; fark **dizin eki** sütunundadır. Hücre kovası
üye başına 5,09 bayt ekliyor ve sorgu başına taranan adayı 120.000'den 821,22'ye indiriyor — yüz
kırk altı kat. Bu, dersteki en ucuz kazanç: 611.072 bayt karşılığında sorgu maliyetinin yüzde 99,3'ü
gidiyor.

Sıralı küme aynı sorguda hiçbir şey kazandırmıyor. Üye başına 25,48 bayt, yani hücre kovasının beş
katı yer tutuyor ve 1 km sorgusunda tastamam aynı 821,22 adayı tarıyor. Bu satır tek başına okunursa
sıralı küme kötü bir seçimdir.

Ek baytın nereye gittiği de sayılabilir: 3.057.152 baytın 2.097.152'si, yani yüzde 68,6'sı, üye
kimliğinden anahtara giden karma tablodur. Bu tablo sorgu için değil **güncelleme** için tutulur.
Gezici kütüphane durağı yer değiştirdiğinde üyenin sıralı dizideki eski yerini bulmak gerekir; tablo
olmadan bu, dizinin taranması demektir. Noktalar hiç kımıldamasaydı sıralı kümenin eki üye başına
25,48 bayttan 8 bayta inerdi — o durumda hücre kovasından yalnız üç bayt pahalı olurdu.

#### Tane Sorgu Anında Seçilirse

Farkı ikinci ve üçüncü satır gösteriyor. Okur bina çevresini, yani 0,25 km'yi sorduğunda hücre
kovası hâlâ 821,22 aday tarıyor: kovalar ekleme anında 1 km'lik taneye göre kurulmuştur ve daha ince
bir soru sorulduğunda kova yine dokuz kilometrekarelik alanı döndürür. Daha ince bir tane istemek,
bütün kovaların yeniden kurulması demektir.

Sıralı kümede anahtar otuz iki bitin tamamını taşıdığı için **her önek uzunluğu geçerli bir aralıktır**.
Aynı 0,25 km sorgusu ilk on altı biti kullanarak sorulduğunda taranan aday 97,39'a iniyor: sekiz kat
az iş ve aynı 40,90 eşleşme. Sıralı kümenin satın aldığı şey adaydan tasarruf değil,
**taneyi ekleme anında değil sorgu anında seçebilme** yeteneğidir.

Bu, üç yapıyı bir karar tablosuna oturtur. Düz tarama sıfır ek bayt tutar ve her sorguda yüz yirmi
bin kayda dokunur; nokta sayısı birkaç bini geçmiyorsa doğru seçimdir. Hücre kovası sorgu yarıçapı
sabitse en ucuz dizindir ve 5,09 baytla adayın yüzde 99,3'ünü eler. Uzamsal anahtarlı sıralı küme,
yarıçap sorgudan sorguya değiştiğinde ya da noktalar yer değiştirdiğinde 25,48 baytı hak eder;
sabit yarıçaplı ve durağan bir kümede o bayt boşa gider.

#### Özet

- Uzamsal anahtar iki koordinatın bitlerini dönüşümlü dizerek tek bir sıralanabilir sayı üretir;
  bir hücre böylece sıralı dizide bitişik bir aralık olur ve önek uzunluğu taneyi belirler.
- Üç yapı da aynı sonucu döndürür: 1 km'de 372,25, 0,25 km'de 40,90 eşleşme. Dizin bir yaklaşıklık
  değil ön elemedir ve kesin uzaklık her adayda ayrıca hesaplanır.
- Hücre kovası dersin en ucuz kazancıdır: üye başına 5,09 bayt karşılığında taranan aday
  120.000'den 821,22'ye iner, yani sorgu maliyetinin yüzde 99,3'ü gider.
- Sıralı küme üye başına 25,48 bayt ister ve kendi tanesindeki sorguda hiçbir aday kazandırmaz; bu
  baytın yüzde 68,6'sı sorgu için değil, üyenin yerini güncelleyebilmek için tutulan karma tablodur.
- Fazladan baytın karşılığı yarıçap değiştiğinde görünür: 0,25 km sorgusunda hücre kovası 821,22,
  sıralı küme 97,39 aday tarar, çünkü tane ekleme anında değil sorgu anında seçilir.

#### Sonraki Adım

Yakınlık sorgusu bir liste döndürüyor ve iş orada bitmiyor. Okur listeden bir noktayı seçtiğinde
kütüphane şunları yapmak zorunda: o noktadaki kopya sayısını okumak, okurun açık ödünç sınırını
denetlemek, kopya sayısını bir azaltmak, ödünç kümesine kaydı eklemek ve etkinlik akışına giriş
yazmak. Buraya kadarki her yapı bu adımlardan **birini** atomik yapabiliyor; beşini birden yapabilen
bir şey görülmedi. Adımlar tek tek gönderildiğinde araya başka bir okurun aynı kopyayı alması
sığar. Sonraki ders bu çok adımlı işi iki biçimde kurar ve aradaki farkı gidiş sayısı, yarış
penceresi ve engellenen süre olarak sayar.

### Sunucu Tarafı Betikleme

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/sunucu-tarafi-betikleme

Çok adımlı işin istemcide ve depoda atomik olarak yapılmasının karşılaştırılması: gidiş sayısının, yarış penceresinin ve fazladan verilen ödüncün eş zamanlılığa göre ölçülmesi, kilit girişlerinin belleğinin betik önbelleğiyle karşılaştırılması ve sınırsız bir betiğin bütün istemcileri bekletmesinin sayılması.

Önceki dersler her yapıyı tek başına ölçtü ve her birinde tek bir işlem atomikti: sayaç bir arttı,
küme bir üye aldı, akış bir giriş kabul etti. Kütüphanenin gerçek işi bunlardan biri değil, beşinin
sırasıyla yapılmasıdır. Bir ödünç verilirken kopya sayısı okunur, okurun açık ödünç sayısı okunur,
sınırlar denetlenir, kopya sayısı bir azaltılır ve ödünç kaydı yazılır. Beş adımın her biri kendi
başına atomiktir; **beşi birlikte değildir.**

Bu ders o boşluğu ölçer: adımlar istemciden tek tek gönderildiğinde araya ne sığar, aynı iş depoda
tek parçada çalıştırıldığında ne değişir ve bu iki yolun her biri kaç bayt tutar.

#### Yarış Penceresi

Aşağıdaki düzenek aynı işi iki biçimde çalıştırır. İlkinde her istemcinin beş adımı diğerlerinin
adımlarıyla serpiştirilir; ikincisinde bir istemcinin adımları bitişik çalışır. Zamanlayıcı tohumlu
olduğu için iki biçim **aynı serpiştirme kuralıyla** karşılaştırılır.

```js
// odunc-yarisi.mjs — ayni cok adimli odunc isi iki bicimde: istemcide adim adim ve depoda
// tek parcada. Zamanlayici tohumludur; her deneme ayni kurala gore serpistirilir.
const KOPYA = 3, DENEME = 500, TOHUM = 20260731, TEPE = 800;
const ADIM = ["kopya sayisini oku", "okurun acik oduncunu oku", "sinirlari denetle",
              "kopya sayisini bir azalt", "odunc kaydini yaz"];

function deneme(C, tohum, atomik) {
  let d = tohum;
  const rast = () => { d = (d + 0x6D2B79F5) | 0; let t = Math.imul(d ^ (d >>> 15), 1 | d);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 2 ** 32; };
  const depo = { kopya: KOPYA, verilen: 0 };
  const ist = Array.from({ length: C }, () => ({ a: 0, k: 0, gecti: false, oku: -1, yaz: -1 }));
  let kalan = C, an = 0;
  while (kalan > 0) {
    const uygun = ist.filter((c) => c.a < ADIM.length);
    const c = uygun[Math.floor(rast() * uygun.length)];
    const n = atomik ? ADIM.length - c.a : 1;              // atomik: kalan adimlar bitisik calisir
    for (let s = 0; s < n; s += 1, c.a += 1, an += 1) {
      if (c.a === 0) { c.k = depo.kopya; c.oku = an; }
      if (c.a === 2) c.gecti = c.k > 0;
      if (c.a === 3) { c.yaz = an; if (c.gecti) { depo.kopya = c.k - 1; depo.verilen += 1; } }
    }
    if (c.a === ADIM.length) kalan -= 1;
  }
  const pencere = ist.reduce((s, c) => s + (c.yaz - c.oku - 3), 0) / C;
  return { verilen: depo.verilen, fazla: Math.max(0, depo.verilen - KOPYA), sayac: depo.kopya, pencere };
}

const tr = (x) => x.toLocaleString("tr-TR", { maximumFractionDigits: 2 });
console.log(`model: ${KOPYA} kopya, ${DENEME} deneme/satir, tohum ${TOHUM}, ` +
  `is basina ${ADIM.length} adim`);
console.log(`gidis sayisi: istemcide ${ADIM.length}, kilitli istemcide ${ADIM.length + 2}, ` +
  `depoda atomik 1`);
console.log(`tepe ${TEPE} odunc/s icin: ${tr(TEPE * ADIM.length)} / ${tr(TEPE * (ADIM.length + 2))}` +
  ` / ${tr(TEPE)} gidis/s\n`);
console.log("istemci".padEnd(9) + "pencere".padStart(9) + "verilen".padStart(9) +
  "fazla".padStart(8) + "sayac".padStart(8) + "fazla%".padStart(9) +
  "atomik verilen".padStart(16) + "atomik fazla".padStart(14));
for (const C of [2, 4, 8, 16, 32]) {
  let p = 0, v = 0, f = 0, s = 0, kac = 0, av = 0, af = 0;
  for (let t = 0; t < DENEME; t += 1) {
    const r = deneme(C, TOHUM + t * 7919, false), a = deneme(C, TOHUM + t * 7919, true);
    p += r.pencere; v += r.verilen; f += r.fazla; s += r.sayac; if (r.fazla > 0) kac += 1;
    av += a.verilen; af += a.fazla;
  }
  console.log(`${C}`.padEnd(9) + (p / DENEME).toFixed(2).padStart(9) +
    (v / DENEME).toFixed(2).padStart(9) + (f / DENEME).toFixed(2).padStart(8) +
    (s / DENEME).toFixed(2).padStart(8) + `%${((kac / DENEME) * 100).toFixed(1)}`.padStart(9) +
    (av / DENEME).toFixed(2).padStart(16) + (af / DENEME).toFixed(2).padStart(14));
}
```

```
model: 3 kopya, 500 deneme/satir, tohum 20260731, is basina 5 adim
gidis sayisi: istemcide 5, kilitli istemcide 7, depoda atomik 1
tepe 800 odunc/s icin: 4.000 / 5.600 / 800 gidis/s

istemci    pencere  verilen   fazla   sayac   fazla%  atomik verilen  atomik fazla
2             2.34     2.00    0.00    1.88     %0.0            2.00          0.00
4             6.80     4.00    1.00    1.69   %100.0            3.00          0.00
8            16.12     7.99    4.99    1.49   %100.0            3.00          0.00
16           34.48    15.99   12.99    1.32   %100.0            3.00          0.00
32           70.94    31.93   28.93    1.14   %100.0            3.00          0.00
```

Bu sayılar **ölçüm** sınıfındadır; ondalık basamaklar tohuma bağlı, sütunlar arasındaki ilişki bağlı
değil.

**Pencere** sütunu okumayla yazma arasına düşen yabancı adım sayısıdır ve eş zamanlılıkla doğrusal
büyüyor: iki istemcide 2,34 adım, otuz iki istemcide 70,94. Pencere büyüdükçe okunan kopya sayısının
yazma anında hâlâ geçerli olma olasılığı düşüyor ve dört istemciden itibaren denemelerin **yüzde
100'ünde** fazladan ödünç veriliyor.

En sert satır sonuncusu. Otuz iki istemci üç kopyalık bir kitap için sıraya girdiğinde 31,93 ödünç
veriliyor, yani 28,93'ü karşılıksız; okurlar rafta olmayan kitapları ödünç almış oluyor. Sayaç ise
1,14 gösteriyor — depoya bakan biri **hâlâ bir kopya var** sanıyor. Sayacın yanlış olması ayrı bir
kusur değil, aynı kusurun ikinci yüzüdür: her istemci kendi okuduğu değerin bir eksiğini yazdığı için
son yazan, kendinden önceki bütün azaltmaları siliyor.

Atomik sütunlar aynı serpiştirmeyle çalıştı ve hiçbir satırda fazla vermedi: istemci sayısı kopya
sayısını aştığında tam üç ödünç verildi, iki istemcide ise iki. Aradaki
fark bir eniyileme değil, **doğruluk** farkıdır.

#### Atomikliği Kurmanın İki Yolu ve Bedeli

Yarışın iki çaresi var. Birincisi kitabın anahtarına bir **kilit** koymaktır: iş beş adım yerine
yedi gidişe çıkar (kilit al, kilit bırak) ve kilit girişleri bellekte yer tutar. İkincisi beş adımı
**depoya tek parça** olarak göndermektir: tek gidiş, araya hiçbir adım giremez.

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BY22 | tepe ödünç işi | 800/s | bütün şubelerin tepe saati |
| BY23 | açık ödünç kaydı | 400.000 | gecikme taramasının dolaşacağı derlem |
| BY24 | deponun dokunma hızı | 2.000.000 dokunma/s | makineye bağlıdır; oranlar bağlı değil |
| BY25 | tepe komut hızı | 40.000 komut/s | bütün istemcilerin toplamı |

BY24 bu dersin ortama bağlı tek varsayımıdır ve yalnız dokunma sayısını milisaniyeye çevirmek için
kullanılır; tablodaki eşdeğer iş ve bekleyen komut oranları hıza bölündüğünde değişmez.

```js
// betik-engeli.mjs — tek is parcacikli depoda betigin dokundugu giris ile engelledigi is.
// Dokunma sayilari depodan sayilir; sureye cevirme BY24'un dokunma hizina dayanir ve o
// varsayim makineye baglidir. Oranlar (esdeger is, bekleyen komut) hiza bagli degildir.
const KAYIT = 400_000, TEPE_ODUNC = 800, TEPE_KOMUT = 40_000, HIZ = 2_000_000, KOPYA_SINIR = 5;

class Depo {                                   // tek is parcacikli bellek ici depo
  constructor() { this.m = new Map(); this.dokunma = 0; }
  oku(k) { this.dokunma += 1; return this.m.get(k); }
  yaz(k, v) { this.dokunma += 1; this.m.set(k, v); }
  dolas(n, f) { let i = 0; for (const [k, v] of this.m) { if (i >= n) break; this.dokunma += 1; f(k, v); i += 1; } }
}
const oduncBetigi = (depo, kitap, okur) => {   // sinirli: dokunma sayisi veriden bagimsiz
  const kopya = depo.oku(`kopya:${kitap}`) ?? 0;
  const acik = depo.oku(`acik:${okur}`) ?? 0;
  if (kopya <= 0 || acik >= KOPYA_SINIR) return "ret";
  depo.yaz(`kopya:${kitap}`, kopya - 1);
  depo.yaz(`acik:${okur}`, acik + 1);
  depo.yaz(`odunc:${okur}:${kitap}`, 1);
  return "kabul";
};
const gecikmisTarama = (depo, n, gun) => {     // sinirsiz: dokunma sayisi derlem boyuyla buyur
  let ucret = 0;
  depo.dolas(n, (k, v) => { if (typeof v === "number" && v < gun) ucret += (gun - v) * 25; });
  return ucret;
};

const depo = new Depo();
for (let i = 0; i < KAYIT; i += 1) depo.yaz(`odunc:${i}`, 100 + (i % 60));
depo.yaz("kopya:9780000041173", 3);

const tr = (x) => x.toLocaleString("tr-TR", { maximumFractionDigits: 2 });
console.log(`model: ${tr(KAYIT)} acik odunc kaydi, tepe ${tr(TEPE_ODUNC)} odunc/s ve ` +
  `${tr(TEPE_KOMUT)} komut/s`);
console.log(`BY24 dokunma hizi ${tr(HIZ)} dokunma/s (makineye baglidir; oranlar bagli degil)\n`);
console.log("betik".padEnd(30) + "dokunma".padStart(10) + "esdeger odunc".padStart(15) +
  "sure ms".padStart(10) + "bekleyen komut".padStart(16) + "toplam bekleme komut.ms".padStart(25));
const ms3 = (x) => x.toLocaleString("tr-TR", { maximumFractionDigits: 3 });
const satir = (ad, dokunma) => {
  const ms = (dokunma / HIZ) * 1000, bekleyen = (TEPE_KOMUT * ms) / 1000;
  console.log(ad.padEnd(30) + tr(dokunma).padStart(10) + tr(dokunma / 5).padStart(15) +
    ms3(ms).padStart(10) + ms3(bekleyen).padStart(16) + tr((bekleyen * ms) / 2).padStart(25));
};
depo.dokunma = 0; oduncBetigi(depo, "9780000041173", 418302);
satir("odunc betigi (sinirli)", depo.dokunma);
for (const n of [10_000, 100_000, KAYIT]) {
  depo.dokunma = 0; gecikmisTarama(depo, n, 130);
  satir(`gecikmis tarama, ${tr(n)} kayit`, depo.dokunma);
}

const govde = [oduncBetigi, gecikmisTarama].map((f) => Buffer.byteLength(f.toString()));
const OZET = 40;                                        // betigi adlandiran ozetin bayti
const kilitAnahtar = "kilit:kopya:9780000041173", kilitDeger = "istemci-0000000000000000-0000";
const KILIT_USTVERI = 56;                               // sure sonu, gostergeler, yuva pay
const kilitGiris = Buffer.byteLength(kilitAnahtar) + Buffer.byteLength(kilitDeger) + KILIT_USTVERI;
console.log(`\nbetik onbellegi: ${govde.length} betik, govde ${tr(govde.reduce((a, b) => a + b, 0))}` +
  ` bayt + ozet ${govde.length * OZET} bayt = ${tr(govde.reduce((a, b) => a + b, 0) + govde.length * OZET)} bayt (sabit)`);
console.log(`kilit girisi: anahtar ${Buffer.byteLength(kilitAnahtar)} + deger ` +
  `${Buffer.byteLength(kilitDeger)} + ustveri ${KILIT_USTVERI} = ${kilitGiris} bayt`);
console.log(`tepe ${tr(TEPE_ODUNC)} es zamanli odunc icin kilit tablosu: ` +
  `${tr(TEPE_ODUNC * kilitGiris)} bayt (es zamanlilikla buyur)`);
```

```
model: 400.000 acik odunc kaydi, tepe 800 odunc/s ve 40.000 komut/s
BY24 dokunma hizi 2.000.000 dokunma/s (makineye baglidir; oranlar bagli degil)

betik                            dokunma  esdeger odunc   sure ms  bekleyen komut  toplam bekleme komut.ms
odunc betigi (sinirli)                 5              1     0,003             0,1                        0
gecikmis tarama, 10.000 kayit     10.000          2.000         5             200                      500
gecikmis tarama, 100.000 kayit   100.000         20.000        50           2.000                   50.000
gecikmis tarama, 400.000 kayit   400.000         80.000       200           8.000                  800.000

betik onbellegi: 2 betik, govde 562 bayt + ozet 80 bayt = 642 bayt (sabit)
kilit girisi: anahtar 25 + deger 29 + ustveri 56 = 110 bayt
tepe 800 es zamanli odunc icin kilit tablosu: 88.000 bayt (es zamanlilikla buyur)
```

Alt üç satır iki yolun bellek farkını veriyor. Betik önbelleği **642 bayt** tutuyor ve bu sayı sabit:
bir betik ne kadar çağrılırsa çağrılsın gövdesi bir kez saklanır, eş zamanlılıkla büyümez. Kilit
yolunda ise her eş zamanlı iş için bir giriş açılır; 110 baytlık giriş tepede 800 kez tutulur ve
tablo **88.000 bayta** çıkar. Aradaki oran yüz otuz yediye bir ve asıl fark oranda değil
**eğimde**: biri sabit, öteki eş zamanlılıkla doğrusal.

Kilidin ikinci bedeli bellekte görünmüyor. Kilidi alan istemci yanıt vermeden kaybolursa kitap süre
sonu dolana kadar kilitli kalır; süre sonu kısa tutulursa iş bitmeden kilit düşer ve yarış geri
gelir. Betik yolunda böyle bir pencere yoktur, çünkü tutulacak bir kilit yoktur.

#### Betiğin Kendi Bedeli

Depo tek iş parçacıklıdır ve bu, atomikliği ücretsiz kılan şeyin ta kendisidir: bir betik çalışırken
başka hiçbir komut çalışamaz, bu yüzden araya adım giremez. Aynı özellik faturayı da yazar.

Üst tablonun ilk satırı ödünç betiğidir: beş dokunma, bir ödünç işine denk iş, hiç kimseyi bekletmez.
Sonraki üç satır aynı depoya gönderilen gecikme taramasıdır ve dokunma sayısı derlemin boyuyla
büyür. 400.000 kayıtlık derlemde betik 400.000 kez dokunuyor; bu, **80.000 ödünç işinin** yerini tutan
bir süre demektir. BY24'ün hızıyla 200 milisaniye, bu sürede 8.000 komut kuyrukta birikiyor ve
toplam bekleme 800.000 komut·milisaniyeye çıkıyor. Yavaş olan sorgu değil, **o sırada gelen her şey**.

Sayıların gösterdiği kural şudur: betiğin doğruluğu adım sayısından bağımsızdır, ama etkisi
değildir. Ayrım kısa betikle uzun betik arasında değil, **sınırlı** betikle **sınırsız** betik
arasındadır. Ödünç betiği her zaman beş dokunma yapar; kütüphanenin derlemi on kat büyüse de beş
kalır. Gecikme taraması derlemle birlikte büyür ve bugün kabul edilebilir olan gecikme yarın kabul
edilemez olur, çünkü artan şey veri boyudur, betiğin metni değil.

Bu, çok adımlı işin üç yolunu bir karar tablosuna oturtur. **Kilitsiz istemci** sıfır depo baytı
tutar, tepede 4.000 gidiş/s üretir ve doğruluğu kaybeder: otuz iki istemcide 28,93 fazladan ödünç.
**Kilitli istemci** doğruluğu geri alır, 5.600 gidiş/s'ye çıkar, 88.000 bayt tutar ve kilidi düşen
istemci sorununu üstlenir. **Depoda atomik betik** 800 gidiş/s ve 642 bayt ister, doğruluğu tam
verir ve karşılığında tek bir kural dayatır: betiğin dokunma sayısı veriye bağlı olmayacak. Bu
kursun Bellek İçi Kalıplar konusu aynı denetimi izleme tabanlı bir yolla ayrıca ölçer.

#### Özet

- Çok adımlı iş istemcide yürütüldüğünde okumayla yazma arasındaki yarış penceresi eş zamanlılıkla
  doğrusal büyür: iki istemcide 2,34 adım, otuz ikide 70,94; dört istemciden itibaren denemelerin
  yüzde 100'ünde fazladan ödünç verilir.
- Kaybın iki yüzü vardır: otuz iki istemcide üç kopyalık kitaptan 31,93 ödünç verilir ve sayaç 1,14
  gösterir — depo hem fazla verdiğini bilmez hem de kalanı yanlış bilir.
- Aynı iş depoda tek parça çalıştığında hiçbir eş zamanlılıkta fazla verilmez — üç kopya varken en
  çok üç ödünç çıkar — ve gidiş sayısı
  tepede 4.000/s'den 800/s'ye iner; kilitli yol doğruluğu verir ama 5.600 gidiş/s'ye çıkar.
- Atomikliğin bellek maliyeti seçilen yola göre farklı eğimlidir: betik önbelleği 642 baytta sabit
  kalır, kilit tablosu eş zamanlı iş başına 110 bayt ister ve tepede 88.000 bayta çıkar.
- Tek iş parçacıklı depoda betiğin bedeli beklemedir ve sınır adım sayısında değil, adım sayısının
  veriye bağlı olup olmadığındadır: 400.000 kayda dokunan tarama 80.000 ödünç işi kadar yer kaplar
  ve 8.000 komutu kuyrukta bekletir.

#### Sonraki Adım

Bu konu dokuz derste bir depo kurdu. Anahtar alanı, dizgiler ve sayaçlar, listeler, karma yapılar,
kümeler ve sıralı kümeler, bit eşlemler ve olasılıksal yapılar, akışlar, uzamsal dizinler ve son
olarak çok adımlı işin atomikliği — her birinde tutulan bayt sayıldı, karşılığında satın alınan şey
ölçüldü ve daha az bellekle yapanın neyi kaybettiği yazıldı.

Bütün bu yapıların tek bir ortak özelliği hiç sorgulanmadı: **hepsi bellekte duruyor.** Sayaç
bellekte, sıralı küme bellekte, akışın 60 MiB'lik gövdesi bellekte, betik önbelleğinin 642 baytı
bellekte. Süreç durduğunda bunların hiçbiri kalmaz. Bu kursun ilk dersi dayanıklılığın bir **ayar**
olduğunu söylemişti; o ayarın ne olduğu, hangi seçenekleri sunduğu ve her seçeneğin ne kadar veri
kaybına izin verdiği hiç açılmadı. Sonraki konu o ayarı açar ve ilk sorusu şudur: bellekteki bu
yapıları diske yazmanın kaç yolu var ve her yol, çöken bir süreçte kaç yazmayı geride bırakır.

## Kalıcılık ve Bellek Yönetimi

Dayanıklılığın ve bellek sınırının birer ayara dönüşmesi: aralıklı disk yazımıyla anlık görüntü kalıcılığı, komut günlüğü ile yeniden yazım maliyeti, dayanıklılık ve başarım arasındaki kalıcılık seçimi, bellek sınırına ulaşıldığında hangi anahtarın atılacağını belirleyen tahliye politikaları ve süre sonu yönetiminin tembel ile etkin temizleme dengesi.

### Anlık Görüntü Kalıcılığı

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/anlik-goruntu-kaliciligi

Deponun bütününü aralıklarla diske yazan kalıcılık biçimi: aralığın kayıp penceresindeki yazma sayısına etkisi, üç aralığın disk maliyeti, bellekte tutulan bayt ile görüntü dosyasının boyutu arasındaki fark ve görüntü alırken duraklatmak ile kopyalamak arasındaki seçim.

Önceki konu ödünç sistemini bütünüyle bellekte kurdu: katalog girişleri, oturum kayıtları,
ödünç sayaçları, bekleme listeleri, sıralama tabloları. Her yapı için tutulan bayt sayıldı ve
karşılığında ne satın alındığı ölçüldü. Bütün o ölçümlerin ortak ve sessiz bir varsayımı
vardı: süreç çalışıyor. Süreç durduğunda geriye hiçbiri kalmaz. Dayanıklılığın bir ayar
olduğu söylendi; o ayarın ne yaptığı, neye mal olduğu ve kapalıyken tam olarak neyin yittiği
sorulmadı.

Bu ders ayarın ilk biçimini ele alır: belirli aralıklarla deponun **bütününü** diske yazmak.
İki sorusu vardır. Süreç durduğunda kaç yazma yiter ve görüntüyü almanın kendisi çalışmaya
devam eden depoya ne kadara mal olur? İkisi de aynı düğmeye — aralığa — bağlıdır ve ters
yönlere çeker.

#### Deponun ve Ölçünün Kurulumu

Ölçüm için ödünç deposu doğrudan yazılır. Depo bir anahtar–değer eşlemesidir; her yazmada
tuttuğu bayt sayısını da günceller, böylece bellek maliyeti tahmin edilmez, sayılır.

**KB1.** Bir girişin bellek maliyeti anahtarın baytı, değerin baytı ve giriş başına sabit
48 bayt üstveridir. Bu 48 bayt karma tablo yuvasını, iki uzunluk alanını ve giriş
işaretçisini temsil eder; gerçek bir çalışma zamanında sayı biraz oynar, ölçümün yönü
oynamaz.

```js
// depo.mjs — bellek içi ödünç deposu ve anlık görüntü alıcısı
export const USTVERI = 48;          // KB1: giriş başına sabit üstveri (yuva + işaretçi + uzunluk)

export class Depo {
  constructor() { this.girisler = new Map(); this.bayt = 0; }
  #maliyet(a, d) { return Buffer.byteLength(a) + Buffer.byteLength(d) + USTVERI; }
  yaz(a, d) {
    const eski = this.girisler.get(a);
    if (eski !== undefined) this.bayt -= this.#maliyet(a, eski);
    this.girisler.set(a, d);
    this.bayt += this.#maliyet(a, d);
  }
  get sayi() { return this.girisler.size; }
  goruntule() {                     // anlık görüntü: bütün girişler tek geçişte diziye çevrilir
    let s = '';
    for (const [a, d] of this.girisler) s += a + '\t' + d + '\n';
    return s;
  }
}

export function uretec(tohum) {     // belirlenimci sözde rastgele üreteç
  let a = tohum >>> 0;
  return () => {
    a = (a + 0x6d2b79f5) >>> 0;
    let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

export function yukUret(sayi, tohum, uyeSayisi = 20000) {
  const r = uretec(tohum), liste = [];
  for (let i = 0; i < sayi; i++) {
    const u = Math.floor(r() * uyeSayisi);
    liste.push([
      'odunc:u' + String(u).padStart(5, '0'),
      `sube=${1 + (u % 8)};gun=${[7, 14, 28][i % 3]};yenileme=${i % 4}`,
    ]);
  }
  return liste;
}
```

Yük, kütüphanenin ödünç trafiğidir: yirmi bin üyeye dağılan altmış bin ödünç yazması. Üreteç
kendi yazılmıştır ve tohumu görünürdür, dolayısıyla aşağıdaki sayıların hepsi yeniden
üretilebilir.

#### Aralık, Kayıp Penceresi ve Disk

Anlık görüntü kalıcılığında dayanıklılık tek bir düğmeyle ayarlanır: kaç yazmada bir görüntü
alınacağı. Aşağıdaki koşum aynı altmış bin yazmayı üç aralıkla oynatır. Her yazmadan sonra "o
anda süreç dursaydı kaç yazma yiterdi" sorusunun yanıtı biriktirilir; sonda ortalaması ve en
kötüsü basılır.

```js
// aralik.mjs — üç anlık görüntü aralığının kayıp penceresi ve disk maliyeti
import { writeFileSync, statSync } from 'node:fs';
import { Depo, yukUret } from './depo.mjs';

const YUK = yukUret(60000, 20260731);
console.log('yük: 60000 yazma, 20000 üye, tohum 20260731');
console.log('aralık  görüntü  diske(MB)  ortalama kayıp  en kötü kayıp');

for (const aralik of [1000, 5000, 20000]) {
  const depo = new Depo();
  let goruntu = 0, diskBayt = 0, kayipToplam = 0, enKotu = 0, sonGoruntu = 0;
  for (let i = 0; i < YUK.length; i++) {
    depo.yaz(YUK[i][0], YUK[i][1]);
    const kayip = (i + 1) - sonGoruntu;        // süreç bu anda dursa yitecek yazma sayısı
    kayipToplam += kayip;
    if (kayip > enKotu) enKotu = kayip;
    if ((i + 1) % aralik === 0) {
      diskBayt += Buffer.byteLength(depo.goruntule());
      goruntu++;
      sonGoruntu = i + 1;
    }
  }
  console.log(String(aralik).padStart(6), String(goruntu).padStart(8),
    String(Math.round(diskBayt / 1048576)).padStart(10),
    String(Math.round(kayipToplam / YUK.length)).padStart(15), String(enKotu).padStart(14));
}

const son = new Depo();
for (const [a, d] of YUK) son.yaz(a, d);
writeFileSync('goruntu.txt', son.goruntule());
console.log('depo:', son.sayi, 'giriş,', son.bayt, 'bayt bellekte |',
  'görüntü dosyası', statSync('goruntu.txt').size, 'bayt');
```

```
yük: 60000 yazma, 20000 üye, tohum 20260731
aralık  görüntü  diske(MB)  ortalama kayıp  en kötü kayıp
  1000       60         30             501           1000
  5000       12          6            2501           5000
 20000        3          2           10001          20000
depo: 19040 giriş, 1593100 bayt bellekte | görüntü dosyası 717260 bayt
```

Kayıp penceresi aralığın doğrudan işlevidir: ortalaması aralığın yarısı, en kötüsü aralığın
kendisidir. Bin yazmalık aralık, yirmi binliğe göre ortalama 9.500 yazmayı kurtarır. Bu satın
alınan şeydir ve bedeli aynı satırda yazılıdır: 2 MB yerine 30 MB disk yazımı, yani on beş
kat. Bedelin böyle büyümesinin nedeni, anlık görüntünün **her seferinde deponun tamamını**
yazmasıdır. Bin yazmalık bir değişiklik için de yaklaşık beş yüz kilobaytlık bütün depo
yeniden diske gider.

Son satır ikinci bir ayrımı verir. Altmış bin yazmadan geriye 19.040 giriş kalmıştır; geri
kalanı aynı üyelerin kayıtlarının üzerine yazmıştır. Anlık görüntünün boyutu yazma sayısıyla
değil **anahtar sayısıyla** büyür — bu, sonraki dersteki günlük biçimiyle arasındaki temel
farktır. Aynı veri bellekte 1.593.100 bayt, dosyada 717.260 bayt tutar: dosya, belleğin
yarısından azıdır. Fark üstveridir. Karma tablo yuvaları, işaretçiler ve uzunluk alanları
diske gitmez. Diskteki dosyanın küçüklüğüne bakıp deponun belleğe rahat sığacağı sonucunu
çıkarmak, giriş başına iki katlık bir hatayı baştan kabul etmek olur.

#### Görüntü Almanın Kendi Maliyeti

Görüntünün tutarlı olması gerekir: yarısı eski, yarısı yeni bir dosyadan kurtarma yapılamaz.
Tutarlılığı sağlamanın iki yolu vardır ve ikisi de bedava değildir.

**Duraklatmak.** Görüntü alınırken yazmalar bekletilir. Bellek maliyeti sıfırdır, bedelini
istemciler bekleme süresiyle öder.

**Kopyalamak.** Yazmalar sürer; görüntüye girecek bir giriş değiştirilmek üzereyken eski hâli
kopyalanır ve görüntü kopyayı okur. Kimse beklemez, bedelini bellek öder: görüntü bitene
kadar kopyalar da tutulur.

**KB2.** Görüntü alma anlık değildir; taranan her bin giriş kırk yazmalık süre alır. Süre,
koşum ortamına değil giriş sayısına bağlı bir birimle sayılır.

```js
// kopya.mjs — görüntü alma sırasındaki iki seçenek: duraklatmak ya da kopyalamak
import { Depo, USTVERI, yukUret } from './depo.mjs';

const YUK = yukUret(60000, 20260731);
const TARAMA = 40;                    // KB2: her 1.000 giriş için 40 yazmalık tarama süresi
console.log('aralık  görüntü  tarama  duraklatan: bekleyen  kopyalayan: giriş  tepe bayt');

for (const aralik of [1000, 5000, 20000]) {
  const depo = new Depo();
  let goruntu = 0, sure = 0, bekleyen = 0, kopyaSayi = 0, tepe = 0, kalan = 0, kopya = null;
  for (let i = 0; i < YUK.length; i++) {
    const [a, d] = YUK[i];
    if (kalan > 0) {                  // görüntü alınıyor: değişen giriş önce kopyalanır
      const eski = depo.girisler.get(a);
      if (eski !== undefined && !kopya.has(a)) { kopya.set(a, eski); kopyaSayi++; }
      if (--kalan === 0) {
        let b = 0;
        for (const [k, v] of kopya) b += Buffer.byteLength(k) + Buffer.byteLength(v) + USTVERI;
        if (b > tepe) tepe = b;
        kopya = null;
      }
    }
    depo.yaz(a, d);
    if ((i + 1) % aralik === 0) {
      sure = Math.ceil((depo.sayi / 1000) * TARAMA);
      goruntu++; bekleyen += sure; kalan = sure; kopya = new Map();
    }
  }
  console.log(String(aralik).padStart(6), String(goruntu).padStart(8), String(sure).padStart(7),
    String(bekleyen).padStart(21), String(kopyaSayi).padStart(18), String(tepe).padStart(10));
}
```

```
aralık  görüntü  tarama  duraklatan: bekleyen  kopyalayan: giriş  tepe bayt
  1000       60     762                 33285              24985      59565
  5000       12     762                  6946               4710      58498
 20000        3     762                  1965                913      49266
```

Tarama süresi üç satırda da aynıdır: 762 yazma. Görüntü alma aralığa değil giriş sayısına
bağlıdır, dolayısıyla aralığı kısaltmak taramayı hızlandırmaz, yalnız daha sık yapar.

Duraklatan seçenek bin yazmalık aralıkta altmış bin yazmanın 33.285'ini bekletir — yükün
yarısından fazlası. Yirmi binlik aralıkta bu sayı 1.965'e iner. Kopyalayan seçenek hiçbir
yazmayı bekletmez; karşılığında görüntü sürerken en çok 59.565 bayt fazladan tutar. Bu,
deponun 1.593.100 baytının yaklaşık yüzde 3,7'sidir. Tepe değerin üç aralıkta da birbirine
yakın çıkması aynı nedendendir: tepeyi belirleyen aralık değil, tarama süresi boyunca kaç
**ayrı** anahtara dokunulduğudur. 762 yazma en fazla 762 ayrı giriş kopyalatabilirdi; tekrar
eden üyeler yüzünden sayı görüntü başına dört yüz civarında kalır. Yazmalar birkaç sıcak
anahtara yığılsaydı kopya daha da azalırdı; her yazma ayrı bir anahtara gitseydi tepe iki
katına yaklaşırdı.

Toplam kopyalanan giriş sayısı ise aralık küçüldükçe artar: 24.985'e karşılık 913. Kopyalama
tepe bellekte ucuzdur, ama görüntü sıklaştıkça yapılan iş olarak birikir.

Bin yazmalık aralıkta gizli bir sınır da görünür hâle gelir. Tarama 762 yazma sürüyorsa, depo
bin yazmanın 762'sinde görüntü alıyor demektir. Aralık tarama süresinin altına indirildiğinde
görüntüler üst üste biner; yeni istek ya atlanır ya da öncekiyle yarışır. Her iki durumda da
gerçekleşen aralık ayarlanandan büyük olur ve yapılandırmada yazan kayıp penceresi gerçeği
anlatmaz.

#### Aralık Kararı

Üç ölçüm tek tabloda toplanır. Sayılar altmış bin yazmalık koşuma aittir.

| Aralık | Ortalama kayıp | En kötü kayıp | Disk yazımı | Kopyalanan giriş |
|---|---|---|---|---|
| 1.000 | 501 yazma | 1.000 yazma | 30 MB | 24.985 |
| 5.000 | 2.501 yazma | 5.000 yazma | 6 MB | 4.710 |
| 20.000 | 10.001 yazma | 20.000 yazma | 2 MB | 913 |

Karar, kaybedilen yazmanın ne olduğuna bakılarak verilir. Katalog önbelleği için yirmi binlik
aralık bile fazla korumadır: yiten girişler zaten kaynağından yeniden üretilebilir. Ödünç
sayaçları ve bekleme listesi için on bin yazmalık ortalama kayıp, kütüphanenin bir günlük
işlemine denk gelebilir ve bunun geri alınması elle yapılır.

Anlık görüntünün yapısal sınırı buradadır: aralık ne kadar kısaltılırsa kısaltılsın kayıp
penceresi sıfır olmaz, çünkü son görüntüden sonraki her yazma korumasızdır. Aralığı tarama
süresinin altına indirmek de bir işe yaramaz. Kayıp penceresini gerçekten kapatmak için
deponun tamamını değil, **her yazmanın kendisini** kaydeden bir biçim gerekir.

#### Özet

- Anlık görüntü kalıcılığı deponun bütününü aralıklarla diske yazar; kayıp penceresinin
  ortalaması aralığın yarısı, en kötüsü aralığın kendisidir.
- Ölçümde bin yazmalık aralık ortalama kaybı 10.001'den 501'e indirdi ve bunun karşılığında
  disk yazımını 2 MB'tan 30 MB'a çıkardı; görüntü her seferinde deponun tamamını yazar.
- Görüntünün boyutu yazma sayısıyla değil anahtar sayısıyla büyür: 60.000 yazma 19.040 giriş
  bıraktı ve dosya 717.260 bayt oldu.
- Aynı veri bellekte 1.593.100 bayt tutar; aradaki fark diske gitmeyen üstveridir, bu yüzden
  dosya boyutundan bellek gereksinimi çıkarılamaz.
- Görüntü alırken duraklatmak 33.285 yazmayı bekletti, kopyalamak hiçbirini bekletmedi ama
  tepe 59.565 bayt fazladan tuttu; tepeyi aralık değil tarama süresindeki ayrı anahtar sayısı
  belirler.

#### Sonraki Adım

Anlık görüntü, deponun bir **anındaki** hâlini yazar ve iki an arasında olan biteni yazmaz.
Kayıp penceresini kapatmanın tek yolu, durumun kendisi yerine duruma götüren yazmaları
kaydetmektir. Sonraki ders bunu ele alır: her yazma bir kayıt olarak dosyanın sonuna eklenir,
kurtarma bu kayıtları yeniden oynatır. Ölçülecek olan, kaydın yazma başına kaç bayt tuttuğu,
dosya sınırsız büyüdüğünde onu kısaltan yeniden yazımın ne kadar yer kazandırdığı ve
kayıtların diske ne sıklıkta eşzamanlanacağı seçiminin kayıp penceresiyle yazma maliyetini
nasıl karşı karşıya getirdiğidir.

### Ekleme Günlüğü Kalıcılığı

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/ekleme-gunlugu-kaliciligi

Durumun değil yazmanın kaydedildiği kalıcılık biçimi: komut günlüğünün yazma başına kaç bayt tuttuğu, günlüğün anlık görüntüye oranla nasıl büyüdüğü, yeniden yazımın kazandırdığı yer ve her komutta, saniyede bir ya da işletim sistemine bırakarak eşzamanlamanın kayıp penceresiyle yazma maliyetine etkisi.

Anlık görüntü, deponun bir andaki hâlini yazar. İki an arasında olan biten hiçbir yere
kaydedilmez, bu yüzden kayıp penceresi aralık ne kadar kısaltılırsa kısaltılsın sıfıra
inmez. Kütüphanenin ödünç sayaçları için bu, en kötü durumda binlerce ödünç kaydının elle
geri toplanması demektir.

Bu ders ikinci biçimi ele alır: durumu değil **yazmayı** kaydetmek. Her değiştirici komut,
uygulandığı sırayla bir dosyanın sonuna eklenir. Kurtarma, boş bir depo üzerinde bu komutları
yeniden oynatır. Dosyanın sonuna eklemekten başka bir işlem yapılmadığı için yazma yolu
ucuzdur; bedeli iki yerde çıkar: dosya durmadan büyür ve "eklendi" ile "diskte" arasındaki
fark bir ayara dönüşür.

#### Yazmanın Kendisini Kaydetmek

Kayıt biçimi deponun komutunu olduğu gibi taşır: komut harfi, anahtar, değer. Yük önceki
dersle aynıdır — yirmi bin üyeye dağılan altmış bin ödünç yazması, aynı tohumla — böylece
iki biçimin sayıları doğrudan karşılaştırılabilir.

```js
// gunluk.mjs — komut günlüğü kaydı ve aynı ödünç yükü
export const kayit = (a, d) => `Y\t${a}\t${d}\n`;   // Y: yazma komutu

export function uretec(tohum) {
  let a = tohum >>> 0;
  return () => {
    a = (a + 0x6d2b79f5) >>> 0;
    let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

export function yukUret(sayi, tohum, uyeSayisi = 20000) {
  const r = uretec(tohum), liste = [];
  for (let i = 0; i < sayi; i++) {
    const u = Math.floor(r() * uyeSayisi);
    liste.push([
      'odunc:u' + String(u).padStart(5, '0'),
      `sube=${1 + (u % 8)};gun=${[7, 14, 28][i % 3]};yenileme=${i % 4}`,
    ]);
  }
  return liste;
}
```

#### Günlüğün Büyümesi ve Yeniden Yazım

Aşağıdaki koşum yükü gerçekten diske yazar ve yirmi binde bir durup iki büyüklüğü yan yana
koyar: o ana kadarki günlük dosyası ve aynı durumun anlık görüntüsü.

```js
// buyume.mjs — günlüğün büyümesi ve yeniden yazımın kazandırdığı yer
import { appendFileSync, writeFileSync, statSync, rmSync } from 'node:fs';
import { kayit, yukUret } from './gunluk.mjs';

const YUK = yukUret(60000, 20260731);
rmSync('odunc.log', { force: true });
const durum = new Map();
let tampon = '', gunlukBayt = 0;
console.log('yazma   günlük bayt  anahtar  görüntü bayt  günlük/görüntü');
for (let i = 0; i < YUK.length; i++) {
  const [a, d] = YUK[i];
  tampon += kayit(a, d);                       // her yazma günlüğün sonuna eklenir
  durum.set(a, d);
  if ((i + 1) % 20000 === 0) {
    appendFileSync('odunc.log', tampon);
    gunlukBayt += Buffer.byteLength(tampon);
    tampon = '';
    let g = 0;
    for (const [k, v] of durum) g += Buffer.byteLength(k) + Buffer.byteLength(v) + 2;
    console.log(String(i + 1).padStart(5), String(gunlukBayt).padStart(12),
      String(durum.size).padStart(8), String(g).padStart(13),
      (gunlukBayt / g).toFixed(2).padStart(15));
  }
}
console.log('dosya:', statSync('odunc.log').size, 'bayt,', YUK.length, 'kayıt,',
  (statSync('odunc.log').size / YUK.length).toFixed(1), 'bayt/yazma');

let yeni = '';                                  // yeniden yazım: yalnız yaşayan anahtarlar
for (const [a, d] of durum) yeni += kayit(a, d);
writeFileSync('odunc-yeni.log', yeni);
const eski = statSync('odunc.log').size, sonra = statSync('odunc-yeni.log').size;
console.log('yeniden yazım:', durum.size, 'kayıt,', sonra, 'bayt |',
  'kazanç', eski - sonra, 'bayt (%' + (((eski - sonra) / eski) * 100).toFixed(1) + ')');
```

```
yazma   günlük bayt  anahtar  görüntü bayt  günlük/görüntü
20000       793333    12717        479015            1.66
40000      1586666    17330        652842            2.43
60000      2380000    19040        717260            3.32
dosya: 2380000 bayt, 60000 kayıt, 39.7 bayt/yazma
yeniden yazım: 19040 kayıt, 755340 bayt | kazanç 1624660 bayt (%68.3)
```

Yazma başına maliyet sabittir: 39,7 bayt. Günlük dosyası **yazma sayısıyla** doğrusal büyür ve
neyi yazdığına bakmaz; aynı üyenin ödünç kaydının onuncu kez güncellenmesi de dosyaya tam
bir kayıt ekler. Anlık görüntü ise **anahtar sayısıyla** büyür ve üzerine yazılan değerlerin
geçmişini taşımaz. Son sütun aradaki makası verir: yirmi bin yazmada günlük görüntünün 1,66
katıdır, altmış binde 3,32 katı. Kütüphane çalışmayı sürdürdükçe oran büyümeye devam eder,
çünkü paydaki sayı durmaz, paydadaki sayı yirmi bin üyede doyar.

Bu büyüme, ekleme günlüğünün doğrudan sonucudur ve tek çaresi vardır: **yeniden yazım**.
Deponun o anki durumu taranır ve yaşayan her anahtar için tek bir kayıt üretilir; eski dosya
bununla değiştirilir. Ölçümde 60.000 kayıt 19.040 kayda iner, dosya 2.380.000 bayttan 755.340
bayta düşer, kazanç yüzde 68,3'tür.

İki ayrıntı burada okunur. Birincisi, yeniden yazılmış günlük (755.340 bayt) aynı durumun
anlık görüntüsünden (717.260 bayt) tam 38.080 bayt büyüktür; bu, 19.040 kaydın her birine
düşen iki baytlık komut önekidir. Yeniden yazımdan sonra günlük, biçim farkı dışında bir
anlık görüntüdür. İkincisi ve daha önemlisi: yeniden yazım da bütün girişleri taramak
zorundadır. Yani önceki dersteki görüntü alma maliyeti — duraklatmak ya da kopyalamak — bu
yolda da vardır, yalnız daha seyrek ortaya çıkar. Ekleme günlüğü görüntü almayı ortadan
kaldırmaz; ne zaman yapılacağını dosyanın boyutuna bağlar.

#### Eşzamanlama Ayarı

Bir kaydın "yazıldı" sayılması üç ayrı yerde gerçekleşebilir. Kayıt önce **uygulamanın kendi
tamponuna** girer. Tampon boşaltıldığında **işletim sisteminin tamponuna** geçer; süreç bu
noktadan sonra çökse bile kayıt yaşar, çünkü onu artık işletim sistemi tutmaktadır. Kaydın
**kalıcı ortama** geçtiğinin garantilenmesi ise ayrı bir istektir; makinenin elektriği
kesildiğinde yalnız o adımı geçmiş kayıtlar durur.

Ayar bu üç durak arasında seçim yapar. Aşağıdaki koşum aynı bin beş yüz kaydı üç kiple yazar
ve koşumdan bağımsız nicelikleri basar: yazma çağrısı sayısı, eşzamanlama sayısı, dosya
boyutu. Süre de ölçülür, ama tek başına bir sayı olarak değil sıralama olarak basılır; süre
diskin türüne ve makinenin yüküne bağlıdır.

**KB3.** Kütüphanenin en yoğun saatinde depoya saniyede 3.000 yazma gelir.
**KB4.** Uygulama tamponu 65.536 bayttır ve işletim sistemi kendi tamponunu beş saniyede bir
kalıcı ortama geçirir.

```js
// eszamanlama.mjs — üç eşzamanlama ayarının maliyeti ve kayıp penceresi
import { openSync, writeSync, fsyncSync, closeSync, statSync, rmSync } from 'node:fs';
import { kayit, yukUret } from './gunluk.mjs';

const N = 1500, HIZ = 3000, TAMPON = 65536, GERIYAZMA = 5;   // KB3, KB4
const YUK = yukUret(N, 20260731);

function kos(ad, donem, tamponla) {
  rmSync(ad, { force: true });
  const fd = openSync(ad, 'w');
  let cagri = 0, es = 0, t = '', tb = 0;
  const bas = process.hrtime.bigint();
  for (let i = 0; i < N; i++) {
    const s = kayit(YUK[i][0], YUK[i][1]);
    if (tamponla) {                              // uygulama tamponu dolunca yazılır, eşzamanlama yok
      t += s; tb += Buffer.byteLength(s);
      if (tb >= TAMPON) { writeSync(fd, t); cagri++; t = ''; tb = 0; }
    } else {
      writeSync(fd, s); cagri++;
      if ((i + 1) % donem === 0) { fsyncSync(fd); es++; }
    }
  }
  if (t) { writeSync(fd, t); cagri++; }
  if (!tamponla && N % donem !== 0) { fsyncSync(fd); es++; }
  const sure = Number(process.hrtime.bigint() - bas);
  closeSync(fd);
  return { cagri, es, bayt: statSync(ad).size, sure };
}

const kipler = [
  ['her yazmada', kos('a.log', 1, false)],
  ['saniyede bir', kos('b.log', HIZ, false)],
  ['işletim sistemine bırak', kos('c.log', 0, true)],
];
console.log(`ölçüm: ${N} kayıt, aynı içerik, üç kip`);
console.log('kip                      yazma çağrısı  eşzamanlama  dosya baytı');
for (const [ad, s] of kipler)
  console.log(ad.padEnd(24), String(s.cagri).padStart(13), String(s.es).padStart(12),
    String(s.bayt).padStart(12));
console.log('süre sıralaması (yavaştan hızlıya):',
  [...kipler].sort((x, y) => y[1].sure - x[1].sure).map(([a]) => a).join(', '));

const bpk = kipler[0][1].bayt / N;              // kayıt başına bayt
const T = 60000;
console.log(`\n${T} yazmalık yük, saniyede ${HIZ} yazma, ${TAMPON} baytlık tampon,`,
  `${GERIYAZMA} saniyelik geri yazma`);
console.log('kip                      eşzamanlama  en kötü kayıp  ortalama kayıp');
const satir = (ad, es, kotu) => console.log(ad.padEnd(24), String(es).padStart(11),
  String(kotu).padStart(14), String(Math.round(kotu / 2)).padStart(15));
satir('her yazmada', T, 0);
satir('saniyede bir', Math.ceil(T / HIZ), HIZ);
satir('işletim sistemine bırak', 0, Math.floor(TAMPON / bpk) + GERIYAZMA * HIZ);
```

```
ölçüm: 1500 kayıt, aynı içerik, üç kip
kip                      yazma çağrısı  eşzamanlama  dosya baytı
her yazmada                       1500         1500        59500
saniyede bir                      1500            1        59500
işletim sistemine bırak              1            0        59500
süre sıralaması (yavaştan hızlıya): her yazmada, saniyede bir, işletim sistemine bırak

60000 yazmalık yük, saniyede 3000 yazma, 65536 baytlık tampon, 5 saniyelik geri yazma
kip                      eşzamanlama  en kötü kayıp  ortalama kayıp
her yazmada                    60000              0               0
saniyede bir                      20           3000            1500
işletim sistemine bırak            0          16652            8326
```

Üç kip aynı dosyayı üretir: 59.500 bayt, aynı içerik. Ayrılan tek şey, o baytların ne zaman
ve hangi garantiyle yerine geçtiğidir.

**Her yazmada** eşzamanlama, altmış bin yazmalık yükte altmış bin ayrı eşzamanlama demektir
ve kayıp penceresini sıfıra indirir. Bu ölçümün alındığı makinede eşzamanlama başına yaklaşık
3,8 milisaniye harcandı; sayı diske ve yüke göre değişir, bu yüzden çıktıya süre değil sıralama
basıldı. Değişmeyen nicelik eşzamanlama sayısıdır ve tabloda üç kip arasındaki fark dört
büyüklük mertebesindedir. Sıfır kayıp penceresinin bir koşulu daha vardır: eşzamanlama, yanıt
istemciye dönmeden **önce** bitmelidir. Kayıt diske geçmeden "ödünç alındı" yanıtı verilirse
pencere yeniden açılır.

**Saniyede bir** eşzamanlama, aynı yükte yirmi eşzamanlamaya iner; yazma yolundan üç bin kat
daha az bekleme çıkarır. Karşılığında en kötü durumda bir saniyelik iş, yani 3.000 ödünç
yazması yiter. Ortalama kayıp bunun yarısıdır.

**İşletim sistemine bırakmak** eşzamanlamayı tümüyle kaldırır ve yazma çağrısı sayısını da
düşürür: bin beş yüz kayıt tek bir çağrıyla yazıldı, çünkü hepsi 65.536 baytlık tampona sığdı.
En ucuz kip budur ve en kötü kaybı da odur: 16.652 yazma. Bu sayının nereden geldiği önemli,
çünkü iki ayrı gecikme toplanıyor — uygulama tamponundaki 1.652 kayıt ve işletim sistemine
verilmiş olup henüz kalıcı ortama geçmemiş beş saniyelik iş.

#### Ayarın Karşılığı

Üç ayar aynı sorunun üç yanıtıdır: bir ödünç yazmasının kaybının bedeli, o yazmayı diske
geçirmenin bedelinden büyük mü? Kütüphanenin oturum kayıtları için değildir; oturumu yiten
görevli yeniden açar. Ödünç sayaçları için sınırdadır. Ceza hesabına giren gecikme
kayıtları için büyüktür, çünkü kaybedilen kayıt paraya karşılık gelir ve geri toplanamaz.

Ekleme günlüğünün anlık görüntüye karşı asıl üstünlüğü, kayıp penceresini **yazma başına**
ayarlanabilir kılmasıdır. Bedeli de dosyanın boyutudur: aynı durum için 3,32 kat yer ve o
yeri geri almak için düzenli yeniden yazım. İki biçimin birbirinin eksiğini kapattığı buradan
görünür, ama hangisinin ne zaman seçileceği henüz tek bir tabloda toplanmadı.

#### Özet

- Ekleme günlüğü durumu değil komutu kaydeder; ölçümde yazma başına 39,7 bayt tuttu ve
  dosya yazma sayısıyla doğrusal büyüdü.
- Günlük yazma sayısıyla, anlık görüntü anahtar sayısıyla büyür: aynı veri için oran 20.000
  yazmada 1,66, 60.000 yazmada 3,32 kat oldu.
- Yeniden yazım 60.000 kaydı 19.040 kayda indirdi ve dosyayı yüzde 68,3 küçülttü; sonuçta
  ortaya çıkan dosya, iki baytlık komut öneki dışında bir anlık görüntüdür ve aynı tam tarama
  maliyetini taşır.
- Eşzamanlama ayarı kayıp penceresini belirler: her yazmada 0 yazma ve 60.000 eşzamanlama;
  saniyede bir 3.000 yazma ve 20 eşzamanlama; işletim sistemine bırakıldığında 16.652 yazma
  ve hiç eşzamanlama.
- Üç kip aynı 59.500 baytlık dosyayı üretti; aralarındaki fark içerikte değil, kaydın hangi
  durakta durduğu garantisindedir.

#### Sonraki Adım

İki kalıcılık biçimi de ölçüldü ve ikisinin de kendi başına eksik kaldığı görüldü: anlık
görüntü kayıp penceresini kapatamıyor, ekleme günlüğü boyutunu tutamıyor. Sonraki ders
seçimi tek bir tabloda toplar. Üç ölçü karşılaştırılır: sürecin yeniden başlamasından
hizmete hazır olmasına kadarki kurtarma adımı, kayıp penceresi ve sürekli maliyet. İki
yolun **birlikte** kullanıldığı düzen de aynı tabloya girer — görüntü tabanı, üzerine son
görüntüden sonraki kayıtlar — ve bu düzenin kurtarmayı neden hem kısalttığı hem de kayıp
penceresini dar tuttuğu sayıyla gösterilir.

### Kalıcılık Seçimi

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/kalicilik-secimi

Dört kalıcılık düzeninin aynı çökme senaryosunda karşılaştırılması: kurtarmanın kaç adım sürdüğü ve hangi düzenin eksik durum kurduğu, kayıp penceresi, diske yazılan bayt ile eşzamanlama sayısı olarak sürekli maliyet ve iki yolun birlikte kullanıldığında anlık görüntünün işinin dayanıklılıktan kurtarma süresine kayması.

İki kalıcılık biçimi de ölçüldü ve ikisi de kendi başına eksik kaldı. Anlık görüntü kayıp
penceresini kapatamıyor: aralık ne olursa olsun son görüntüden sonraki yazmalar korumasız.
Ekleme günlüğü ise kayıp penceresini yazma başına ayarlıyor, ama dosyası aynı durum için üç
kattan fazla yer tutuyor ve kurtarma bütün geçmişi yeniden oynatmak zorunda kalıyor.

Bu ders seçimi yapar. Seçim üç ölçü üzerinden verilir ve üçü de aynı koşumda sayılır:

**Kurtarma adımı.** Süreç yeniden başladığında hizmete hazır olmak için kaç kayıt uygulanır.
Süre değil adım sayılır; adım ortamdan bağımsızdır.

**Kayıp penceresi.** Çökme anında kaç yazma yiter.

**Sürekli maliyet.** Depo normal çalışırken kalıcılığın diske yazdığı bayt, yaptığı
eşzamanlama ve bütün girişleri tarayan tam geçiş sayısı.

#### Dört Düzen, Tek Çökme

**KB5.** Çökme, 55.000. yazmadan hemen sonra olur. Anlık görüntü 20.000 yazmada bir alınır,
yani son tamamlanmış görüntü 40.000. yazmaya aittir. Günlük saniyede bir eşzamanlanır ve
koşum boyunca bir kez yeniden yazılır. Her tamamlanan görüntü bir eşzamanlama yapar.

Koşum dört düzeni de gerçekten kurar: dosyalar diske yazılır, kurtarma o dosyalardan okunur ve
kurulan durum, 55.000. yazmadaki doğru durumla giriş giriş karşılaştırılır.

```js
// secim.mjs — dört kalıcılık düzeninin kurtarması, kayıp penceresi ve sürekli maliyeti
import { writeFileSync, readFileSync, statSync } from 'node:fs';

function uretec(t0) {
  let a = t0 >>> 0;
  return () => { a = (a + 0x6d2b79f5) >>> 0; let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };
}
const r = uretec(20260731), YUK = [];
for (let i = 0; i < 60000; i++) {
  const u = Math.floor(r() * 20000);
  YUK.push(['odunc:u' + String(u).padStart(5, '0'),
    `sube=${1 + (u % 8)};gun=${[7, 14, 28][i % 3]};yenileme=${i % 4}`]);
}
const durumu = (n) => { const m = new Map();
  for (let i = 0; i < n; i++) m.set(YUK[i][0], YUK[i][1]); return m; };
const yaz = (m) => { let s = '';
  for (const [a, d] of m) s += a + '\t' + d + '\n'; return s; };

const GORUNTU = 20000, COKME = 55000;            // görüntü aralığı ve çökme anı
writeFileSync('g40.txt', yaz(durumu(40000)));    // son tamamlanmış görüntü
writeFileSync('tam.log', YUK.slice(0, COKME).map(([a, d]) => `Y\t${a}\t${d}\n`).join(''));
writeFileSync('kuyruk.log', YUK.slice(40000, COKME).map(([a, d]) => `Y\t${a}\t${d}\n`).join(''));
const dogru = durumu(COKME);

function kurtar(dosyalar) {                      // dosyaları sırayla oynatıp durumu kurar
  const m = new Map();
  let adim = 0, bayt = 0;
  for (const f of dosyalar) {
    bayt += statSync(f).size;
    for (const s of readFileSync(f, 'utf8').split('\n')) {
      if (s === '') continue;
      const p = s.split('\t');
      m.set(p[p.length - 2], p[p.length - 1]);
      adim++;
    }
  }
  let fark = 0;
  for (const [a, d] of dogru) if (m.get(a) !== d) fark++;
  return { bayt, adim, giris: m.size, fark };
}

const duzen = [                                  // ad, kaynak, görüntü, günlük, yeniden yazım, kayıp
  ['kalıcılık kapalı', [], 0, 0, 0, 60000],
  ['yalnız görüntü', ['g40.txt'], 1, 0, 0, GORUNTU],
  ['yalnız günlük', ['tam.log'], 0, 1, 1, 3000],
  ['görüntü + günlük', ['g40.txt', 'kuyruk.log'], 1, 1, 0, 3000],
];
console.log('çökme anı: 55000. yazma | görüntü aralığı: 20000 yazma | eşzamanlama: saniyede bir');
console.log('düzen              okunan bayt  kurtarma adımı  kurulan giriş  eksik giriş');
for (const [ad, dosyalar] of duzen) {
  const k = kurtar(dosyalar);
  console.log(ad.padEnd(18), String(k.bayt).padStart(11), String(k.adim).padStart(15),
    String(k.giris).padStart(14), String(k.fark).padStart(12));
}

let goruntuBayt = 0;                             // 60000 yazmalık koşumun sürekli maliyeti
for (let n = GORUNTU; n <= 60000; n += GORUNTU) goruntuBayt += Buffer.byteLength(yaz(durumu(n)));
const gunlukBayt = Buffer.byteLength(YUK.map(([a, d]) => `Y\t${a}\t${d}\n`).join(''));
const yenidenYazim = Buffer.byteLength(yaz(durumu(60000)));
console.log('\n60000 yazmalık koşumun sürekli maliyeti');
console.log('düzen              diske yazılan  eşzamanlama  tam tarama  en kötü kayıp');
for (const [ad, , goruntu, gunluk, yeniden, kayip] of duzen) {
  const bayt = goruntu * goruntuBayt + gunluk * gunlukBayt + yeniden * yenidenYazim;
  console.log(ad.padEnd(18), String(bayt).padStart(13),
    String(goruntu * 3 + gunluk * 20).padStart(12),
    String(goruntu * 3 + yeniden).padStart(11), String(kayip).padStart(14));
}

console.log('\nbirlikte kullanımda görüntü aralığının kurtarmaya etkisi (aynı çökme anı)');
console.log('görüntü aralığı  görüntü adımı  günlük adımı  toplam adım  görüntü diski');
for (const aralik of [5000, 20000, 60000]) {
  const son = Math.floor(COKME / aralik) * aralik;
  let disk = 0;
  for (let n = aralik; n <= 60000; n += aralik) disk += Buffer.byteLength(yaz(durumu(n)));
  const g = son ? durumu(son).size : 0;
  console.log(String(aralik).padStart(15), String(g).padStart(14),
    String(COKME - son).padStart(13), String(g + COKME - son).padStart(12),
    String(disk).padStart(14));
}
```

```
çökme anı: 55000. yazma | görüntü aralığı: 20000 yazma | eşzamanlama: saniyede bir
düzen              okunan bayt  kurtarma adımı  kurulan giriş  eksik giriş
kalıcılık kapalı             0               0              0        18763
yalnız görüntü          652842           17330          17330         9816
yalnız günlük          2181666           55000          18763            0
görüntü + günlük       1247842           32330          18763            0

60000 yazmalık koşumun sürekli maliyeti
düzen              diske yazılan  eşzamanlama  tam tarama  en kötü kayıp
kalıcılık kapalı               0            0           0          60000
yalnız görüntü           1849117            3           3          20000
yalnız günlük            3097260           20           1           3000
görüntü + günlük         4229117           23           3           3000

birlikte kullanımda görüntü aralığının kurtarmaya etkisi (aynı çökme anı)
görüntü aralığı  görüntü adımı  günlük adımı  toplam adım  görüntü diski
           5000          18763             0        18763        6535929
          20000          17330         15000        32330        1849117
          60000              0         55000        55000         717260
```

#### Kurtarmanın Okunması

İlk tablonun en anlamlı sütunu sonuncusudur. Doğru durum 18.763 giriştir; **eksik giriş**,
kurtarma bittiğinde bu girişlerden kaçının ya hiç bulunmadığını ya da yanlış değer taşıdığını
sayar.

Kalıcılığın kapalı olduğu düzen sıfır adımda kurtarır ve 18.763 girişin hepsini eksik kurar.
Sıfır kurtarma adımı bir başarı değildir; kurtarılacak bir şey olmadığının başka türlü
yazılışıdır.

Asıl uyarı ikinci satırdadır. Yalnız görüntüyle kurtarma 17.330 giriş kurar — bu, doğru
sayının yüzde 92'sidir ve depo yüzeyden bakıldığında dolu görünür. Buna karşın girişlerin
9.816'sı, yani yarısından fazlası ya yoktur ya da eski değerdedir. Kütüphanenin bakış
açısından bu, yarısı geçen haftaya ait bir ödünç tablosuyla açılmak demektir; ödünç sayıları
tutmaz, iade tarihleri geçmişi gösterir ve hatanın hangi kayıtta olduğu dışarıdan
anlaşılmaz. Eksik durumla açılmak, hiç açılmamaktan daha zor fark edilir.

Yalnız günlük ve birlikte kullanım aynı sonucu verir: sıfır eksik giriş. Aralarındaki fark
maliyettedir. Günlük tek başına 55.000 adım uygular ve 2.181.666 bayt okur; birlikte kullanım
aynı durumu 32.330 adımda ve 1.247.842 bayt okuyarak kurar. Kurtarma işi yüzde 41 azalır,
çünkü ilk 40.000 yazmanın hepsini tek tek oynatmak yerine onların **sonucu** okunur.

#### Sürekli Maliyet

İkinci tablo aynı düzenlerin çalışma sırasındaki bedelini verir. Yalnız görüntü 1.849.117 bayt
yazar ve üç eşzamanlama yapar; en ucuz korumalı düzendir ve en kötü durumda 20.000 yazma
kaybeder. Yalnız günlük 3.097.260 bayt yazar, yirmi eşzamanlama yapar ve kaybı 3.000 yazmaya
indirir.

Birlikte kullanım en pahalı satırdır: 4.229.117 bayt, yirmi üç eşzamanlama. Bu sayının
günlüğün ve görüntünün toplamı olması beklenir, ama tam toplam değildir — birlikte kullanımda
günlüğün ayrıca yeniden yazılmasına gerek kalmaz, çünkü her tamamlanan görüntüden sonra günlük
kırpılabilir. Görüntü, günlüğün budayıcısı hâline gelir.

Tam tarama sütunu sessiz bir maliyeti sayar. Önceki derste ölçülen tarama bedeli — duraklatma
ya da kopyalama — her tam geçişte yeniden ödenir. Yalnız günlük düzeni bu geçişi koşum boyunca
bir kez yapar, görüntü içeren düzenler üç kez.

#### Birlikte Kullanımda Görüntünün İşi

Üçüncü tablo, birlikte kullanımın asıl kararını gösterir. Kayıp penceresi artık görüntünün işi
değildir; onu günlük tutar ve üç satırda da 3.000 yazmadır. Değişen tek şey kurtarma adımıdır.

Görüntü aralığı 60.000 yazma olduğunda, yani çökmeden önce hiç görüntü tamamlanmadığında,
kurtarma 55.000 adıma çıkar ve görüntü diski 717.260 bayta iner. Aralık 20.000 olduğunda
kurtarma 32.330 adıma, aralık 5.000 olduğunda 18.763 adıma iner — bu, mümkün olan en düşük
değerdir, çünkü giriş sayısının altına inilemez. Bedeli görüntü diskidir: 717.260 bayttan
6.535.929 bayta, dokuz katına çıkar.

Bu, dersin kararıdır. **Anlık görüntü ile ekleme günlüğü birlikte kullanıldığında görüntünün
işi dayanıklılık değil kurtarma süresidir.** Aralık, "ne kadar veri kaybetmeyi göze
alıyorum" sorusuna göre değil, "yeniden başlamanın ne kadar sürmesine izin veriyorum"
sorusuna göre ayarlanır.

#### Karar

Kütüphanenin verisi tek bir düzen istemez; her veri kendi bedelini taşır.

| Veri | Düzen | Gerekçe |
|---|---|---|
| Katalog önbelleği | kalıcılık kapalı | Kaynağından yeniden üretilebilir; kurtarma adımı sıfır, kayıp önemsiz |
| Oturum kayıtları | yalnız görüntü | Yeniden oturum açmak ucuz; 20.000 yazmalık pencere kabul edilir, sürekli maliyet en düşük |
| Ödünç sayaçları, bekleme listesi | görüntü + günlük | Eksik durumla açılmak kabul edilemez; kurtarma 32.330 adım, pencere 3.000 yazma |
| Gecikme cezası kayıtları | görüntü + günlük, her yazmada eşzamanlama | Kaybedilen kayıt paraya karşılık gelir; pencere sıfıra iner, bedeli yazma başına eşzamanlamadır |

Tabloda eksik kalan tek düzen "yalnız günlük"tür ve bunun nedeni ölçümde görünür: aynı
dayanıklılığı verirken kurtarması birlikte kullanımın 1,7 katı adım sürer, üstelik günlüğün
büyümesini durdurmak için yine tam tarama gerekir. Günlüğü tek başına kullanmak, görüntünün
maliyetinden kaçmaz; onu yalnız erteler.

#### Özet

- Kurtarma üç ölçüyle değerlendirilir: uygulanan kayıt sayısı, kayıp penceresi ve çalışma
  sırasındaki sürekli maliyet.
- Yalnız görüntüyle kurtarma doğru girişlerin yüzde 92'sini kurdu ama 9.816'sını eksik ya da
  eski bıraktı; eksik durumla açılmak, hiç açılmamaktan daha zor fark edilir.
- Aynı doğru durumu günlük 55.000 adımda, görüntü ile günlük birlikte 32.330 adımda kurdu;
  okunan bayt 2.181.666'dan 1.247.842'ye indi.
- Birlikte kullanım en pahalı sürekli maliyeti taşır (4.229.117 bayt, 23 eşzamanlama), ama
  görüntü günlüğü kırptığı için ayrıca yeniden yazım gerektirmez.
- Birlikte kullanımda görüntü aralığı kayıp penceresini değil kurtarma adımını ayarlar:
  5.000'lik aralık kurtarmayı 18.763 adıma indirdi, görüntü diskini dokuz katına çıkardı.

#### Sonraki Adım

Kalıcılık kararı verildiğinde depo, çökmeden sonra kendine gelmeyi öğrenmiş olur. Çökmeden
**önce** gelen bir sınır ise henüz ele alınmadı: bellek biter. Bu konudaki bütün ölçümler
deponun büyümesine izin verdi; 19.040 giriş 1.593.100 bayt tuttu ve kimse durdurmadı. Sonraki
ders bellek sınırına ulaşıldığında hangi anahtarın atılacağı sorusunu ele alır. Aynı ödünç
erişim izinde dört politika — en az kullanılan, en az sık kullanılan, rastgele ve süreye göre
— isabet oranı ve tutulan bayt düzleminde karşılaştırılır; politikanın girişlere eklediği
üstverinin kendisi de sayıya girer.

### Bellek Sınırı ve Tahliye Politikaları

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/bellek-siniri-ve-tahliye-politikalari

Bellek sınırına ulaşıldığında hangi anahtarın atılacağını belirleyen dört politikanın aynı ödünç erişim izinde karşılaştırılması: politikanın girişlere eklediği üstverinin bütçeden aldığı pay, aynı bütçede tutulabilen giriş sayısı, üç bütçede ölçülen isabet oranları ve süreye göre tahliyenin belleği neden korumadığı.

Kalıcılık kararı, çökmeden sonra ne olacağını çözer. Çökmeden önce gelen bir sınır ise bu
konuda hiç ele alınmadı: bellek biter. Buraya kadarki bütün ölçümlerde depo serbestçe büyüdü;
19.040 ödünç girişi 1.593.100 bayt tuttu ve kimse durdurmadı. Gerçek bir kurulumda depoya bir
üst sınır verilir ve sınıra ulaşıldığında her yeni giriş bir eskisinin yerine geçer.

Bu dersin sorusu, hangi eskisinin gideceğidir. Tahliye politikalarının tanımları ve isabet
oranı aritmetiği önceki kurslarda kuruldu; burada tekrarlanmaz. Ölçülen şey, aynı ödünç
erişim izinde dört politikanın **aynı bayt bütçesiyle** ne satın aldığıdır.

#### Bütçe Giriş Sayısı Değil, Bayttır

Sınır giriş sayısıyla verilirse politikaların maliyeti görünmez olur. Her politika kendi
kararını verebilmek için girişe bir şeyler ekler ve o ek, sınırın içinden yenir.

**KB6.** Politika üstverileri: en az kullanılan, erişim sırasındaki yerini tutmak için iki
bağ (16 bayt); en az sık kullanılan, bir sayaç ve sıklık kovasındaki bağ (24 bayt); rastgele,
yalnız örnekleme dizisindeki yeri (8 bayt); süreye göre, süre sonu damgası ve sıra bilgisi
(16 bayt). Giriş gövdesinin maliyeti önceki derslerdeki gibidir: anahtar, değer ve 48 bayt.

**KB7.** Erişim izi kütüphanenin ödünç örüntüsüdür: dört dönem boyunca isteklerin yüzde 60'ı
o dönemin iki yüz kitaplık ders listesine, yüzde 25'i hiç değişmeyen bin kitaplık popüler
kesime, yüzde 15'i yirmi bin kitaplık katalogun geri kalanına gider. Dönem değiştiğinde ders
listesi tümüyle yenilenir.

**KB8.** Katalog girişleri üç bin erişimlik bir tazelik penceresiyle konur; bu pencere yalnız
süreye göre politikayı ilgilendirir.

```js
// politika.mjs — aynı bayt bütçesinde dört tahliye politikası
export const TEMEL = 48, OMUR = 3000;            // KB1: giriş üstverisi, KB8: tazelik penceresi

export function uretec(t0) {
  let a = t0 >>> 0;
  return () => { a = (a + 0x6d2b79f5) >>> 0; let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };
}

// Ödünç izi: dört dönem; her dönemde 200 kitaplık ders listesi değişir.
export function izUret(uzunluk, tohum, katalog = 20000) {
  const r = uretec(tohum), iz = [], donem = uzunluk / 4;
  for (let i = 0; i < uzunluk; i++) {
    const d = Math.floor(i / donem), p = r();
    let k;
    if (p < 0.60) k = d * 200 + Math.floor(r() * 200);        // dönemin ders listesi
    else if (p < 0.85) k = 1000 + Math.floor(r() * 1000);     // kalıcı popüler kitaplar
    else k = Math.floor(r() * katalog);                       // katalogun geri kalanı
    iz.push('kitap:' + String(k).padStart(5, '0'));
  }
  return iz;
}
export const deger = (a) => {
  const k = Number(a.slice(6));
  return `sube=${1 + (k % 8)};raf=${String.fromCharCode(65 + (k % 26))}${String(k % 100).padStart(2, '0')}`;
};

class Onbellek {
  constructor(butce, ustveri) {
    this.butce = butce; this.ust = ustveri; this.g = new Map();
    this.bayt = 0; this.isabet = 0; this.iska = 0; this.dusen = 0; this.saat = 0;
  }
  maliyet(a) { return Buffer.byteLength(a) + Buffer.byteLength(deger(a)) + TEMEL + this.ust; }
  eris(a) {
    this.saat++;
    if (this.g.has(a) && this.gecerli(a)) { this.isabet++; this.dokun(a); return; }
    if (this.g.has(a)) { this.dusen++; this.sil(a); }
    this.iska++;
    while (this.bayt + this.maliyet(a) > this.butce) this.sil(this.kurban());
    this.g.set(a, deger(a)); this.bayt += this.maliyet(a); this.eklendi(a);
  }
  sil(a) { this.bayt -= this.maliyet(a); this.g.delete(a); this.silindi(a); }
  gecerli() { return true; }
  dokun() {} eklendi() {} silindi() {}
  get ustveriBayt() { return this.g.size * this.ust; }
}

export class AzKullanilan extends Onbellek {      // en az kullanılan: erişim sırası bağı, 16 bayt
  constructor(b) { super(b, 16); }
  dokun(a) { const d = this.g.get(a); this.g.delete(a); this.g.set(a, d); }
  kurban() { return this.g.keys().next().value; }
}

export class AzSik extends Onbellek {             // en az sık kullanılan: sayaç + kova bağı, 24 bayt
  constructor(b) { super(b, 24); this.sayac = new Map(); this.kova = new Map(); this.enAz = 1; }
  #tasi(a, eski, yeni) {
    if (eski) { const s = this.kova.get(eski); s.delete(a); if (s.size === 0 && this.enAz === eski) this.enAz = yeni; }
    if (yeni) { if (!this.kova.has(yeni)) this.kova.set(yeni, new Set()); this.kova.get(yeni).add(a); }
  }
  dokun(a) { const c = this.sayac.get(a); this.sayac.set(a, c + 1); this.#tasi(a, c, c + 1); }
  eklendi(a) { this.sayac.set(a, 1); this.#tasi(a, 0, 1); this.enAz = 1; }
  silindi(a) { this.#tasi(a, this.sayac.get(a), 0); this.sayac.delete(a); }
  kurban() {
    while (!this.kova.get(this.enAz) || this.kova.get(this.enAz).size === 0) this.enAz++;
    return this.kova.get(this.enAz).values().next().value;
  }
}

export class Rastgele extends Onbellek {          // rastgele: yalnız anahtar dizisi, 8 bayt
  constructor(b, tohum) { super(b, 8); this.dizi = []; this.yer = new Map(); this.r = uretec(tohum); }
  eklendi(a) { this.yer.set(a, this.dizi.length); this.dizi.push(a); }
  silindi(a) {
    const i = this.yer.get(a), son = this.dizi.pop();
    if (i < this.dizi.length) { this.dizi[i] = son; this.yer.set(son, i); }
    this.yer.delete(a);
  }
  kurban() { return this.dizi[Math.floor(this.r() * this.dizi.length)]; }
}

export class SureyeGore extends Onbellek {        // süreye göre: süre sonu damgası + sıra, 16 bayt
  constructor(b) { super(b, 16); this.sonu = new Map(); }
  gecerli(a) { return this.sonu.get(a) > this.saat; }
  eklendi(a) { this.sonu.set(a, this.saat + OMUR); }
  silindi(a) { this.sonu.delete(a); }
  kurban() { return this.g.keys().next().value; }   // sabit ömürde en yakın süre sonu = en eski giriş
  get dolmus() {                                    // süresi dolmuş ama hâlâ tutulan girişler
    let sayi = 0, bayt = 0;
    for (const a of this.g.keys()) if (this.sonu.get(a) <= this.saat) { sayi++; bayt += this.maliyet(a); }
    return { sayi, bayt };
  }
}
```

Süreye göre politikanın kurban seçimi bir sadeleştirme taşır ve açıkça söylenmesi gerekir:
bütün girişler aynı ömürle konduğu için "süre sonu en yakın olan", "en eski eklenen" ile aynı
girişi işaret eder. Bu yüzden kurban, erişimde yeri değişmeyen ekleme sırasının başındadır.

#### Aynı İzde Dört Politika

```js
// olcum.mjs — dört politikanın aynı bütçedeki giriş sayısı ve isabet oranı
import { AzKullanilan, AzSik, Rastgele, SureyeGore, izUret, deger, TEMEL } from './politika.mjs';

const IZ = izUret(200000, 20260731);
const kur = { 'en az kullanılan': (b) => new AzKullanilan(b), 'en az sık kullanılan': (b) => new AzSik(b),
  'rastgele': (b) => new Rastgele(b, 4242), 'süreye göre': (b) => new SureyeGore(b) };
const BUTCE = [200000, 400000, 800000];
const ornek = 'kitap:01234';
console.log('iz: 200000 erişim, 20000 kitap, dört dönem, tohum 20260731');
console.log('giriş gövdesi:', Buffer.byteLength(ornek) + Buffer.byteLength(deger(ornek)) + TEMEL, 'bayt');
console.log('politika              üstveri  giriş/bayt  400000 baytta giriş  üstveri payı');
for (const [ad, f] of Object.entries(kur)) {
  const o = f(400000);
  for (const a of IZ) o.eris(a);
  console.log(ad.padEnd(21), String(o.ust).padStart(7), String(o.maliyet(ornek)).padStart(11),
    String(o.g.size).padStart(20),
    ('%' + ((o.ustveriBayt / o.bayt) * 100).toFixed(1)).padStart(13));
}

console.log('\nisabet oranı (aynı iz, üç bütçe)');
console.log('politika              200000 bayt  400000 bayt  800000 bayt  düşen giriş');
for (const [ad, f] of Object.entries(kur)) {
  const satir = [], dusen = [];
  for (const b of BUTCE) {
    const o = f(b);
    for (const a of IZ) o.eris(a);
    satir.push(('%' + ((o.isabet / IZ.length) * 100).toFixed(1)).padStart(12));
    dusen.push(o.dusen);
  }
  console.log(ad.padEnd(21), satir.join(' '), String(dusen[1]).padStart(12));
}

console.log('\nsüreye göre: bütçe artınca ne değişti');
console.log('bütçe    isabet  tutulan giriş  süresi dolmuş  ölü bayt');
for (const b of BUTCE) {
  const o = new SureyeGore(b);
  for (const a of IZ) o.eris(a);
  const d = o.dolmus;
  console.log(String(b).padStart(6), String(o.isabet).padStart(9), String(o.g.size).padStart(14),
    String(d.sayi).padStart(14), String(d.bayt).padStart(9));
}
```

```
iz: 200000 erişim, 20000 kitap, dört dönem, tohum 20260731
giriş gövdesi: 73 bayt
politika              üstveri  giriş/bayt  400000 baytta giriş  üstveri payı
en az kullanılan           16          89                 4494         %18.0
en az sık kullanılan       24          97                 4123         %24.7
rastgele                    8          81                 4938          %9.9
süreye göre                16          89                 4494         %18.0

isabet oranı (aynı iz, üç bütçe)
politika              200000 bayt  400000 bayt  800000 bayt  düşen giriş
en az kullanılan             %83.0        %87.4        %90.0            0
en az sık kullanılan         %78.6        %87.1        %89.6            0
rastgele                     %78.5        %85.0        %89.9            0
süreye göre                  %65.5        %65.5        %65.5        43746

süreye göre: bütçe artınca ne değişti
bütçe    isabet  tutulan giriş  süresi dolmuş  ölü bayt
200000    130965           2247           1228    109292
400000    130965           4494           3475    309275
800000    130965           8988           7969    709241
```

#### Üstverinin Payı

İlk tablo, politika seçiminin bir bellek kararı olduğunu gösterir. Giriş gövdesi 73 bayttır;
politikanın eklediği 8 ile 24 bayt arasındaki fark, aynı 400.000 baytlık bütçede tutulabilen
giriş sayısını 4.123 ile 4.938 arasında oynatır. En az sık kullanılan politikanın sayaçları
ve kova bağları bütçenin yüzde 24,7'sini, yani dörtte birini alır; rastgele politikada bu pay
yüzde 9,9'dur. Aynı bütçeyle rastgele politika, en az sık kullanılandan yüzde 20 daha fazla
kitap tutar.

Bu, politika karşılaştırmasını giriş sayısı üzerinden yapmanın neden yanıltıcı olduğunu
gösterir: sabit giriş sayısıyla ölçülen bir karşılaştırma, ucuz üstverili politikayı
kazandığı yerde cezalandırmadan bırakır.

#### İsabet Oranının Okunması

En az kullanılan politika üç bütçede de öndedir. Beklenen sonuç budur, çünkü izin ana kaynağı
dönemin ders listesidir ve o listeye erişim yakın geçmişte yoğunlaşır.

En az sık kullanılan politika dar bütçede belirgin biçimde geride kalır: yüzde 78,6 karşısında
yüzde 83,0. Nedeni izin yapısındadır. Dönem değiştiğinde eski ders listesinin girişleri
yüksek sayaçlarla oturmayı sürdürür; yeni listenin kitapları ise sayaç bir ile girer ve bir
sonraki tahliyede ilk sırada yer alır. Politika, geçmişte doğru olanı bugün de doğru sayar.
Bütçe 800.000 bayta çıktığında iki politikanın farkı 0,4 puana iner, çünkü bütçe eski listeyi
de yeni listeyi de aynı anda taşıyabilecek kadar geniştir.

Rastgele politikanın sonucu daha ilginçtir. 400.000 baytta en az kullanılanın 2,4 puan
gerisindedir; 800.000 baytta fark 0,1 puana düşer. Rastgele seçim, atacağı girişi hiç
düşünmeden seçer, ama iki avantajı vardır: üstverisi en ucuzdur, dolayısıyla aynı bütçede en
çok girişi tutar ve bütçe çalışma kümesini rahatça aldığında hangi girişin atıldığı
sonucu değiştirmez. Dar bütçede politika 4,5 puan fark eder; geniş bütçede fark ölçüm
gürültüsüne yaklaşır.

Karar bundan çıkar: politika tartışması yalnız bütçe çalışma kümesinden **küçükken**
anlamlıdır. Bütçe rahatsa, tahliye politikasını değiştirmek için harcanan emek, aynı emeğin
bellek üstverisini azaltmaya harcanmasından daha az getirir.

#### Süreye Göre Tahliye Belleği Korumaz

Son tablo, dört politikadan birinin aslında farklı bir işi yaptığını gösterir. Süreye göre
politika üç bütçede de aynı isabet oranını verir: yüzde 65,5. Bu bir yuvarlama değildir —
isabet sayısı üç koşumda da tam olarak 130.965'tir. Bütçe dört katına çıkarıldığında isabet
hiç değişmemiştir.

Değişen tek şey ölü ağırlıktır. Bütçe 200.000 baytken depo 2.247 giriş tutar ve bunların
1.228'i süresi dolmuş girişlerdir; 800.000 baytta 8.988 girişin 7.969'u süresi dolmuştur ve
709.241 bayt kaplar. Yani bütçenin dörtte üçünden fazlası, hiçbir isabet üretmeyecek
girişlere gider.

Bunun nedeni politikanın kendi mantığıdır. Süresi dolmuş bir giriş, bellekte dursa da
erişildiğinde ıska sayılır ve düşer: dört yüz bin baytlık koşumda 43.746 giriş bu yolla
düştü. Tahliye ise en eski girişi atar ve sabit ömürde en eski giriş zaten süresi dolmuş
olandır. Sonuç olarak tahliye hiçbir zaman yaşayan bir girişi atmaz; fazladan bellek yalnız
daha fazla ölü girişin bekletilmesine yarar.

Süre sonu bir tazelik aracıdır, bellek aracı değildir. Kütüphanenin katalog girişlerinin üç
bin erişimde bir yenilenmesi bir doğruluk kararıdır ve yerindedir; ama bellek sınırını
karşılamak için ayrıca bir tahliye politikası gerekir. İkisi birbirinin yerine geçmez.

#### Özet

- Bellek sınırı giriş sayısıyla değil baytla verilir, çünkü her politikanın girişe eklediği
  üstveri bütçenin içinden yenir: pay yüzde 9,9 ile yüzde 24,7 arasında değişti.
- Aynı 400.000 baytta rastgele politika 4.938, en az sık kullanılan 4.123 giriş tuttu; ucuz
  üstveri, zayıf kararı kısmen telafi eder.
- En az kullanılan politika üç bütçede de öndedir; en az sık kullanılan dar bütçede 4,4 puan
  geride kaldı, çünkü dönem değişince eski ders listesinin yüksek sayaçları yerini korudu.
- Bütçe genişledikçe politikalar arasındaki fark kapanır: 800.000 baytta en az kullanılan ile
  rastgele arasında 0,1 puan kaldı.
- Süreye göre tahliye üç bütçede de aynı 130.965 isabeti verdi; fazladan bellek yalnız ölü
  girişleri büyüttü (109.292 bayttan 709.241 bayta). Süre sonu tazeliği korur, belleği değil.

#### Sonraki Adım

Son ölçüm bir soruyu açıkta bıraktı: süresi dolmuş 7.969 giriş, kimse onlara dokunmadığı için
bellekte durdu. Bir girişin süresi dolduğu anda silinmesini sağlayan bir mekanizma yoksa,
tazelik penceresi yalnız okuma sonucunu düzeltir, tutulan baytı düzeltmez. Sonraki ders bu
boşluğu ele alır: girişi yalnız erişildiğinde temizleyen tembel yöntem ile arka planda
örnekleyerek tarayan etkin yöntem. Ölçülecek olan, süresi dolmuş girişlerin bellekte ne kadar
yer tuttuğu, tembel temizlemede bu yerin ne kadar geç boşaldığı ve etkin temizlemenin
örnekleme turu başına neye mal olduğudur.

### Süre Sonu Yönetimi

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/sure-sonu-yonetimi

Süresi dolmuş anahtarların bellekten ne zaman düştüğü: yalnız erişimde temizleyen tembel yöntemin bıraktığı ölü bayt ve gecikme, arka planda örnekleyerek tarayan etkin yöntemin tur başına maliyeti ve üç örnekleme boyunda ölü bellek ile örnekleme işi arasındaki azalan getiri.

Önceki dersin son ölçümü bir boşluk bıraktı. Süre sonu damgası taşıyan bir depoda, sekiz
yüz bin baytlık bütçenin sonunda tutulan 8.988 girişin 7.969'u süresi dolmuş girişlerdi ve
bellekte duruyorlardı. Süresi dolmuş bir girişe erişildiğinde doğru davranış üretiliyordu —
giriş yok sayılıyor ve düşürülüyordu — ama kimse erişmediği sürece giriş yerini koruyordu.
Tazelik penceresi okuma sonucunu düzeltti, tutulan baytı düzeltmedi.

Bu ders o boşluğu ele alır. İki yol vardır ve ikisi de aynı depoda birlikte çalışabilir.
**Tembel temizleme**, girişi yalnız ona erişildiğinde kontrol eder ve süresi dolmuşsa siler;
maliyeti sıfıra yakındır, çünkü zaten yapılacak olan aramanın üstüne bir karşılaştırma
ekler. **Etkin temizleme**, arka planda anahtar alanından örnek alır, süresi dolmuş olanları
siler ve örneklemede çok dolmuş giriş çıkarsa turu yineler; maliyeti, hiçbir istemcinin
istemediği bir iştir.

#### Düzenek

**KB9.** Kütüphanenin bir saatlik trafiği: saniyede 20 yeni anahtar konur ve 60 erişim
yapılır. Anahtarların yüzde 30'u 60 saniyelik ödünç kilidi, yüzde 50'si 300 saniyelik katalog
girişi, yüzde 20'si 1.800 saniyelik oturum kaydıdır. Erişimlerin yüzde 70'i son iki bin
anahtara, yüzde 30'u bütün anahtar alanına gider. Değer gövdesi 24 bayttır; giriş üstverisi
48, süre sonu damgası 16 bayttır.

**KB10.** Etkin temizleme her saniye bir tur başlatır. Turda belirli sayıda anahtar
örneklenir; süresi dolmuş olanlar silinir. Örneklenenlerin dörtte birinden fazlası dolmuşsa
tur yinelenir, en çok on altı kez.

```js
// sure.mjs — süre sonu damgalı depo, tembel ve etkin temizleme
export const TEMEL = 48, DAMGA = 16;             // KB1: giriş üstverisi, KB6: süre sonu damgası

export function uretec(t0) {
  let a = t0 >>> 0;
  return () => { a = (a + 0x6d2b79f5) >>> 0; let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };
}

export class Depo {
  constructor(tohum) {
    this.g = new Map(); this.sonu = new Map(); this.dizi = []; this.yer = new Map();
    this.bayt = 0; this.saat = 0; this.r = uretec(tohum);
    this.tembelSilinen = 0; this.etkinSilinen = 0; this.ornekleneni = 0; this.tur = 0;
    this.gecikmeToplam = 0; this.gecikmeEnKotu = 0; this.tepeOlu = 0;
  }
  maliyet(a) { return Buffer.byteLength(a) + 24 + TEMEL + DAMGA; }
  koy(a, omur) {
    if (!this.g.has(a)) {
      this.bayt += this.maliyet(a);
      this.yer.set(a, this.dizi.length); this.dizi.push(a);
    }
    this.g.set(a, 1); this.sonu.set(a, this.saat + omur);
  }
  sil(a) {
    this.bayt -= this.maliyet(a); this.g.delete(a); this.sonu.delete(a);
    const i = this.yer.get(a), son = this.dizi.pop();
    if (i < this.dizi.length) { this.dizi[i] = son; this.yer.set(son, i); }
    this.yer.delete(a);
  }
  eris(a) {                                      // tembel temizleme: dolmuş giriş erişimde düşer
    if (!this.g.has(a)) return 'yok';
    if (this.sonu.get(a) > this.saat) return 'isabet';
    const gecikme = this.saat - this.sonu.get(a);
    this.gecikmeToplam += gecikme;
    if (gecikme > this.gecikmeEnKotu) this.gecikmeEnKotu = gecikme;
    this.tembelSilinen++; this.sil(a);
    return 'dolmus';
  }
  etkin(ornek, esik = 0.25, enCokTur = 16) {      // etkin temizleme: örnekleyip silen turlar
    for (let t = 0; t < enCokTur; t++) {
      let silinen = 0, bakilan = 0;
      for (let i = 0; i < ornek && this.dizi.length > 0; i++) {
        const a = this.dizi[Math.floor(this.r() * this.dizi.length)];
        bakilan++;
        if (this.sonu.get(a) <= this.saat) { this.sil(a); silinen++; }
      }
      this.ornekleneni += bakilan; this.etkinSilinen += silinen; this.tur++;
      if (bakilan === 0 || silinen / bakilan < esik) return;
    }
  }
  olu() {
    let sayi = 0, bayt = 0;
    for (const a of this.g.keys()) if (this.sonu.get(a) <= this.saat) { sayi++; bayt += this.maliyet(a); }
    return { sayi, bayt };
  }
}

// Bir saatlik kütüphane trafiği: saniyede 20 yeni anahtar, 60 erişim.
export function kos(ornek, tohum = 20260731, sure = 3600) {
  const d = new Depo(tohum + 7), r = uretec(tohum);
  const tur = [['kilit', 60], ['katalog', 300], ['oturum', 1800]];
  const anahtarlar = [];
  for (let t = 1; t <= sure; t++) {
    d.saat = t;
    for (let i = 0; i < 20; i++) {
      const p = r(), [ad, omur] = p < 0.30 ? tur[0] : p < 0.80 ? tur[1] : tur[2];
      const a = ad + ':' + String(anahtarlar.length).padStart(6, '0');
      anahtarlar.push(a); d.koy(a, omur);
    }
    for (let i = 0; i < 60; i++) {                // %70 son 2000 anahtardan, %30 tüm alandan
      const n = anahtarlar.length;
      const j = r() < 0.70 ? n - 1 - Math.floor(r() * Math.min(2000, n))
        : Math.floor(r() * n);
      d.eris(anahtarlar[j]);
    }
    if (ornek > 0) d.etkin(ornek);
    if (t % 60 === 0) {                           // tepe ölçümü dakikada bir alınır
      const o = d.olu();
      if (o.bayt > d.tepeOlu) d.tepeOlu = o.bayt;
    }
  }
  return d;
}
```

#### Ölçüm

```js
// olcum.mjs — tembel temizleme tek başına ve üç örnekleme boyuyla etkin temizleme
import { kos } from './sure.mjs';

console.log('bir saatlik trafik: 72000 anahtar konur, 216000 erişim yapılır, tohum 20260731');
console.log('yöntem            tutulan giriş  ölü giriş  ölü bayt  ölü pay  tepe ölü bayt');
const kosumlar = [['yalnız tembel', 0], ['etkin, 5 örnek', 5], ['etkin, 20 örnek', 20],
  ['etkin, 100 örnek', 100]];
const sonuc = kosumlar.map(([ad, n]) => [ad, n, kos(n)]);
for (const [ad, , d] of sonuc) {
  const o = d.olu();
  console.log(ad.padEnd(17), String(d.g.size).padStart(13), String(o.sayi).padStart(10),
    String(o.bayt).padStart(9), ('%' + ((o.bayt / d.bayt) * 100).toFixed(1)).padStart(8),
    String(d.tepeOlu).padStart(14));
}

console.log('\ntemizlemenin maliyeti ve gecikmesi');
console.log('yöntem            tembel silinen  ortalama gecikme  en kötü  etkin silinen  örneklenen');
for (const [ad, , d] of sonuc) {
  const ort = d.tembelSilinen ? (d.gecikmeToplam / d.tembelSilinen).toFixed(1) : '0.0';
  console.log(ad.padEnd(17), String(d.tembelSilinen).padStart(14), (ort + ' sn').padStart(17),
    (d.gecikmeEnKotu + ' sn').padStart(8), String(d.etkinSilinen).padStart(14),
    String(d.ornekleneni).padStart(11));
}

const t = sonuc[0][2], y = sonuc[2][2];
console.log('\nerişim başına ek iş: tembel 0 (erişilen girişle sınırlı) |',
  'etkin, 20 örnek:', (y.ornekleneni / 216000).toFixed(3), 'giriş,',
  y.tur, 'tur /', 3600, 'saniye');
console.log('yalnız tembelde bellek:', t.bayt, 'bayt | etkin, 20 örnekte:', y.bayt, 'bayt |',
  'fark', t.bayt - y.bayt, 'bayt (%' + (((t.bayt - y.bayt) / t.bayt) * 100).toFixed(1) + ')');
```

```
bir saatlik trafik: 72000 anahtar konur, 216000 erişim yapılır, tohum 20260731
yöntem            tutulan giriş  ölü giriş  ölü bayt  ölü pay  tepe ölü bayt
yalnız tembel             39714      29205   2963642    %73.6        2963642
etkin, 5 örnek            20891      10382   1053264    %49.7        1112990
etkin, 20 örnek           14460       3951    400775    %27.4         414019
etkin, 100 örnek          12539       2030    205831    %16.2         212789

temizlemenin maliyeti ve gecikmesi
yöntem            tembel silinen  ortalama gecikme  en kötü  etkin silinen  örneklenen
yalnız tembel              32286          421.4 sn  3500 sn              0           0
etkin, 5 örnek             23572          206.5 sn  3009 sn          27537       62375
etkin, 20 örnek            16649           68.1 sn  1563 sn          40891      167200
etkin, 100 örnek           12772           30.7 sn   781 sn          46689      362500

erişim başına ek iş: tembel 0 (erişilen girişle sınırlı) | etkin, 20 örnek: 0.774 giriş, 8360 tur / 3600 saniye
yalnız tembelde bellek: 4027669 bayt | etkin, 20 örnekte: 1464802 bayt | fark 2562867 bayt (%63.6)
```

#### Tembel Temizlemenin Sınırı

Birinci satır, tembel temizlemenin çalışmadığı anlamına gelmez. Tam tersine çalışır: koşum
boyunca 32.286 giriş erişim sırasında yakalanıp silindi. Sorun, temizlemenin **yalnız
erişilen girişe** dokunmasıdır. Bir saatin sonunda depoda 39.714 giriş kalır ve bunların
29.205'i süresi dolmuş girişlerdir; kapladıkları 2.963.642 bayt, deponun tuttuğu belleğin
yüzde 73,6'sıdır. Bu girişler süreleri dolduktan sonra bir daha istenmedi, dolayısıyla hiçbir
zaman kontrol edilmediler.

İkinci tablo gecikmeyi verir. Tembel yolla silinen girişler, süreleri dolduktan ortalama
421,4 saniye sonra bellekten düştü; en kötü durumda 3.500 saniye, yani koşumun neredeyse
tamamı boyunca yer tuttu. Kütüphanenin ölçüsüyle: altmış saniyelik bir ödünç kilidi, işini
bitirdikten yaklaşık yedi dakika sonra hâlâ bellekte duruyor.

Bu, süre sonunun tek başına bir bellek aracı olmadığını doğrular. Tazelik penceresi
sözleşmesini tutar — dolmuş bir giriş asla okunmaz — ama sözleşmenin bellek tarafı boşta
kalır. Bir saatlik trafikte deponun dörtte üçü hiç kimsenin isteyemeyeceği veriye gider.

#### Etkin Temizlemenin Fiyatı

Etkin temizleme bu boşluğu kapatır ve bedelini hiçbir istemcinin istemediği bir işle öder.
Turda yirmi anahtar örnekleyen düzen, ölü payı yüzde 73,6'dan yüzde 27,4'e indirir ve deponun
toplam belleğini 4.027.669 bayttan 1.464.802 bayta, yani yüzde 63,6 aşağı çeker. Gecikmenin
ortalaması 421,4 saniyeden 68,1 saniyeye iner.

Maliyet, örneklenen giriş sayısıyla ölçülür: 167.200 giriş. Aynı saatte yapılan erişim sayısı
216.000 olduğuna göre, temizleme her erişime karşılık 0,774 giriş dokunuşu ekler. Turların
sayısı 8.360'tır — saniyede ortalama 2,3 tur. Eşik kuralı burada görünür: dolmuş giriş
yoğunken tur yinelenir ve temizleme hızlanır, alan seyrekleştiğinde tek turla biter.
Örnekleme turu, deponun erişimlere ayırdığı zamandan alınır; bu yüzden tur başına örnek sayısı
ve tur sayısının üst sınırı birer yanıt süresi kararıdır.

Örnekleme boyunun etkisi doğrusal değildir. Beşten yirmiye çıkmak 104.825 ek örnekleme
karşılığında ölü belleği 1.053.264 bayttan 400.775 bayta indirdi: örnek başına 6,2 bayt.
Yirmiden yüze çıkmak 195.300 ek örnekleme karşılığında 400.775 bayttan 205.831 bayta indirdi:
örnek başına 1,0 bayt. Marjinal getiri altıda birine düşer. Nedeni doğrudandır: alan
temizlendikçe rastgele seçilen bir anahtarın dolmuş çıkma olasılığı azalır, örnekleme boş
dönmeye başlar.

Ölü payının hiçbir düzende sıfıra inmemesi de aynı nedendendir. Yüz örneklik turlarda bile
2.030 giriş dolmuş olarak kalır; rastgele örnekleme bir girişi eninde sonunda bulur, ama
"eninde sonunda" bir zaman aralığıdır ve bu aralık bellekte bayt olarak durur.

#### Seçim

İki yöntem birbirinin yerine geçmez; birlikte kullanılır ve iki farklı şeyi güvence altına
alırlar. Tembel temizleme **doğruluğu** güvence altına alır: süresi dolmuş bir giriş, etkin
temizleme onu henüz bulmamış olsa da okunamaz. Etkin temizleme **belleği** güvence altına
alır: hiç erişilmeyecek girişlerin yerini geri kazanır.

Örnekleme boyu ise bir bütçe ayarıdır. Kütüphanenin ödünç kilitleri gibi kısa ömürlü ve
yoğun üretilen anahtarlarda alan hızla dolduğu için büyük örnek karşılığını verir. Oturum
kayıtları gibi uzun ömürlü ve seyrek anahtarlarda aynı örnekleme çoğunlukla boş döner ve
harcanan iş yanıt süresine ödenmiş olur. Yirmi örneklik tur, bu koşumda ölü belleğin yüzde
86'sını erişim başına 0,774 giriş dokunuşuyla geri aldı; yüz örneklik tur kalan payın yarısını
daha aldı ve maliyeti iki katına çıkardı.

#### Özet

- Tembel temizleme yalnız erişilen girişe dokunur: koşum sonunda 39.714 girişin 29.205'i
  süresi dolmuş kaldı ve belleğin yüzde 73,6'sını tuttu.
- Tembel yolla düşen girişler süreleri dolduktan ortalama 421,4 saniye, en kötü durumda 3.500
  saniye sonra bellekten çıktı.
- Turda yirmi anahtar örnekleyen etkin temizleme ölü payı yüzde 27,4'e, toplam belleği
  4.027.669 bayttan 1.464.802 bayta indirdi; bedeli erişim başına 0,774 giriş dokunuşudur.
- Örnekleme boyunu artırmanın getirisi azalır: beşten yirmiye geçiş örnek başına 6,2 bayt,
  yirmiden yüze geçiş 1,0 bayt kazandırdı.
- Tembel temizleme doğruluğu, etkin temizleme belleği güvence altına alır; süre sonu tek
  başına ne birini ne diğerini karşılar.

#### Sonraki Adım

Bu konu deponun kendine bakma biçimlerini kurdu: durumunu diske yazmak, yazmalarını
kaydetmek, hangisini seçeceğine kurtarma ve kayıp penceresine bakarak karar vermek, bellek
sınırına ulaşınca hangi anahtarı atacağını bilmek ve süresi dolanı yerinde bırakmamak.
Ölçülen her sayı tek bir sürecin içinden okundu.

Bu kararların hepsinin ortak bir varsayımı var: düğüm çalışıyor. Anlık görüntü diske yazıldı
ama o disk o makinenin diski; kayıp penceresi hesaplandı ama süreç yeniden başlayabildiği
varsayıldı; tahliye politikası bellek sınırını korudu ama o bellek tek bir makinenin belleği.
Makinenin kendisi durduğunda — ya da erişilemez hâle geldiğinde — kurtarma dosyaları oradadır,
depoyu isteyen istemciler ise başka yerdedir. Sonraki konu bu soruyu açar: verinin ikinci bir
kopyası nasıl tutulur, o kopya ne kadar geride kalır ve devralma sırasında kabul edilmiş ama
henüz yayılmamış bir yazmaya ne olur.

## Küme ve Kullanım Kalıpları

Tek düğümün ötesine geçiş ve deponun günlük işleri: okuma ölçekleme ile yedeklilik için çoğaltma, gözcü süreçleriyle otomatik devralma, anahtar alanının bölümlenmesiyle kümeleme, izleme tabanlı çakışma denetimiyle iyimser kilitleme, hafif mesajlaşma için yayımla–abone ol, önbellek, oturum, hız sınırı ile sıralama tablosu senaryoları, yavaş komut analiziyle başarım tanısı ve kimlik doğrulama, ağ yalıtımı ile şifreleme.

### Çoğaltma

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/cogaltma

Bellek içi deponun tek düğümden çıkması: çoğaltmanın bir ayar değil deponun onaylama biçiminin zorunlu sonucu olması, kopya sayısı ile yayılım gecikmesinin aynı iş yükünde koşturulup düğüm başına düşen iş, tutulan veri, bayat okuma sayısı ve oranının ölçülmesi, bayatlık penceresinin gecikme çarpı yazma hızına indirgenmesi, ana düğümdeki çıkış tamponunun bellek olarak sayılması ve yetişemeyen kopyanın tamponu taşırıp tam eşitlemeye ve hizmet dışı turlara yol açmasının gösterilmesi.

Önceki konu kalıcılığı ve tahliyeyi tek bir düğümün kararları olarak kurdu: hangi anın diske
yazılacağı, bellek sınırı dolduğunda hangi girişin atılacağı, süresi dolmuş anahtarın ne zaman
temizleneceği. Bunların hepsi tek bir sürecin içinde verildi ve hepsi o sürecin ayakta olduğunu
varsaydı. Sürecin kendisi durduğunda ne olacağı hiç sorulmadı.

Bu konu deponun birden çok düğüme yayıldığı kurulumları ele alır. İlk soru en basit olanıdır:
aynı veri ikinci bir düğümde de dursun. Kütüphane katalog ve ödünç sistemi burada da örnek
alandır; ölçülen iş, katalog önbelleğinin okunması ve ödünç kayıtlarının yazılmasıdır.

#### Onaylama Biçiminin Zorunlu Sonucu

Çoğaltmanın mekaniği bu kursun konusu değil. Günlük akışı, çoğaltma gecikmesi ve kopya eklemenin
azalan kazancı Veri Katmanı Ölçekleme kursunda ölçüldü; kümenin bir üyeyi kaybettiğinde kendi
kararını nasıl verdiği İlişkisel Olmayan Veri Modelleri kursunda ölçüldü. Burada tekrarlanmaz.

Bu dersin sorusu bellek içi deponun kendi kısıtıdır. Depo bir yazmayı kabul ederken onu belleğine
yazar ve **hemen** onaylar; onayı kopyanın uygulamasına bağlamak, deponun var oluş nedenini iptal
etmek olur. Bu yüzden bellek içi depoda çoğaltma **eşzamansızdır** ve bu bir ayar değil, onaylama
biçiminin sonucudur. Bunun üç ölçülebilir bedeli vardır ve üçü de bellekten ödenir:

1. **Her kopya veri kümesinin tamamını tutar.** Kopya eklemek saklanan baytı böler değil,
   çarpar. Diskte bu bir kapasite kalemidir; bellekte doğrudan satın alma kalemidir.
2. **Ana düğüm her kopya için bir çıkış tamponu tutar.** Kopyaya henüz ulaşmamış yazmalar
   ana düğümün belleğinde bekler. Tampon da bellektir.
3. **Kopyanın geriliği bir zaman aralığı değil, bir yazma sayısıdır.** Kopyadan okunan her
   anahtar, o pencerede yazılmışsa **bayat** döner.

#### Düzenek

Aşağıdaki düzenek bir **modeldir**: gerçek depo, ağ ya da kopya kurulmaz. Tur soyut bir adımdır;
yayılım gecikmesi ve uygulama kapasitesi tur cinsinden parametrelerdir, ölçülmüş süre değildir.
Modelin tek gerçekçi varsayımı yukarıdaki eşzamansızlıktır.

**BK1 — yayılım gecikmesi varsayılan olarak 2 turdur ve kopya, ana düğümün yazma hızına
yetişir.** Gerekçe: sağlıklı bir kopya birikmiş iş taşımaz, yalnız sabit bir gerilikle gelir.
**BK2 — katalog girişi 240 bayt, çoğaltma akışındaki bir yazma kaydı 96 bayttır.** Gerekçe:
kayıt yalnız değişikliği taşır, girişin tamamını değil. **BK3 — erişimlerin yüzde 70'i
anahtarların yüzde 10'una gider.** Gerekçe: ödünç ve arama trafiği popüler kitaplarda toplanır.
BK1 ve BK2 doğrusal etkilidir: gecikme iki katına çıkarsa bayatlık penceresi de iki katına çıkar.

```js
// cogaltma/model.mjs — bellek ici deponun cogaltmasi SUREC ICI MODELDIR. Tur soyut bir adimdir;
// yayilim gecikmesi ve uygulama kapasitesi tur cinsinden parametrelerdir, olculmus sure degildir.
// Gercek depo, ag ya da kopya kurulmaz. Modelin tek gercekci varsayimi cogaltmanin ESZAMANSIZ
// olmasidir: ana dugum yazmayi bellegine alip hemen onaylar, kopyanin uygulamasini beklemez.
export const GIRIS_BAYT = 240;   // katalog girisi basina: anahtar + deger + ustveri (kendi hesabim)
export const KAYIT_BAYT = 96;    // cogaltma akisinda bir yazma kaydinin bayti

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

export function kosum({ kopya, gecikme, kapasite = null, tamponSiniri = Infinity, esitlemeTur = 5,
                        tur = 200, yazma = 40, okuma = 400, anahtar = 2000, tohum = 20260731 }) {
  const rnd = uretec(tohum);
  const sicak = Math.floor(anahtar * 0.10);                  // populer kitaplar: %10 anahtar
  const sec = () => (rnd() < 0.70 ? Math.floor(rnd() * sicak)  // erisimlerin %70'i onlara gider
                                  : sicak + Math.floor(rnd() * (anahtar - sicak)));
  const sira = new Int32Array(anahtar);                      // anahtarin son yazmasinin sira numarasi
  const uygulanan = Array(kopya).fill(0);                    // kopyanin uyguladigi yazma sayisi
  const disari = Array(kopya).fill(0);                       // esitleme yuzunden hizmet disi tur
  const zirve = Array(kopya).fill(0);                        // kopya basina en buyuk cikis tamponu
  let kabul = 0, bayat = 0, kopyaOkuma = 0, tamEsitleme = 0, ilkTasma = null, disariToplam = 0;

  for (let t = 1; t <= tur; t += 1) {
    for (let w = 0; w < yazma; w += 1) { kabul += 1; sira[sec()] = kabul; }
    for (let i = 0; i < kopya; i += 1) {
      const hedef = Math.max(0, (t - gecikme[i]) * yazma);   // gecikme kadar geriden gelir
      if (disari[i] > 0) {                                   // tam esitleme suruyor: okuma karsilamaz
        disari[i] -= 1; disariToplam += 1;
        if (disari[i] === 0) uygulanan[i] = hedef;           // esitleme kopyayi olagan gerilige getirir
        continue;
      }
      uygulanan[i] = Math.min(hedef, uygulanan[i] + (kapasite === null ? yazma : kapasite[i]));
      const tampon = (kabul - uygulanan[i]) * KAYIT_BAYT;    // ana dugumdeki cikis tamponu: BELLEKTIR
      if (tampon > zirve[i]) zirve[i] = tampon;
      if (tampon > tamponSiniri) {                           // tampon tasti: kopya dusurulur
        tamEsitleme += 1; disari[i] = esitlemeTur;
        if (ilkTasma === null) ilkTasma = t;
      }
    }
    const acik = [0, ...[...Array(kopya).keys()].map((i) => i + 1).filter((n) => disari[n - 1] === 0)];
    for (let r = 0; r < okuma; r += 1) {
      const n = acik[r % acik.length];                       // okumalar acik dugumlere sirayla dagilir
      const k = sec();
      if (n === 0) continue;                                 // ana dugum her zaman guncel
      kopyaOkuma += 1;
      if (sira[k] > uygulanan[n - 1]) bayat += 1;            // kopya bu anahtari henuz uygulamadi
    }
  }
  const dugum = kopya + 1, tampon = zirve.reduce((a, b) => a + b, 0);
  return { kabul, bayat, kopyaOkuma, tamEsitleme, ilkTasma, tampon, disariToplam,
           veri: dugum * anahtar * GIRIS_BAYT,               // her dugum veri kumesinin TAMAMINI tutar
           enYuklu: Math.round(okuma / dugum) + yazma };     // ana dugum: okuma payi + butun yazmalar
}
```

```js
// cogaltma/olc.mjs — ayni is yuku uc ayarda: kopya sayisi, yayilim gecikmesi, yavas kopya
import { kosum, GIRIS_BAYT, KAYIT_BAYT } from "./model.mjs";

const s = (x, n) => String(x).padStart(n);
const kib = (b, n = 7) => s((b / 1024).toFixed(1), n);
const yuzde = (r) => s(((r.bayat / r.kopyaOkuma) * 100).toFixed(2) + "%", 11);

console.log("200 tur; her turda 40 odunc/oturum yazmasi, 400 katalog okumasi. 2.000 anahtar,");
console.log("giris basina 240 bayt, cogaltma kaydi 96 bayt. Tohum 20260731. Gecikme 2 tur.\n");
console.log("kopya | tutulan veri | en yuklu dugum | kopyadan okuma |  bayat | bayat orani");
console.log("------|--------------|----------------|----------------|--------|------------");
for (let k = 0; k <= 4; k += 1) {
  const r = kosum({ kopya: k, gecikme: Array(k).fill(2) });
  console.log(`${s(k, 5)} | ${kib(r.veri, 8)} KiB | ${s(r.enYuklu, 7)} is/tur | ${s(r.kopyaOkuma, 14)} |` +
    ` ${s(r.bayat, 6)} | ${k === 0 ? s("yok", 11) : yuzde(r)}`);
}

console.log("\n2 kopya sabit; yayilim gecikmesi degisiyor:");
console.log("gecikme | bayatlik penceresi | kopyadan okuma |  bayat | bayat orani | cikis tamponu");
console.log("--------|--------------------|----------------|--------|-------------|--------------");
for (const g of [1, 2, 4, 8]) {
  const r = kosum({ kopya: 2, gecikme: [g, g] });
  console.log(`${s(g, 7)} | ${s(g * 40, 12)} yazma | ${s(r.kopyaOkuma, 14)} | ${s(r.bayat, 6)} |` +
    ` ${yuzde(r)} | ${kib(r.tampon)} KiB`);
}

console.log("\n2 kopya; ikincisinin uygulama kapasitesi dusuk, tampon siniri 64 KiB, esitleme 5 tur:");
console.log("kapasite | tampon buyume | ilk tasma | tam esitleme | hizmet disi | zirve tampon |  bayat");
console.log("---------|---------------|-----------|--------------|-------------|--------------|-------");
for (const c of [40, 36, 32, 24]) {
  const r = kosum({ kopya: 2, gecikme: [2, 2], kapasite: [40, c], tamponSiniri: 64 * 1024 });
  console.log(`${s(c, 8)} | ${s((40 - c) * KAYIT_BAYT, 7)} b/tur | ` +
    `${s(r.ilkTasma === null ? "yok" : r.ilkTasma, 9)} | ${s(r.tamEsitleme, 12)} | ` +
    `${s(r.disariToplam, 7)} tur | ${kib(r.tampon)} KiB | ${s(r.bayat, 6)}`);
}

const N = [0, 1, 2, 3, 4];
console.log("\nkosumdan bagimsiz nicelikler:");
console.log("  bayatlik penceresi = gecikme x yazma hizi (yazma)");
console.log("  tampon buyume hizi = (yazma hizi - uygulama kapasitesi) x " + KAYIT_BAYT + " bayt/tur");
console.log("  kopya sayisi   : " + N.map((n) => s(n, 8)).join(""));
console.log("  okuma/dugum    : " + N.map((n) => s((400 / (n + 1)).toFixed(1), 8)).join(""));
console.log("  toplam veri KiB: " + N.map((n) => s(((n + 1) * 2000 * GIRIS_BAYT / 1024).toFixed(1), 8)).join(""));
```

```
200 tur; her turda 40 odunc/oturum yazmasi, 400 katalog okumasi. 2.000 anahtar,
giris basina 240 bayt, cogaltma kaydi 96 bayt. Tohum 20260731. Gecikme 2 tur.

kopya | tutulan veri | en yuklu dugum | kopyadan okuma |  bayat | bayat orani
------|--------------|----------------|----------------|--------|------------
    0 |    468.8 KiB |     440 is/tur |              0 |      0 |         yok
    1 |    937.5 KiB |     240 is/tur |          40000 |   7132 |      17.83%
    2 |   1406.3 KiB |     173 is/tur |          53200 |   9494 |      17.85%
    3 |   1875.0 KiB |     140 is/tur |          60000 |  10740 |      17.90%
    4 |   2343.8 KiB |     120 is/tur |          64000 |  11420 |      17.84%

2 kopya sabit; yayilim gecikmesi degisiyor:
gecikme | bayatlik penceresi | kopyadan okuma |  bayat | bayat orani | cikis tamponu
--------|--------------------|----------------|--------|-------------|--------------
      1 |           40 yazma |          53200 |   4992 |       9.38% |     7.5 KiB
      2 |           80 yazma |          53200 |   9494 |      17.85% |    15.0 KiB
      4 |          160 yazma |          53200 |  16344 |      30.72% |    30.0 KiB
      8 |          320 yazma |          53200 |  25517 |      47.96% |    60.0 KiB

2 kopya; ikincisinin uygulama kapasitesi dusuk, tampon siniri 64 KiB, esitleme 5 tur:
kapasite | tampon buyume | ilk tasma | tam esitleme | hizmet disi | zirve tampon |  bayat
---------|---------------|-----------|--------------|-------------|--------------|-------
      40 |       0 b/tur |       yok |            0 |       0 tur |    15.0 KiB |   9494
      36 |     384 b/tur |       153 |            1 |       5 tur |    71.6 KiB |  16660
      32 |     768 b/tur |        78 |            2 |      10 tur |    72.0 KiB |  16769
      24 |    1536 b/tur |        40 |            4 |      20 tur |    72.0 KiB |  16480

kosumdan bagimsiz nicelikler:
  bayatlik penceresi = gecikme x yazma hizi (yazma)
  tampon buyume hizi = (yazma hizi - uygulama kapasitesi) x 96 bayt/tur
  kopya sayisi   :        0       1       2       3       4
  okuma/dugum    :    400.0   200.0   133.3   100.0    80.0
  toplam veri KiB:    468.8   937.5  1406.3  1875.0  2343.8
```

#### Kopya Eklemenin Bilançosu

**Satın alınan şey okumanın bölünmesidir, saklanan verinin bölünmesi değil.** İlk tablo iki
sütunu yan yana koyuyor: dört kopyada düğüm başına okuma 400'den 80'e iner, tutulan veri 468,8
KiB'den 2.343,8 KiB'ye çıkar. Bu bir dağıtma değil, çoğaltmadır — adın kendisi bunu söylüyor.
Diskte aynı çarpan bir kapasite kalemidir; bellekte doğrudan satın alınan bir kalemdir ve
kopya başına maliyeti sabittir: veri kümesinin tamamı, 468,8 KiB.

**Kopya eklemek veriyi bayatlatmıyor; bayat okumanın payını büyütüyor.** Bayat oranı bir
kopyada yüzde 17,83, dört kopyada yüzde 17,84 — değişmiyor. Değişen, kopyaya giden okuma
sayısıdır: 40.000'den 64.000'e. Bayat okuma sayısı da onunla birlikte 7.132'den 11.420'ye
çıkıyor. Oran gecikmeye bağlıdır, kopya sayısına değil; sayı ise ikisinin çarpımıdır.

**Bayatlık bir süre değil, bir yazma sayısıdır.** İkinci tablo bunu doğrudan gösteriyor:
pencere `gecikme × yazma hızı` ile 40, 80, 160 ve 320 yazmadır ve bayat oranı yüzde 9,38'den
yüzde 47,96'ya çıkar. Sekiz turluk gecikmede kopyadan yapılan okumaların neredeyse yarısı
güncel olmayan bir katalog kaydı döndürür. Bu, kopyanın bozuk olduğu anlamına gelmez; ödünç
sayacının o anda ana düğümdeki değerini değil, 320 yazma öncesindeki değerini döndürdüğü
anlamına gelir.

**Gecikmenin bir de bellek fiyatı vardır.** Aynı tablonun son sütunu çıkış tamponunu ölçüyor:
7,5 KiB'den 60 KiB'ye. Bu bayt, kopyalarda değil **ana düğümde** durur ve doğrudan gecikmeyle
orantılıdır. Kopyanın geride kalması yalnız okuyucunun sorunu değildir; geriliğin karşılığı
ana düğümün bellek bütçesinden çekilir.

#### Yetişemeyen Kopya

Üçüncü tablo, kopyanın sabit bir gerilikle gelmeyi bıraktığı durumu ölçüyor. Uygulama kapasitesi
yazma hızının altına düştüğü anda tampon her tur `(40 − kapasite) × 96` bayt büyür ve bu büyüme
durmaz: kapasite 36'da 384 bayt, 24'te 1.536 bayt. Tampon sınırına ne zaman çarpılacağı
bölmeden çıkar — 64 KiB sınırında ilk taşma sırasıyla 153., 78. ve 40. turdadır.

Taşmanın sonucu kopyanın düşürülmesi ve **tam eşitlemedir**: kopya artık akışı takip edemediği
için veri kümesinin tamamını yeniden yükler. Bunun bellek bütçesindeki karşılığı en kötü anda
ortaya çıkar — ana düğüm zaten tamponu taşırmış durumdayken bir de veri kümesinin tamamının
anlık görüntüsünü, yani 468,8 KiB'lik bir kalemi üretmek zorundadır. Tabloda tam eşitleme sayısı
kapasite düştükçe 1, 2 ve 4'e çıkıyor ve hizmet dışı tur 5'ten 20'ye.

Son sütun ilk bakışta ters görünen bir sayı taşıyor: yetişemeyen kopyada bayat okuma 9.494'ten
16.660'a çıkıyor ama kapasite daha da düştüğünde 16.769'da kalıyor, hatta 24'te 16.480'e
iniyor. Nedeni tablodaki komşu sütundur. Hizmet dışı turlarda o kopya hiç okuma karşılamaz;
okumalar ana düğüme ve sağlıklı kopyaya kayar ve oradan güncel yanıt döner. Bayat okumanın
azalması bir iyileşme değildir: kopya kaldırıldığı için düğüm başına okuma yeniden yükselmiştir.
Yavaş bir kopya, okuma kapasitesi olarak sayıldığı sürece ölçüyü iki kez bozar — bayat yanıt
verdiği sürece bir kez, verecek durumda olmadığı sürece bir kez daha.

#### Özet

- Bellek içi depoda çoğaltma bir ayar değil, yazmayı bellekten onaylamanın sonucudur; onayı
  kopyaya bağlamak deponun kendi gerekçesini iptal eder.
- Kopya eklemek okumayı böler ama saklanan veriyi çarpar: düğüm başına okuma 400'den 80'e
  inerken tutulan veri 468,8 KiB'den 2.343,8 KiB'ye çıktı ve kopya başına maliyet sabittir.
- Bayat okuma oranı kopya sayısıyla değişmedi (yüzde 17,83'e karşı yüzde 17,84); değişen,
  kopyaya giden okuma sayısı ve dolayısıyla bayat okuma sayısıdır (7.132'ye karşı 11.420).
- Bayatlık penceresi `gecikme × yazma hızı`dır: 40, 80, 160 ve 320 yazmalık pencerelerde bayat
  oranı yüzde 9,38'den yüzde 47,96'ya çıktı.
- Ana düğümdeki çıkış tamponu bir bellek kalemidir ve gecikmeyle doğru orantılıdır: 7,5 KiB'den
  60 KiB'ye; kopyanın geriliğinin faturası ana düğüme kesilir.
- Uygulama kapasitesi yazma hızının altına düştüğünde tampon `(40 − kapasite) × 96` bayt/tur
  büyür, 64 KiB sınırını 153., 78. ve 40. turda aşar ve her aşımda tam eşitleme ile hizmet dışı
  tur doğurur.

#### Sonraki Adım

Bu ders çoğaltmayı ana düğümün ayakta olduğu varsayımıyla ölçtü: kopya geride kalabilir,
tamponu taşırabilir, hatta yeniden eşitlenebilir — yazmaları kabul eden düğüm hep aynıdır.
Ana düğüm durduğunda ise iki soru aynı anda açılır. Birincisi kimin karar vereceğidir: düğümün
gerçekten durduğuna hükmetmek, ona ulaşamayan bir gözlemcinin tek başına verebileceği bir karar
değildir. İkincisi ve bu kursa özgü olanı şudur: kopya, tanım gereği ana düğümün gerisindedir.
Yerine geçtiği anda elinde olmayan yazmalar, ödünç işlemi başarılı diye bildirilmiş yazmalardır.
Sonraki ders gözcü süreçlerinin çoğunluk kararını, devralma penceresinde kaybedilen kabul edilmiş
yazma sayısını ve algılama eşiğinin kısaltılmasının doğurduğu yanlış devralmayı sayar.

### Otomatik Devralma

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/otomatik-devralma

Bellek içi deponun erişilebilirliği: gözcü süreçlerinin çoğunluk kararının kendi görüşleri ayrıştığında sınanması, sekiz gözcü ayarında arıza kesintisi ile yanlış devralma sayısının ölçülmesi, algılama eşiğinin kısaltılmasının kesintiyi kısaltıp yanlış devralmayı ve bölünmüş yazmayı büyütmesinin sayılması, eşzamansız çoğaltmanın devralma başına kaybı gecikme çarpı yazma hızı olarak garanti etmesinin gösterilmesi ve sıcak yedeğin tuttuğu tam kopyanın kesinti ile kayıp karşısında ne satın aldığının hesaplanması.

Önceki ders çoğaltmayı ana düğümün ayakta olduğu varsayımıyla ölçtü: kopya geride kaldı, tamponu
taşırdı, yeniden eşitlendi, ama yazmayı kabul eden düğüm hep aynı kaldı. Bu ders o düğümün
durduğu anı ele alır ve iki soruyu birlikte sorar: durduğuna kim karar verir ve karar
verildiğinde elde ne kalır.

#### Kararı Kim Verir

Bir düğüme ulaşamamak, o düğümün durduğunu göstermez; ulaşamayan taraf da bozulmuş olabilir.
Bu yüzden karar tek bir gözlemciye bırakılmaz: **gözcü** süreçleri ana düğümü ayrı ayrı izler ve
devralma ancak gözcü **çoğunluğu** aynı yönde oy verdiğinde başlar. Çoğunluk `floor(g/2)+1`
oydur; lider seçimi ile kurtarma kurslarında kurulmuş bu kalıbın mekaniği burada tekrarlanmaz.

Bu dersin sorusu bellek içi deponun kendi kısıtıdır. Depo yazmayı bellekten onaylar, kopyanın
uygulamasını beklemez; yedek de tanım gereği ana düğümün gerisindedir. Yerine geçtiği anda elinde
olmayan yazmalar **kabul edilmiş ve başarılı diye bildirilmiş** ödünç kayıtlarıdır. Devralmada
kayıp bir olasılık değil, bir **garantidir**; ölçülecek olan yalnız sayısıdır.

#### Düzenek

Aşağıdaki düzenek bir **modeldir**: gerçek gözcü süreci, ağ ya da düğüm kurulmaz. Tur soyut bir
adımdır; algılama eşiği, seçim ve yükleme süresi tur cinsinden parametrelerdir, ölçülmüş süre
değildir.

**BK4 — ana düğümün geçici duraklamaları (2–7 tur) ile gözcülerin kendi ulaşamama aralıkları
(3–6 tur) ayrı üreteçlerden gelir ve ayardan bağımsızdır.** Gerekçe: aynı arıza dizisi bütün
ayarlarda koşturulabilsin diye takvim önceden üretilir, tohum görünürdür; ayrıca gözcünün kendi
kusuru, izlediği düğümün duraklamasından ayırt edilemez. **BK5 — yanlış devralmadan sonra eski
ana düğüm 3 tur boyunca yazma kabul etmeyi sürdürür.** Gerekçe: yerinden edildiğini öğrenmesi
anlık değildir; o turlarda yazılan kayıtlar **bölünmüş yazmadır** ve düğüm yedeğe indirildiğinde
atılır.

```js
// devralma/model.mjs — gozculu devralma SUREC ICI MODELDIR. Tur soyut bir adimdir; algilama
// esigi, secim ve yukleme suresi tur cinsinden parametrelerdir, olculmus sure degildir. Gercek
// gozcu sureci, ag ya da dugum kurulmaz. Onceki dersin kisiti aynen surer: cogaltma eszamansizdir,
// bu yuzden yedegin uygulamadigi yazmalar KABUL EDILMIS ama ulasmamis yazmalardir.
export const GIRIS_BAYT = 240, KAYIT_BAYT = 96, ANAHTAR = 2000;

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

export function kosum({ gozcu = 3, dusenGozcu = 0, algilama = 3, secim = 2, gecikme = 2,
                        soguk = null, tur = 300, yazma = 40, ariza = 200, bolunme = 3,
                        duraklamaOlas = 0.02, kendiOlas = 0.03, tohum = 20260731 }) {
  const rnd = uretec(tohum);
  const takvim = (olas, enAz, cesit) => {                  // takvimler ayardan bagimsiz uretilir
    const d = new Uint8Array(tur + 2);
    for (let t = 1; t <= tur; t += 1) {
      if (d[t] || rnd() >= olas) continue;
      for (let x = t, b = enAz + Math.floor(rnd() * cesit); x < t + b && x <= tur; x += 1) d[x] = 1;
    }
    return d;
  };
  const durak = takvim(duraklamaOlas, 2, 6);               // ana dugumun gecici duraklamasi
  const kendi = [...Array(7)].map(() => takvim(kendiOlas, 3, 4));  // gozcunun KENDI ulasamamasi
  const cogunluk = Math.floor(gozcu / 2) + 1, oyVeren = gozcu - dusenGozcu;
  const bekleme = secim + (soguk ? soguk.yukleme : 0), sayac = Array(7).fill(0);
  let kabul = 0, uygulanan = 0, arizaAktif = true, kalan = 0, sebep = null, bolunmeKalan = 0;
  let yazmasiz = 0, gercek = 0, yanlis = 0, kayip = 0, bolunmus = 0, arizaKayip = 0, kesinti = null;

  for (let t = 1; t <= tur; t += 1) {
    if (bolunmeKalan > 0) { bolunmeKalan -= 1; bolunmus += yazma; }  // eski ana dugum hala yaziyor
    const ulasilamaz = (arizaAktif && t >= ariza) || durak[t] === 1;
    if (kalan > 0) {                                       // devralma penceresi: yazma kabul edilmez
      kalan -= 1; yazmasiz += 1;
      if (kalan > 0) continue;
      const taban = soguk                                  // soguk yedek son anlik goruntuden baslar
        ? Math.floor(kabul / (soguk.aralik * yazma)) * soguk.aralik * yazma : uygulanan;
      kayip += kabul - taban;
      if (sebep === "gercek") { gercek += 1; arizaAktif = false; kesinti = t - ariza + 1; arizaKayip = kabul - taban; }
      else { yanlis += 1; bolunmeKalan = bolunme; }
      kabul = taban; uygulanan = taban; sayac.fill(0);
      for (let x = t; x <= tur && durak[x] === 1; x += 1) durak[x] = 0;  // yeni dugum o duraklamada degil
      continue;
    }
    if (ulasilamaz) yazmasiz += 1;
    else { kabul += yazma; uygulanan = Math.max(uygulanan, kabul - gecikme * yazma); }
    let oy = 0;                                            // her gozcu KENDI gorusuyle oy verir
    for (let j = 0; j < oyVeren; j += 1) {
      sayac[j] = ulasilamaz || kendi[j][t] === 1 ? sayac[j] + 1 : 0;
      if (sayac[j] >= algilama) oy += 1;
    }
    if (oy >= cogunluk) { kalan = bekleme; sebep = arizaAktif && t >= ariza ? "gercek" : "yanlis"; }
  }
  return { cogunluk, oyVeren, gercek, yanlis, kayip, arizaKayip, bolunmus, yazmasiz, kesinti,
           veri: (soguk ? 1 : 2) * ANAHTAR * GIRIS_BAYT }; // sicak yedek veri kumesini bellekte tutar
}
```

```js
// devralma/olc.mjs — ayni arizada uc ayar: gozcu cogunlugu, algilama esigi, yedek turu
import { kosum, GIRIS_BAYT, KAYIT_BAYT, ANAHTAR } from "./model.mjs";

const s = (x, n) => String(x).padStart(n);
const kib = (b) => s((b / 1024).toFixed(1), 8);

console.log("300 tur; her turda 40 odunc kaydi. Ana dugum 200. turda kalici olarak durur. Tohum");
console.log("20260731: ana dugumde 2-7 turluk gecici duraklamalar, her gozcude 3-6 turluk kendi");
console.log("ulasamama araliklari uretilir. Gecikme 2 tur, algilama 3, secim 2 tur.\n");
console.log("gozcu | dusen | cogunluk | oy veren | ariza kesintisi | yanlis devralma | yazmasiz");
console.log("------|-------|----------|----------|-----------------|-----------------|---------");
for (const [g, d] of [[1, 0], [2, 0], [3, 0], [3, 1], [3, 2], [5, 0], [5, 2], [5, 3]]) {
  const r = kosum({ gozcu: g, dusenGozcu: d });
  console.log(`${s(g, 5)} | ${s(d, 5)} | ${s(r.cogunluk, 8)} | ${s(r.oyVeren, 8)} | ` +
    `${s(r.kesinti === null ? "yok" : r.kesinti + " tur", 15)} | ${s(r.yanlis, 15)} | ` +
    `${s(r.yazmasiz, 5)} tur`);
}

console.log("\n3 gozcu, hepsi ayakta; algilama esigi degisiyor:");
console.log("esik | yanlis devralma | bolunmus yazma | ariza kesintisi | yazmasiz | toplam kayip");
console.log("-----|-----------------|----------------|-----------------|----------|-------------");
for (const a of [1, 2, 3, 4, 5, 6]) {
  const r = kosum({ algilama: a });
  console.log(`${s(a, 4)} | ${s(r.yanlis, 15)} | ${s(r.bolunmus, 14)} | ` +
    `${s(r.kesinti + " tur", 15)} | ${s(r.yazmasiz, 5)} tur | ${s(r.kayip, 12)}`);
}

console.log("\nYedek turu. Gecici duraklama kapali: tek arizali devralma. Soguk yedegin verisi");
console.log("bellekte degildir; son anlik goruntu 30 tur oncesine aittir, yuklemesi 12 tur surer:");
console.log("yedek         | tutulan veri | ariza kesintisi | kayip yazma | kayip bayt");
console.log("--------------|--------------|-----------------|-------------|-----------");
for (const g of [1, 2, 4, 8]) {
  const r = kosum({ gecikme: g, duraklamaOlas: 0, kendiOlas: 0 });
  console.log(`sicak, gec. ${g} | ${kib(r.veri)} KiB | ${s(r.kesinti + " tur", 15)} | ` +
    `${s(r.arizaKayip, 11)} | ${s(r.arizaKayip * KAYIT_BAYT, 6)} bayt`);
}
const c = kosum({ duraklamaOlas: 0, kendiOlas: 0, soguk: { yukleme: 12, aralik: 30 } });
console.log(`soguk yedek   | ${kib(c.veri)} KiB | ${s(c.kesinti + " tur", 15)} | ` +
  `${s(c.arizaKayip, 11)} | ${s(c.arizaKayip * KAYIT_BAYT, 6)} bayt`);

console.log("\nkosumdan bagimsiz nicelikler:");
console.log("  gozcu cogunlugu      = floor(g/2)+1; katlanilan gozcu kaybi = g - cogunluk");
console.log("  g       : " + [1, 2, 3, 4, 5, 6, 7].map((n) => s(n, 4)).join(""));
console.log("  cogunluk: " + [1, 2, 3, 4, 5, 6, 7].map((n) => s(Math.floor(n / 2) + 1, 4)).join(""));
console.log("  kayip   : " + [1, 2, 3, 4, 5, 6, 7].map((n) => s(n - (Math.floor(n / 2) + 1), 4)).join(""));
console.log("  devralma basina kayip = gecikme x yazma hizi (sicak yedek)");
console.log("  gecikme : " + [1, 2, 4, 8].map((n) => s(n, 6)).join(""));
console.log("  kayip   : " + [1, 2, 4, 8].map((n) => s(n * 40, 6)).join(""));
console.log("  ariza kesintisi      = algilama esigi + secim (+ soguk yedekte yukleme) tur");
console.log("  soguk yedekte en kotu kayip = anlik goruntu araligi x yazma hizi = 30 x 40 = 1200");
console.log("  sicak yedegin bedeli = " + (ANAHTAR * GIRIS_BAYT / 1024).toFixed(1) +
  " KiB, yani veri kumesinin ikinci tam kopyasi (" + ANAHTAR + " x " + GIRIS_BAYT + " bayt)");
```

```
300 tur; her turda 40 odunc kaydi. Ana dugum 200. turda kalici olarak durur. Tohum
20260731: ana dugumde 2-7 turluk gecici duraklamalar, her gozcude 3-6 turluk kendi
ulasamama araliklari uretilir. Gecikme 2 tur, algilama 3, secim 2 tur.

gozcu | dusen | cogunluk | oy veren | ariza kesintisi | yanlis devralma | yazmasiz
------|-------|----------|----------|-----------------|-----------------|---------
    1 |     0 |        1 |        1 |           6 tur |              12 |    40 tur
    2 |     0 |        2 |        2 |           5 tur |               6 |    29 tur
    3 |     0 |        2 |        3 |           5 tur |               9 |    35 tur
    3 |     1 |        2 |        2 |           5 tur |               6 |    29 tur
    3 |     2 |        2 |        1 |             yok |               0 |   115 tur
    5 |     0 |        3 |        5 |           5 tur |               4 |    25 tur
    5 |     2 |        3 |        3 |           5 tur |               4 |    25 tur
    5 |     3 |        3 |        2 |             yok |               0 |   115 tur

3 gozcu, hepsi ayakta; algilama esigi degisiyor:
esik | yanlis devralma | bolunmus yazma | ariza kesintisi | yazmasiz | toplam kayip
-----|-----------------|----------------|-----------------|----------|-------------
   1 |              13 |           1560 |           3 tur |    33 tur |          960
   2 |              11 |           1320 |           5 tur |    34 tur |          880
   3 |               9 |           1080 |           5 tur |    35 tur |          800
   4 |               4 |            480 |           6 tur |    28 tur |          400
   5 |               2 |            240 |           7 tur |    26 tur |          240
   6 |               2 |            240 |           8 tur |    28 tur |          240

Yedek turu. Gecici duraklama kapali: tek arizali devralma. Soguk yedegin verisi
bellekte degildir; son anlik goruntu 30 tur oncesine aittir, yuklemesi 12 tur surer:
yedek         | tutulan veri | ariza kesintisi | kayip yazma | kayip bayt
--------------|--------------|-----------------|-------------|-----------
sicak, gec. 1 |    937.5 KiB |           5 tur |          40 |   3840 bayt
sicak, gec. 2 |    937.5 KiB |           5 tur |          80 |   7680 bayt
sicak, gec. 4 |    937.5 KiB |           5 tur |         160 |  15360 bayt
sicak, gec. 8 |    937.5 KiB |           5 tur |         320 |  30720 bayt
soguk yedek   |    468.8 KiB |          17 tur |         760 |  72960 bayt

kosumdan bagimsiz nicelikler:
  gozcu cogunlugu      = floor(g/2)+1; katlanilan gozcu kaybi = g - cogunluk
  g       :    1   2   3   4   5   6   7
  cogunluk:    1   2   2   3   3   4   4
  kayip   :    0   0   1   1   2   2   3
  devralma basina kayip = gecikme x yazma hizi (sicak yedek)
  gecikme :      1     2     4     8
  kayip   :     40    80   160   320
  ariza kesintisi      = algilama esigi + secim (+ soguk yedekte yukleme) tur
  soguk yedekte en kotu kayip = anlik goruntu araligi x yazma hizi = 30 x 40 = 1200
  sicak yedegin bedeli = 468.8 KiB, yani veri kumesinin ikinci tam kopyasi (2000 x 240 bayt)
```

#### Çoğunluğun İki Yüzü

**Çoğunluk toplanamadığında devralma hiç başlamaz.** Üç gözcüden ikisi ve beş gözcüden üçü
düştüğünde ayakta kalan gözcüler ana düğümün durduğunu doğru görüyor; yine de oy sayısı
çoğunluğun altındadır. Sonuç son sütunda: 115 yazmasız tur. Bu kuralın kendisidir — bir gözcü,
gördüğünü ötekilere doğrulatamadığı sürece kararı veremez.

**Çoğunluk asıl olarak gözcünün kendi kusuruna karşıdır.** Tek gözcülü satırda çoğunluk 1'dir ve
gözcünün kendi ulaşamama aralıkları doğrudan karara dönüşür: 12 yanlış devralma. Beş gözcüde
çoğunluk 3'e çıkar ve aynı takvimle yanlış devralma 4'e iner. Ana düğümün arıza takvimi bütün
satırlarda aynıdır; değişen, bir gözcünün yanılmasının tek başına yetip yetmediğidir.

Üç gözcülü satır ilk bakışta ters görünüyor: çoğunluk yine 2 iken yanlış devralma 6'dan 9'a
çıkıyor. Nedeni oy veren sayısıdır — iki gözcüde **ikisinin birden**, üç gözcüde **herhangi
ikisinin** yanılması yeter ve bu üç kat daha çok ikilidir. Doğrulaması aynı tablodadır: üç
gözcüden biri düştüğünde satır, iki gözcülü satırla birebir aynı sayıları veriyor.

#### Eşiğin Fiyatı

İkinci tablo algılama eşiğini iki yönde birden ölçüyor. Eşik 1'de gerçek arızanın kesintisi
3 tura iner; aynı satırda yanlış devralma 13, bölünmüş yazma 1.560 kayıttır. Eşik 6'da yanlış
devralma 2'ye, bölünmüş yazma 240'a düşer; karşılığında kesinti 8 tura çıkar.

Bölünmüş yazma bu takasın en pahalı kalemidir. Yanlış devralmada eski ana düğüm sağlamdır ve üç
tur boyunca ödünç kaydı kabul etmeyi sürdürür; bu kayıtlar yeni ana düğüme hiç ulaşmaz, eski
düğüm yedeğe indirildiğinde atılır. Eşik 1 ile eşik 6 arasındaki fark bu kalemde 1.320 kayıttır.

Toplam kayıp sütunu aynı yönde ilerliyor (960'a karşı 240): **her devralma, doğru ya da yanlış
olsun, kendi kaybını üretir.** Yazmasız tur sütunu ise tek yönlü değildir (33, 34, 35, 28, 26,
28) — eşiği yükseltmek gerçek arızadaki kesintiyi uzatır ama yanlış devralmaların açtığı
pencereleri kapatır.

#### Yedeğin Bellek Bedeli

Üçüncü tablo geçici duraklamaları kapatıp tek bir gerçek arızayı yalıtıyor. Sıcak yedek
satırlarında kayıp tam olarak `gecikme × yazma hızı`dır: 40, 80, 160 ve 320 ödünç kaydı. Kesinti
dört satırda da 5 turdur; gecikme kesintiyi değil, yalnız kaybı büyütür.

Soğuk yedek satırı bu kursun kuralını doğrudan gösteriyor. Yedeğin verisi bellekte tutulmadığında
tutulan veri 937,5 KiB'den 468,8 KiB'ye iner. Karşılığında kesinti 5 turdan 17 tura, kayıp 80
kayıttan 760 kayda (72.960 bayt; en kötü durumda 1.200 kayda kadar) çıkar. Sıcak yedeğin
468,8 KiB'lik bedeli tam olarak bunu satın alır: kesintinin üçte biri ve kaybın onda biri.

#### Özet

- Gözcü çoğunluğu `floor(g/2)+1`'dir; üç gözcüden ikisi ya da beş gözcüden üçü düştüğünde
  devralma hiç başlamadı ve arızadan sonraki 115 turda tek bir ödünç kaydı kabul edilmedi.
- Çoğunluk kuralı asıl olarak gözcünün kendi kusuruna karşıdır: aynı arıza takvimiyle tek gözcüde
  12, beş gözcüde 4 yanlış devralma ölçüldü.
- Çoğunluk oranını yükseltmeyen gözcü eklemesi tersine çalışır: iki gözcü (çoğunluk 2) 6, üç
  gözcü (çoğunluk yine 2) 9 yanlış devralma verdi.
- Algılama eşiği iki yönlüdür: eşik 1'de kesinti 3 tur, yanlış devralma 13, bölünmüş yazma 1.560
  kayıt; eşik 6'da kesinti 8 tur, yanlış devralma 2, bölünmüş yazma 240.
- Eşzamansız çoğaltma devralma başına kaybı garanti eder ve sayısı `gecikme × yazma hızı`dır:
  40, 80, 160, 320 ödünç kaydı — hepsi başarılı diye bildirilmiş kayıtlardır.
- Sıcak yedek veri kümesinin ikinci tam kopyasını, 468,8 KiB'yi tutar; karşılığında kesintiyi
  17 turdan 5 tura, kaybı 760 kayıttan 80 kayda indirir.

#### Sonraki Adım

Buraya kadarki iki ders aynı örtük varsayımı taşıdı: veri kümesinin tamamı tek bir düğüme
sığıyor. Çoğaltma o kümenin kopyalarını çıkardı, devralma kopyalardan birini göreve aldı; hiçbiri
kümeyi bölmedi. Katalog ve ödünç kayıtları bir düğümün bellek bütçesini aştığında geriye tek yol
kalır: anahtar alanını bölmek. Bedeli hemen görünür — aynı işlemde geçen iki anahtar farklı
düğümlere düşerse o işlem tek bir düğümde yürütülemez. Sonraki ders anahtarın bölüme nasıl
eşlendiğini, çok anahtarlı işlemlerin kaçının küme kısıtına takıldığını ve etiketle aynı bölüme
toplamanın düğüm başına tutulan baytı ne kadar dengesizleştirdiğini sayar.

### Kümeleme

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/kumeleme

Anahtar alanının bölümlenmesi: anahtarın karma yoluyla yuvaya, yuvanın düğüme eşlenmesinin kendi yazılmış karma işleviyle gösterilmesi, çok anahtarlı işlemin küme kısıtının dört düğüm sayısında ölçülüp tek düğüme düşme oranının bire bölü n üzeri k eksi bir formülüyle karşılaştırılması, anahtarları etiketle aynı bölüme toplamanın işlemleri yüzde yüze çıkarırken kullanılan yuva sayısını altıya düşürmesi ve etiketin taneliğine göre tutulan baytın çoğalmasıyla düğüm başına yükün dengesizleşmesinin sayılması.

Önceki iki ders veri kümesinin tamamının tek düğüme sığdığını varsaydı. Çoğaltma o kümenin
kopyalarını çıkardı, devralma kopyalardan birini göreve aldı; ikisi de saklanan baytı
çarptı, hiçbiri bölmedi. Katalog ve ödünç kayıtları bir düğümün bellek bütçesini aştığında geriye
tek yol kalır: anahtar alanını bölmek ve her düğüme yalnız bir payı vermek.

Bu ders o bölmeyi ele alır. Parçalama ve parça anahtarı seçimi İleri Veri Ölçekleme ve NoSQL
kurslarında ölçüldü; burada tekrarlanmaz. Buranın sorusu farklıdır: bölme kararı bellek içi
deponun **işlem** semantiğini nasıl kısıtlar ve o kısıtı kaldırmanın bellek bütçesindeki
karşılığı nedir.

#### Anahtardan Düğüme

Bölümleme iki adımdır. Anahtar bir **karma** işlevinden geçirilir ve sabit sayıdaki **yuvadan**
birine düşer; yuva aralıkları düğümlere dağıtılır. Aradaki yuva katmanı, düğüm eklendiğinde
yalnız yuva aralıklarının taşınmasını sağlar — anahtarların yeniden karmalanması gerekmez.
Aşağıdaki düzenekte 16.384 yuva vardır ve karma işlevi kendi yazılmıştır.

Bunun doğrudan sonucu şudur: **iki anahtarın hangi düğüme düşeceği birbirinden bağımsızdır.**
Bir ödünç işlemi katalog girişine, ödünç sayacına ve bekleme kuyruğuna birlikte dokunuyorsa,
bu üç anahtarın aynı düğümde olması bir tasarım kararı değil, bir rastlantıdır. Aynı düğümde
olmadıklarında işlem tek bir düğümde yürütülemez — **çok anahtarlı işlemin küme kısıtı** budur.

#### Düzenek

Düzenek bir **modeldir**: gerçek küme, ağ ya da düğüm kurulmaz; düğüm bir sayıdır ve anahtarın
hangi düğüme düştüğü karmadan hesaplanır.

**BK6 — şube payları eşit değildir: altı şube ödünçlerin yüzde 34, 24, 16, 12, 9 ve 5'ini
taşır.** Gerekçe: kütüphane şubeleri eşit büyüklükte değildir ve etiketleme kararının bedeli
tam olarak bu eşitsizlikte görünür. **BK7 — iki işlem biçimi ölçülür: T1 katalog girişi, ödünç
sayacı ve bekleme kuyruğuna dokunur; T2 buna şubenin eşzamanlı ödünç sayacını ekler.** Gerekçe:
sınır denetimi olmayan bir ödünç ile şube sınırını denetleyen bir ödünç farklı sayıda anahtara
dokunur ve küme kısıtı anahtar sayısına üstel bağlıdır.

```js
// kumeleme/model.mjs — anahtar alani bolumlemesi SUREC ICI MODELDIR. Gercek kume, ag ya da
// dugum kurulmaz; dugum bir sayidir ve anahtarin hangi dugume dustugu karmadan hesaplanir.
// Karma islevi (FNV-1a, 32 bit) burada kendi yazilmistir.
export const YUVA = 16384, KATALOG_BAYT = 240, ODUNC_BAYT = 96, KUYRUK_BAYT = 128, SAYAC_BAYT = 64;

export function karma(s) {
  let h = 0x811c9dc5;
  for (let i = 0; i < s.length; i += 1) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193) >>> 0; }
  return h >>> 0;
}
export function yuvaNo(anahtar) {          // anahtarda etiket varsa YALNIZ etiket karmalanir
  const a = anahtar.indexOf("{"), b = anahtar.indexOf("}");
  return karma(a >= 0 && b > a + 1 ? anahtar.slice(a + 1, b) : anahtar) % YUVA;
}
export const dugumu = (yuva, n) => Math.floor(yuva * n / YUVA);   // esit yuva araligi

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

// Sube paylari esit degildir: buyuk sube odunclerin ucte birini tasir.
export const PAY = [0.34, 0.24, 0.16, 0.12, 0.09, 0.05];

export function veri({ kitap = 3000, odunc = 12000, kuyrukPay = 0.25, tohum = 20260731 }) {
  const rnd = uretec(tohum), kayit = [], kuyruk = new Set();
  const esik = PAY.map((p, i) => PAY.slice(0, i + 1).reduce((a, b) => a + b, 0));
  for (let i = 0; i < odunc; i += 1) {
    const x = rnd(), s = esik.findIndex((e) => x < e);
    kayit.push([s < 0 ? PAY.length - 1 : s, Math.floor(rnd() * kitap)]);
  }
  for (let k = 0; k < kitap; k += 1) if (rnd() < kuyrukPay) kuyruk.add(k);
  return { kitap, kayit, kuyruk };
}

// Uc etiketleme semasi. Etiket, anahtarin hangi parcasinin karmalanacagini belirler.
export const SEMA = {
  yok: { katalog: (k) => `katalog:${k}`, odunc: (s, k) => `odunc:${s}:${k}`,
         kuyruk: (k) => `kuyruk:${k}`, sayac: (s) => `sube:${s}:acikOdunc` },
  sube: { katalog: (k, s) => `{sube:${s}}katalog:${k}`, odunc: (s, k) => `{sube:${s}}odunc:${k}`,
          kuyruk: (k, s) => `{sube:${s}}kuyruk:${k}`, sayac: (s) => `{sube:${s}}acikOdunc` },
  kitap: { katalog: (k) => `{kitap:${k}}katalog:${k}`, odunc: (s, k) => `{kitap:${k}}odunc:${s}`,
           kuyruk: (k) => `{kitap:${k}}kuyruk:${k}`, sayac: (s) => `sube:${s}:acikOdunc` },
};

export function kosum({ sema, dugum, d }) {
  const S = SEMA[sema], bayt = new Map(), yuvalar = new Set();
  const ekle = (anahtar, b) => { bayt.set(anahtar, b); yuvalar.add(yuvaNo(anahtar)); };
  let t1 = 0, t2 = 0;
  for (const [s, k] of d.kayit) {
    ekle(S.katalog(k, s), KATALOG_BAYT);                 // etiket sube ise katalog girisi COGALIR
    ekle(S.odunc(s, k), ODUNC_BAYT);
    if (d.kuyruk.has(k)) ekle(S.kuyruk(k, s), KUYRUK_BAYT);
    ekle(S.sayac(s), SAYAC_BAYT);
    const g = [S.katalog(k, s), S.odunc(s, k), S.kuyruk(k, s)].map((a) => dugumu(yuvaNo(a), dugum));
    if (g.every((x) => x === g[0])) t1 += 1;              // T1: katalog + odunc + kuyruk
    if (g.every((x) => x === g[0]) && dugumu(yuvaNo(S.sayac(s)), dugum) === g[0]) t2 += 1;
  }
  const yuk = Array(dugum).fill(0), sayi = Array(dugum).fill(0);
  for (const [anahtar, b] of bayt) {
    const n = dugumu(yuvaNo(anahtar), dugum);
    yuk[n] += b; sayi[n] += 1;
  }
  const toplam = yuk.reduce((a, b) => a + b, 0);
  return { yuvalar: yuvalar.size, anahtar: bayt.size, toplam, yuk, sayi,
           enYuklu: Math.max(...yuk), dengesizlik: Math.max(...yuk) / (toplam / dugum),
           t1: t1 / d.kayit.length, t2: t2 / d.kayit.length };
}
```

```js
// kumeleme/olc.mjs — ayni veri kumesi: once dugum sayisi, sonra uc etiketleme semasi
import { veri, kosum, yuvaNo, dugumu, YUVA } from "./model.mjs";

const s = (x, n) => String(x).padStart(n);
const kib = (b, n = 8) => s((b / 1024).toFixed(1), n);
const yuz = (x, n = 7) => s((x * 100).toFixed(2) + "%", n);
const d = veri({});

console.log("3.000 kitap, 6 sube, 12.000 odunc kaydi (tohum 20260731). Katalog girisi 240,");
console.log("odunc 96, kuyruk 128, sube sayaci 64 bayt. " + YUVA + " yuva, dugume esit araliklarla.");
console.log("T1 = katalog + odunc + kuyruk; T2 = T1 + sube sayaci. Etiket yok:\n");
console.log("dugum | anahtar | toplam bayt | en yuklu dugum | dengesizlik | T1 tek dugumde | T2");
console.log("------|---------|-------------|----------------|-------------|----------------|--------");
for (const n of [3, 4, 6, 8]) {
  const r = kosum({ sema: "yok", dugum: n, d });
  console.log(`${s(n, 5)} | ${s(r.anahtar, 7)} | ${kib(r.toplam, 7)} KiB | ${kib(r.enYuklu, 10)} KiB | ` +
    `${s(r.dengesizlik.toFixed(3), 11)} | ${yuz(r.t1, 14)} | ${yuz(r.t2)}`);
}

console.log("\n6 dugum sabit; etiketleme semasi degisiyor:");
console.log("sema  | kullanilan yuva | anahtar | toplam bayt | en yuklu | dengesizlik | T1     | T2");
console.log("------|-----------------|---------|-------------|----------|-------------|--------|--------");
for (const sema of ["yok", "sube", "kitap"]) {
  const r = kosum({ sema, dugum: 6, d });
  console.log(`${sema.padEnd(5)} | ${s(r.yuvalar, 15)} | ${s(r.anahtar, 7)} | ${kib(r.toplam, 7)} KiB | ` +
    `${kib(r.enYuklu, 6)} K | ${s(r.dengesizlik.toFixed(3), 11)} | ${yuz(r.t1)} | ${yuz(r.t2)}`);
}

console.log("\n6 dugum, etiket = sube. Dugum basina yuk (sube paylari 34/24/16/12/9/5):");
const b = kosum({ sema: "sube", dugum: 6, d });
console.log("dugum   : " + b.yuk.map((_, i) => s("d" + i, 9)).join(""));
console.log("anahtar : " + b.sayi.map((x) => s(x, 9)).join(""));
console.log("KiB     : " + b.yuk.map((x) => s((x / 1024).toFixed(1), 9)).join(""));

console.log("\nkosumdan bagimsiz nicelikler:");
console.log("  K anahtarli islemin tek dugume dusme olasiligi = (1/n)^(K-1), etiket yokken");
console.log("  n       : " + [3, 4, 6, 8].map((n) => s(n, 8)).join(""));
console.log("  K=3     : " + [3, 4, 6, 8].map((n) => s((100 / n ** 2).toFixed(2) + "%", 8)).join(""));
console.log("  K=4     : " + [3, 4, 6, 8].map((n) => s((100 / n ** 3).toFixed(2) + "%", 8)).join(""));
console.log("  etiket = sube  -> kullanilabilir yuva sayisi = sube sayisi = 6");
console.log("  etiket = kitap -> kullanilabilir yuva sayisi = kitap sayisi = 3000");
console.log("  ornek yuva no  : sube:0 -> " + yuvaNo("{sube:0}katalog:7") +
  ", kitap:7 -> " + yuvaNo("{kitap:7}katalog:7") + ", etiketsiz katalog:7 -> " + yuvaNo("katalog:7"));
console.log("  6 dugumde bu yuvalarin dugumleri: " + [
  dugumu(yuvaNo("{sube:0}katalog:7"), 6), dugumu(yuvaNo("{kitap:7}katalog:7"), 6),
  dugumu(yuvaNo("katalog:7"), 6)].join(", "));
```

```
3.000 kitap, 6 sube, 12.000 odunc kaydi (tohum 20260731). Katalog girisi 240,
odunc 96, kuyruk 128, sube sayaci 64 bayt. 16384 yuva, dugume esit araliklarla.
T1 = katalog + odunc + kuyruk; T2 = T1 + sube sayaci. Etiket yok:

dugum | anahtar | toplam bayt | en yuklu dugum | dengesizlik | T1 tek dugumde | T2
------|---------|-------------|----------------|-------------|----------------|--------
    3 |   11758 |  1539.6 KiB |      553.0 KiB |       1.078 |         11.43% |   3.15%
    4 |   11758 |  1539.6 KiB |      408.4 KiB |       1.061 |          5.97% |   1.09%
    6 |   11758 |  1539.6 KiB |      284.4 KiB |       1.108 |          2.32% |   0.30%
    8 |   11758 |  1539.6 KiB |      210.6 KiB |       1.094 |          1.22% |   0.13%

6 dugum sabit; etiketleme semasi degisiyor:
sema  | kullanilan yuva | anahtar | toplam bayt | en yuklu | dengesizlik | T1     | T2
------|-----------------|---------|-------------|----------|-------------|--------|--------
yok   |            8709 |   11758 |  1539.6 KiB |  284.4 K |       1.108 |   2.32% |   0.30%
sube  |               6 |   18151 |  2899.0 KiB | 1452.5 K |       3.006 | 100.00% | 100.00%
kitap |            2813 |   11758 |  1539.6 KiB |  266.0 K |       1.036 | 100.00% |  16.83%

6 dugum, etiket = sube. Dugum basina yuk (sube paylari 34/24/16/12/9/5):
dugum   :        d0       d1       d2       d3       d4       d5
anahtar :         0        0        0     9052     9099        0
KiB     :       0.0      0.0      0.0   1446.6   1452.5      0.0

kosumdan bagimsiz nicelikler:
  K anahtarli islemin tek dugume dusme olasiligi = (1/n)^(K-1), etiket yokken
  n       :        3       4       6       8
  K=3     :   11.11%   6.25%   2.78%   1.56%
  K=4     :    3.70%   1.56%   0.46%   0.20%
  etiket = sube  -> kullanilabilir yuva sayisi = sube sayisi = 6
  etiket = kitap -> kullanilabilir yuva sayisi = kitap sayisi = 3000
  ornek yuva no  : sube:0 -> 11178, kitap:7 -> 8777, etiketsiz katalog:7 -> 15851
  6 dugumde bu yuvalarin dugumleri: 4, 3, 5
```

#### Küme Kısıtının Büyüklüğü

**Bölmek dengelidir; kısıtlayan şey denge değildir.** İlk tablonun dengesizlik sütunu dört
düğüm sayısında da 1,06 ile 1,11 arasındadır: en yüklü düğüm ortalamanın yüzde on bir üstündedir.
Karma dağıtımı işini yapıyor ve tutulan bayt gerçekten bölünüyor — üç düğümde en yüklü düğüm
553,0 KiB, sekiz düğümde 210,6 KiB tutuyor. Bu, çoğaltmanın yapamadığı şeydir.

**Kaybedilen şey işlemdir.** Aynı tablonun son iki sütunu bunu sayıyor: üç düğümlü kümede
T1 işlemlerinin yüzde 11,43'ü, T2 işlemlerinin yüzde 3,15'i tek düğüme düşüyor. Sekiz düğümde
oranlar yüzde 1,22 ve yüzde 0,13'e iniyor. Beklenen değer koşumdan bağımsız satırlarda:
`(1/n)^(K-1)`. Ölçülen sayılar bu beklentinin yakınındadır — ödünç kayıtları bağımsız değildir,
aynı kitap–şube çifti birden çok kez geçer, bu yüzden sapma vardır — ama eğilim aynıdır:
**düğüm eklemek işlemin tek düğümde kalma şansını üstel olarak düşürür** ve dokunulan anahtar
sayısı üsttedir. T2, T1'e tek bir anahtar ekliyor ve oranı sekiz düğümde dokuz kat düşürüyor.

Bu, kümeye geçişin gerçek bedelidir. Şube sınırını denetleyen bir ödünç işlemi bin denemesinden
birinde tek düğümde yürütülebiliyorsa, o işlem küme kurulumunda uygulama tarafından yürütülmek
zorundadır ve atomikliği kalmaz.

#### Etiketin Taneliği

İkinci tablo kısıtı kaldırmanın üç yolunu karşılaştırıyor. **Etiket**, anahtarın yalnız belirli
bir parçasının karmalanmasıdır; aynı etiketi taşıyan bütün anahtarlar aynı yuvaya, dolayısıyla
aynı düğüme düşer.

**Etiket şube olduğunda küme kısıtı tamamen kalkar ve bölümleme de kalkar.** T1 ve T2 yüzde
100'e çıkıyor. Karşılığında kullanılan yuva sayısı 8.709'dan **6'ya** iniyor: altı şube, altı
yuva. Üçüncü blok sonucu açıkça gösteriyor — altı düğümün dördü tamamen boştur, bütün veri iki
düğümde toplanmıştır ve dengesizlik 3,006'dır. Bellek sınırına çarpacak düğüm bellidir.

İkinci bir kalem daha var ve doğrudan bellek bütçesindendir: toplam tutulan bayt 1.539,6 KiB'den
**2.899,0 KiB'ye** çıkıyor, anahtar sayısı 11.758'den 18.151'e. Nedeni, katalog girişinin artık
şube etiketiyle yazılmasıdır — aynı kitabın kaydı, o kitabın ödünç verildiği her şubede ayrıca
durur. Etiketleme yalnız yeri değil, **kopya sayısını** da belirler.

**Etiket kitap olduğunda ölçüler tersine döner.** Kullanılan yuva 2.813'e çıkar, dengesizlik
1,036'ya iner — etiketsiz dağıtımdan bile daha dengelidir — ve toplam bayt hiç büyümez, çünkü
katalog girişi çoğalmaz. T1 yüzde 100'dür: bir kitabın katalog girişi, ödünç kaydı ve bekleme
kuyruğu her zaman aynı düğümdedir. T2 ise yüzde 16,83'te kalır, çünkü şubenin eşzamanlı ödünç
sayacı kitap etiketini taşımaz ve altı düğümden birine rastgele düşer.

Buradan çıkan kural, etiketin kendisiyle değil işlemle ilgilidir: **etiket bir işlemi tek düğüme
sığdırmaz, işlemin kapsamını belirler.** Şube sayacını da aynı düğüme çekmek için ya sayacı
kitap etiketine bağlamak — ki o zaman şube başına tek bir sayaç kalmaz — ya da sınır denetimini
işlemin dışına çıkarmak gerekir. Küme kurulumunda hangi işlemlerin atomik kalacağına anahtar
adlandırması karar verir.

#### Özet

- Anahtar önce karmayla 16.384 yuvadan birine, yuva sonra düğüme eşlenir; iki anahtarın aynı
  düğüme düşmesi bir tasarım kararı değil, rastlantıdır.
- Etiketsiz bölümleme dengelidir (dengesizlik 1,06–1,11) ve tutulan baytı gerçekten böler:
  en yüklü düğüm üç düğümde 553,0 KiB, sekiz düğümde 210,6 KiB.
- Çok anahtarlı işlemin tek düğüme düşme oranı `(1/n)^(K-1)`'e uyar: T1 için yüzde 11,43'ten
  yüzde 1,22'ye, T2 için yüzde 3,15'ten yüzde 0,13'e iner.
- Etiket şube olduğunda T1 ve T2 yüzde 100'e çıkar; bedeli kullanılan yuvanın 6'ya inmesi, altı
  düğümün dördünün boş kalması ve dengesizliğin 3,006 olmasıdır.
- Şube etiketi katalog girişini şube başına çoğalttığı için toplam tutulan bayt 1.539,6 KiB'den
  2.899,0 KiB'ye, anahtar sayısı 11.758'den 18.151'e çıktı.
- Kitap etiketi dengeyi 1,036'ya çeker ve baytı hiç büyütmez ama yalnız T1'i yüzde 100 yapar;
  şube sayacını içeren T2 yüzde 16,83'te kalır — etiket, işlemin kapsamını belirler.

#### Sonraki Adım

Bu ders bir işlemin bütün anahtarlarının aynı düğümde toplanabildiğini gösterdi, ama toplandıktan
sonra ne olacağını hiç sormadı. Bir ödünç işlemi şubenin eşzamanlı ödünç sayacını okur, sınırı
aşıp aşmadığına bakar ve ancak öyle artırır; okuma ile artırma arasında geçen sürede başka bir
şube görevlisi aynı sayacı değiştirmiş olabilir. Bellek içi depoda bu boşluğu kapatmanın yolu
kilit almak değil, anahtarı **izlemek** ve değiştiyse işlemi düşürüp yeniden denemektir. Sonraki
ders bu izleme tabanlı denetimi eşzamanlılık altında ölçer: çakışma oranı, işlem başına yeniden
deneme sayısı, izlenen anahtarın tuttuğu üstveri ve işlem yürürken bütün istemcilerin bekletildiği
turlar.

### İşlemler ve İyimser Kilitleme

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/islemler-ve-iyimser-kilitleme

İzleme tabanlı çakışma denetiminin bellek içi depoda ölçülmesi: eşzamanlılık iki katına çıktıkça çakışma ve iş başına yeniden deneme sayısının üstel büyümesi, aynı işin üç şemada karşılaştırılması ve koşulsuz atomik adımın hiç çakışmadan sınırı seksen altı kez aşması, izleme üstverisinin istemci sayısıyla büyüyüp koruduğu sayaç belleğini geçmesi, tek iş parçacıklı deponun işlem bloğu yürürken bütün istemcileri bekletmesinin blok boyuna göre sayılması.

Önceki ders bir işlemin bütün anahtarlarının aynı düğümde toplanabildiğini gösterdi, ama
toplandıktan sonra ne olacağını sormadı. Şube sınırını denetleyen bir ödünç işlemi şunu yapar:
şubenin eşzamanlı ödünç sayacını okur, sınırı aşıp aşmadığına bakar, ancak öyle artırır. Okuma
ile artırma arasında bir boşluk vardır ve o boşlukta başka bir görevli aynı sayacı değiştirmiş
olabilir.

İyimser kilitleme kalıbı — sürüm karşılaştır, çakışırsa yeniden dene — İşlem Yönetimi kursunda
kuruldu ve orada ölçüldü; burada tekrarlanmaz. Bu dersin konusu kalıbın **bellek içi depodaki
gerçekleşmesidir**: depo tek iş parçacıklıdır, komutları sırayla işler ve bir işlem bloğu
bölünmeden yürür. Bu iki özellik hem çakışmanın nasıl algılandığını hem de bedelinin kime
ödetildiğini belirler.

#### İzleme, Düşünme, Gönderme

İstemci önce sayacı **izlemeye** alır, sonra okur. Okuduğu değerle sınırı karşılaştırması depo
tarafında değil istemci tarafında olur; depo o sırada başka istemcilere hizmet eder. İstemci
işlemi gönderdiğinde depo tek bir soruya bakar: izlenen anahtar, izleme kurulduğundan beri
değişti mi. Değiştiyse işlem hiç yürümez ve istemci baştan başlar.

Çakışma penceresi tam olarak bu **düşünme** aralığıdır ve uzunluğu istemcinin elindedir. Deponun
elinde olan şey ise başkadır: işlem bloğu yürürken hiçbir başka komut araya giremez. Bu,
atomikliğin nereden geldiğini açıklar ve aynı zamanda bedelini de yazar — blok ne kadar uzunsa
bütün istemciler o kadar bekler.

#### Düzenek

Düzenek bir **modeldir**: gerçek depo, ağ ya da istemci kurulmaz. Tik soyut bir adımdır ve depo
her tikte tam olarak bir komut işler.

**BK8 — düşünme aralığı 3 tik, işlem bloğu varsayılan olarak 4 komuttur.** Gerekçe: okuma ile
gönderme arasındaki hesap ile bloğun uzunluğu bağımsız iki ayardır ve ikisi ayrı ölçülür.
**BK9 — işlerin yüzde 55'i ödünç verme (+1), yüzde 45'i iadedir (−1) ve iş listesi şemadan
bağımsız üretilir.** Gerekçe: sayaç sınırın yakınında dolaşsın ve denetim gerçekten bağlasın;
aynı iş listesi üç şemada da koşturulabilsin. **BK10 — izlenen anahtar başına 48, sayaç başına
64, kilit anahtarı başına 64 bayt üstveri tutulur.** Gerekçe: izleme kaydı anahtar bağını ve
sürümü tutar; sayaç ile kilit birer giriştir.

```js
// islem/model.mjs — TEK IS PARCACIKLI bellek ici deponun SUREC ICI MODELIDIR. Tik soyut bir
// adimdir: depo her tikte tam olarak bir komut isler, islem blogu ise bolunmeden blok tik tutar.
// Gercek depo, ag ya da istemci kurulmaz. Dusunme, istemcinin okuma ile gonderme arasindaki
// hesabidir; depo o sirada baskalarina hizmet eder ve cakisma penceresi tam olarak orasidir.
export const SAYAC_BAYT = 64, IZLEME_BAYT = 48, KILIT_BAYT = 64;

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

export function kosum({ sema, istemci, sube = 6, sinir = 20, isPay = 30, dusunme = 3,
                        blok = 4, tohum = 20260731 }) {
  const rnd = uretec(tohum);
  // Is listesi semadan bagimsiz uretilir: her is ya odunc verme (+1) ya iade (-1).
  const isler = [...Array(istemci)].map(() => [...Array(isPay)].map(() => rnd() < 0.55));
  const sayac = Array(sube).fill(10), surum = Array(sube).fill(0), kilit = Array(sube).fill(-1);
  const c = [...Array(istemci)].map((_, i) => ({ id: i, s: i % sube, i: 0, faz: 0, bekle: 0, gor: 0 }));
  let tik = 0, mesgul = 0, sira = 0, cakisma = 0, asim = 0, engel = 0, blokEngel = 0, tamam = 0;

  const uygula = (x) => {                                  // isin sayaca etkisi; sinir DENETLENIR
    const artir = isler[x.id][x.i];
    if (artir && sayac[x.s] < sinir) sayac[x.s] += 1;
    else if (!artir && sayac[x.s] > 0) sayac[x.s] -= 1;
    surum[x.s] += 1; x.i += 1; tamam += 1; x.faz = 0;
  };

  while (tamam < istemci * isPay && tik < 400000) {
    tik += 1;
    for (const x of c) if (x.bekle > 0) x.bekle -= 1;
    const hazir = c.filter((x) => (x.i < isPay || x.faz > 0) && x.bekle === 0);
    if (mesgul > 0) { mesgul -= 1; engel += hazir.length; blokEngel += hazir.length; continue; }
    if (hazir.length === 0) continue;
    const x = hazir[sira % hazir.length]; sira += 1;        // depo bir komuta hizmet eder
    engel += hazir.length - 1;
    if (sema === "atomik") {                                // tek atomik adim, kosul YOK
      const artir = isler[x.id][x.i];
      if (artir) { if (sayac[x.s] >= sinir) asim += 1; sayac[x.s] += 1; }
      else if (sayac[x.s] > 0) sayac[x.s] -= 1;
      surum[x.s] += 1; x.i += 1; tamam += 1;
    } else if (sema === "izleme") {
      if (x.faz === 0) { x.gor = surum[x.s]; x.faz = 1; }   // izle
      else if (x.faz === 1) { x.bekle = dusunme; x.faz = 2; }  // oku, sonra istemci hesaplar
      else if (surum[x.s] !== x.gor) { cakisma += 1; x.faz = 0; }  // izlenen anahtar degismis
      else { mesgul = blok - 1; uygula(x); }                // islem blogu BOLUNMEDEN yurur
    } else {                                                // kaba kilit: okuma-yazma arasi tutulur
      if (x.faz === 0) { if (kilit[x.s] === -1) { kilit[x.s] = x.id; x.faz = 1; } }
      else if (x.faz === 1) { x.bekle = dusunme; x.faz = 2; }
      else if (x.faz === 2) { uygula(x); x.faz = 3; }
      else { kilit[x.s] = -1; x.faz = 0; }
    }
  }
  const ustveri = sema === "izleme" ? istemci * IZLEME_BAYT : sema === "kilit" ? sube * KILIT_BAYT : 0;
  return { tik, cakisma, asim, engel, blokEngel, tamam, sayac,
           yeniden: cakisma / (istemci * isPay), bellek: sube * SAYAC_BAYT + ustveri, ustveri };
}
```

```js
// islem/olc.mjs — ayni is: once eszamanlilik, sonra uc sema, sonra islem blogu boyu
import { kosum, SAYAC_BAYT, IZLEME_BAYT } from "./model.mjs";

const s = (x, n) => String(x).padStart(n);
const yuz = (x, n = 7) => s((x * 100).toFixed(2) + "%", n);

console.log("6 sube, sube basina eszamanli odunc siniri 20. Her istemci 30 is yapar (%55 odunc,");
console.log("%45 iade; tohum 20260731). Dusunme 3 tik, islem blogu 4 komut. Izleme semasi:\n");
console.log("istemci |  is | cakisma | is basina yeniden deneme | toplam tik | engellenen istemci-tik");
console.log("--------|-----|---------|--------------------------|------------|-----------------------");
for (const n of [2, 4, 8, 16, 32]) {
  const r = kosum({ sema: "izleme", istemci: n });
  console.log(`${s(n, 7)} | ${s(n * 30, 3)} | ${s(r.cakisma, 7)} | ${s(r.yeniden.toFixed(3), 24)} | ` +
    `${s(r.tik, 10)} | ${s(r.engel, 22)}`);
}

console.log("\n16 istemci sabit; uc sema:");
console.log("sema    | toplam tik | cakisma | sinir asimi | engellenen | ustveri bayt | tutulan bayt");
console.log("--------|------------|---------|-------------|------------|--------------|-------------");
for (const sema of ["atomik", "izleme", "kilit"]) {
  const r = kosum({ sema, istemci: 16 });
  console.log(`${sema.padEnd(7)} | ${s(r.tik, 10)} | ${s(r.cakisma, 7)} | ${s(r.asim, 11)} | ` +
    `${s(r.engel, 10)} | ${s(r.ustveri, 12)} | ${s(r.bellek, 12)}`);
}

console.log("\n16 istemci, izleme semasi; islem blogu boyu degisiyor:");
console.log("blok | toplam tik | cakisma | blok yuzunden bekleyen | engellenen | bekleme payi");
console.log("-----|------------|---------|------------------------|------------|-------------");
for (const b of [1, 2, 4, 8, 16]) {
  const r = kosum({ sema: "izleme", istemci: 16, blok: b });
  console.log(`${s(b, 4)} | ${s(r.tik, 10)} | ${s(r.cakisma, 7)} | ${s(r.blokEngel, 22)} | ` +
    `${s(r.engel, 10)} | ${yuz(r.blokEngel / r.engel, 13)}`);
}

console.log("\nkosumdan bagimsiz nicelikler:");
console.log("  izleme ustverisi = istemci sayisi x " + IZLEME_BAYT + " bayt (izlenen anahtar basina)");
console.log("  istemci : " + [2, 4, 8, 16, 32].map((n) => s(n, 7)).join(""));
console.log("  bayt    : " + [2, 4, 8, 16, 32].map((n) => s(n * IZLEME_BAYT, 7)).join(""));
console.log("  sayac bellegi = 6 x " + SAYAC_BAYT + " = " + 6 * SAYAC_BAYT + " bayt (semadan bagimsiz)");
console.log("  bir islem blogu yururken bekleyen is <= (blok - 1) x hazir istemci sayisi");
console.log("  blok    : " + [1, 2, 4, 8, 16].map((n) => s(n, 7)).join(""));
console.log("  ust sinir (15 hazir istemci): " + [1, 2, 4, 8, 16].map((n) => s((n - 1) * 15, 7)).join(""));
```

```
6 sube, sube basina eszamanli odunc siniri 20. Her istemci 30 is yapar (%55 odunc,
%45 iade; tohum 20260731). Dusunme 3 tik, islem blogu 4 komut. Izleme semasi:

istemci |  is | cakisma | is basina yeniden deneme | toplam tik | engellenen istemci-tik
--------|-----|---------|--------------------------|------------|-----------------------
      2 |  60 |       0 |                    0.000 |        387 |                    470
      4 | 120 |       0 |                    0.000 |        717 |                   2244
      8 | 240 |      87 |                    0.362 |       1704 |                  10070
     16 | 480 |     475 |                    0.990 |       4314 |                  54181
     32 | 960 |    2201 |                    2.293 |      12372 |                 350407

16 istemci sabit; uc sema:
sema    | toplam tik | cakisma | sinir asimi | engellenen | ustveri bayt | tutulan bayt
--------|------------|---------|-------------|------------|--------------|-------------
atomik  |        480 |       0 |          86 |       7080 |            0 |          384
izleme  |       4314 |     475 |           0 |      54181 |          768 |         1152
kilit   |       4353 |       0 |           0 |      58883 |          384 |          768

16 istemci, izleme semasi; islem blogu boyu degisiyor:
blok | toplam tik | cakisma | blok yuzunden bekleyen | engellenen | bekleme payi
-----|------------|---------|------------------------|------------|-------------
   1 |       2965 |     506 |                      0 |      37476 |         0.00%
   2 |       3356 |     475 |                   6268 |      41303 |        15.18%
   4 |       4314 |     475 |                  19146 |      54181 |        35.34%
   8 |       6230 |     475 |                  44902 |      79937 |        56.17%
  16 |      10062 |     475 |                  96414 |     131449 |        73.35%

kosumdan bagimsiz nicelikler:
  izleme ustverisi = istemci sayisi x 48 bayt (izlenen anahtar basina)
  istemci :       2      4      8     16     32
  bayt    :      96    192    384    768   1536
  sayac bellegi = 6 x 64 = 384 bayt (semadan bagimsiz)
  bir islem blogu yururken bekleyen is <= (blok - 1) x hazir istemci sayisi
  blok    :       1      2      4      8     16
  ust sinir (15 hazir istemci):       0     15     45    105    225
```

#### Çakışma Doğrusal Değildir

İlk tablo eşzamanlılığı iki katına çıkarıyor ve çakışmayı sayıyor. İki ve dört istemcide tek bir
çakışma yok: altı şubeye dağılan görevliler aynı sayaca aynı anda dokunmuyor. Sekiz istemcide
87 çakışma, on altıda 475, otuz ikide 2.201 çakışma ölçülüyor. İş sayısı iki katına çıkarken
çakışma dört ile beş kat artıyor, çünkü çakışma **istemci sayısıyla değil, aynı sayaca aynı
pencerede dokunan istemci çiftleriyle** orantılıdır.

İş başına yeniden deneme sütunu bunu doğrudan okunur kılıyor: 0,362'den 0,990'a, oradan 2,293'e.
Otuz iki istemcide bir ödünç işlemi ortalama üç kez gönderiliyor ve ikisi boşa gidiyor. Toplam
tik 1.704'ten 12.372'ye çıkıyor — iş dört katına çıkarken harcanan tik yedi katına.

Engellenen istemci-tik sütunu ise 10.070'ten 350.407'ye, otuz beş kat büyüyor. Bu sayı, hazır
olduğu hâlde sırası gelmediği için bekleyen istemcilerin tik toplamıdır ve tek iş parçacıklı
deponun asıl ölçüsüdür: iş yapılmıyorsa bile birileri bekliyordur.

#### Aynı İşin Üç Şeması

İkinci tablo on altı istemcide üç şemayı yan yana koyuyor. **Koşulsuz atomik adım en hızlısıdır
ve yanlıştır.** 480 iş 480 tikte biter — komut başına bir tik, tek bir yeniden deneme yok. Sınır
aşımı sütunu neyin satın alındığını yazıyor: 86 kez şube sınırının üstünde ödünç verilmiştir.
Atomik artırma bir yarış koşulunu çözer ama bir **koşulu** yürütemez; sınırı okuyup karara
bağlamak iki adım ister ve iki adım arasına başkası girer.

**İzleme şeması doğrudur ve tikin dokuz katını harcar.** 4.314 tik, 475 çakışma, sıfır sınır
aşımı. Fark, doğruluğun fiyatıdır; ölçülen büyüklüğü de budur.

**Kaba kilit aynı doğruluğu verir ve daha ucuz değildir.** 4.353 tik ile izleme şemasının biraz
üstünde, engellenen istemci-tik ise 58.883 ile açıkça üstünde kalıyor. Nedeni, kilidin
istemcinin düşünme aralığı boyunca da tutulmasıdır: çakışma yok ama bekleyen var. İyimser
denetimin buradaki üstünlüğü, çakışma **olmadığında** kimseyi bekletmemesidir.

Bellek sütunları kursun kuralını hatırlatıyor. İzleme üstverisi istemci sayısıyla büyür: on
altı istemcide 768 bayt, otuz ikide 1.536 bayt. Korunan verinin kendisi ise altı sayaçtan
ibarettir, 384 bayt. Sekiz istemciden sonra **çakışma denetiminin defteri, koruduğu veriden
büyüktür.** Kilidin üstverisi ise şube başınadır ve istemci sayısından bağımsızdır: 384 bayt.
İki yaklaşım arasındaki bellek farkı, üstverinin neye bağlandığından çıkar.

#### İşlem Bloğunun Uzunluğu

Üçüncü tablo tek iş parçacıklı deponun kendine özgü bedelini yalıtıyor. Blok 1'de bloktan doğan
bekleme sıfırdır. Blok 16'da 96.414 istemci-tik yalnız blok yürürken birikir ve toplam beklemenin
yüzde 73,35'ini oluşturur. Çakışma sayısı bu satırlarda neredeyse sabittir (475) — blok
uzunluğu çakışmayı değiştirmez, çünkü çakışma penceresi düşünme aralığındadır.

Değişen şey, işlem yürürken **başka hiç kimsenin hizmet alamamasıdır.** Toplam tik 2.965'ten
10.062'ye çıkar. Koşumdan bağımsız üst sınır son satırdadır: on altılık blokta 15 hazır istemci
için 225 istemci-tik, tek bir işlem başına. Bir işlem bloğuna komut eklemek, o komutu bütün
istemcilerin faturasına yazar.

#### Özet

- Çakışma eşzamanlılıkla doğrusal büyümez: iki ve dört istemcide hiç çakışma olmadı, 8, 16 ve 32
  istemcide 87, 475 ve 2.201 çakışma ölçüldü; iş başına yeniden deneme 0,362'den 2,293'e çıktı.
- Koşulsuz atomik adım 480 işi 480 tikte bitirdi ve hiç çakışmadı, ama şube sınırını 86 kez
  aştı: atomik artırma yarışı çözer, koşulu yürütmez.
- İzleme şeması sıfır sınır aşımıyla 4.314 tik harcadı; doğruluğun fiyatı bu dokuz kattır.
- Kaba kilit aynı doğruluğu 4.353 tikte verdi ama 58.883 istemci-tik engelledi (izlemede 54.181),
  çünkü kilit istemcinin düşünme aralığı boyunca da tutulur.
- İzleme üstverisi istemci başınadır (16 istemcide 768 bayt) ve altı sayacın 384 baytını aşar;
  kilit üstverisi şube başınadır ve istemci sayısıyla büyümez.
- İşlem bloğu 1'den 16'ya çıktığında toplam tik 2.965'ten 10.062'ye, bloktan doğan bekleme
  0'dan 96.414 istemci-tike çıktı ve toplam beklemenin yüzde 73,35'i oldu; çakışma sayısı
  değişmedi.

#### Sonraki Adım

Bu ders bir yazmanın hangi koşullarda kabul edileceğini ölçtü, ama yazmanın kabul edildiğinden
kimin haberi olacağını hiç sormadı. Ödünç sayacı sınıra dayandığında bekleme listesindeki
okurun bilgilendirilmesi, bir kitap iade edildiğinde raf ekranının tazelenmesi, şube panosunun
son etkinliği göstermesi — bunların hepsi aynı soruyu sorar: değişiklik, onu bekleyen taraflara
nasıl ulaşır. Bu iş için anahtar okumak gerekmez; deponun kendisi bir iletiyi abonelerine
dağıtabilir. Sonraki ders bu dağıtımın ne olduğunu ve daha önemlisi ne **olmadığını** ölçer:
teslim güvencesi yoktur, abonesi olmayan ileti kaybolur ve yavaş bir abone deponun belleğinde
bir tampon büyütür.

### Yayımla–Abone Ol

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/yayimla-abone-ol

Bellek içi deponun hafif mesajlaşma yolu ve teslim güvencesinin olmayışı: abonesi yokken yayımlanan iletinin sayılan kaybı, yavaş abonenin çıkış tamponunda tuttuğu bayt ile düşürülme eşiği ve aynı olay akışının kalıcı bir yapıyla kurulduğunda ödediği bellek.

Önceki ders çakışma denetimini tek anahtar üzerinde çalıştırdı: bir istemcinin okuduğu değerin
başkası tarafından değiştirilip değiştirilmediği izlemeyle görüldü ve çakışan işlem yeniden
denendi. Orada iletişim, aynı anahtara dokunan iki istemci arasında dolaylı biçimde kuruldu.
Bir istemcinin başka bir istemciye **doğrudan** bir şey söylemesi, yani deponun bir iletim
kanalı olarak kullanılması hiç ele alınmadı.

Bellek içi depolar bunun için ayrı bir yol taşır: bir istemci bir **kanala** yayımlar, o kanala
abone olan istemciler iletiyi alır. Anahtar alanına hiçbir şey yazılmaz. Bu dersin ölçtüğü şey
o yolun hızı değil, **neyi güvence altına almadığıdır**.

#### Deponun Yayım Yolu

Kütüphane sisteminde ödünç olayları şube panolarına anlık olarak düşer. Aşağıdaki düzenek üç
şube kanalı, her şube için bir pano ve üç yüzüncü adımda katılan bir denetim abonesi kurar.
Panoların bağlantısı belirlenimli bir takvimle kopar; gerçek ağ kesintisi yerine adım sayısına
bakan bir kural kullanılır, böylece sayılar her koşumda aynı çıkar (**BK10**).

```js
// yayim-kaybi.mjs — abonesi olmayan kanala yayimlanan ileti kaybolur; kayip sayilir
class Depo {                                       // yayimla-abone ol: kayit yok, geri alma yok
  constructor() { this.kanallar = new Map(); }     // kanal -> Set(abone)
  abone(kanal, a) {
    if (!this.kanallar.has(kanal)) this.kanallar.set(kanal, new Set());
    this.kanallar.get(kanal).add(a);
  }
  birak(kanal, a) { this.kanallar.get(kanal)?.delete(a); }
  yayimla(kanal, ileti) {                          // donen sayi: iletinin ulastigi abone sayisi
    const kume = this.kanallar.get(kanal);
    if (!kume || kume.size === 0) return 0;         // abonesizse ileti hicbir yere yazilmaz
    for (const a of kume) a.al(ileti);
    return kume.size;
  }
}

const olaylar = (adet) => {                        // belirlenimli is yuku, tohum gorunur
  let tohum = 20250731;
  const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
  return Array.from({ length: adet }, (_, i) => ({
    adim: i + 1, sube: Math.floor(rast() * 3) + 1, kitapId: Math.floor(rast() * 400) + 1,
  }));
};

const depo = new Depo();
const kanal = (s) => `sube:${s}:odunc`;
const panolar = [1, 2, 3].map((s) => ({ ad: `pano${s}`, sube: s, aldi: 0, al() { this.aldi += 1; } }));
const denetim = { ad: "denetim", aldi: 0, al() { this.aldi += 1; } };
const sayac = [1, 2, 3].map(() => ({ yayim: 0, teslim: 0, abonesiz: 0 }));

// Pano baglantisi: her pano donemsel olarak kopar (belirlenimli takvim, rastgelelik yok).
const panoAcik = (s, adim) => (adim + s * 37) % 100 >= 25;   // adimlarin %75'inde bagli
for (const o of olaylar(600)) {
  for (const p of panolar) {                       // baglanti durumu abonelige cevrilir
    const acik = panoAcik(p.sube, o.adim);
    if (acik) depo.abone(kanal(p.sube), p); else depo.birak(kanal(p.sube), p);
  }
  if (o.adim === 301) for (const s of [1, 2, 3]) depo.abone(kanal(s), denetim);  // gec katilan abone

  const s = sayac[o.sube - 1];
  const ulasan = depo.yayimla(kanal(o.sube), o);
  s.yayim += 1; s.teslim += ulasan;
  if (ulasan === 0) s.abonesiz += 1;
}

console.log(["kanal", "yayim", "teslim", "abonesiz yayim", "panonun kacirdigi"]
  .map((b, i) => (i === 0 ? b.padEnd(16) : b.padStart(18))).join(""));
let toplam = [0, 0, 0, 0];
for (const [i, s] of sayac.entries()) {
  const kacan = s.yayim - panolar[i].aldi;
  toplam = toplam.map((t, j) => t + [s.yayim, s.teslim, s.abonesiz, kacan][j]);
  console.log(kanal(i + 1).padEnd(16) +
    [s.yayim, s.teslim, s.abonesiz, kacan].map((n) => String(n).padStart(18)).join(""));
}
console.log("toplam".padEnd(16) + toplam.map((n) => String(n).padStart(18)).join(""));
console.log(`gec katilan abonenin aldigi: ${denetim.aldi}  (katilmadan once yayimlanan: 300)`);
```

```
kanal                        yayim            teslim    abonesiz yayim panonun kacirdigi
sube:1:odunc                   199               266                21                46
sube:2:odunc                   195               227                31                55
sube:3:odunc                   206               260                22                46
toplam                         600               753                74               147
gec katilan abonenin aldigi: 300  (katilmadan once yayimlanan: 300)
```

Üç sayı okunmalı. **Teslim** sütunu yayım sayısından büyüktür (753 > 600), çünkü bir ileti kaç
abone varsa o kadar kopyalanır; yayımlayan taraf bu sayıyı önceden bilmez. **Abonesiz yayım**
sütunu 74 iletinin hiçbir yere yazılmadığını söyler: kanalda o an kimse yoktu, ileti üretildiği
adımda yok oldu. **Panonun kaçırdığı** sütunu daha büyüktür (147), çünkü bir pano bağlı değilken
başka bir abone bağlıysa ileti kaybolmaz ama o panoya ulaşmaz.

Son satır en keskin olanıdır. Denetim abonesi üç yüzüncü adımda katıldı ve tam 300 ileti aldı;
kendisinden önceki 300 iletiyi **hiçbir yolla** göremez. Deponun anahtar alanında bu iletilerden
geriye hiçbir iz kalmamıştır. Yayımla–abone ol yolunda teslim, iletinin yayımlandığı **an** ile
abonenin bağlı olduğu **an**ın çakışmasına indirgenmiştir.

#### Yavaş Abonenin Tamponu

Kayıp yalnız bağlantı kopukluğundan gelmez. Abone bağlı olduğu hâlde iletileri okuduğundan hızlı
üretiliyorsa, okunmamış iletiler deponun tuttuğu bir **çıkış tamponunda** birikir. Bu tampon
bellekten yenir ve yayımlayanın hesabında görünmez. Aşağıdaki koşum, aboneyi farklı okuma
hızlarıyla çalıştırır ve tamponun tuttuğu baytı sayar. Tampon 32.768 baytı aştığında depo aboneyi
düşürür; düşürülen abone için sonraki her yayım kayıptır.

```js
// yavas-abone.mjs — yavas abonenin cikis tamponu: tutulan bayt ve sinira carpma adimi
const ILETI = (adim, sube, kitapId) =>            // aynen tasinacak govde; bayti olculur
  JSON.stringify({ olay: "odunc", adim, sube, kitapId });

function kosu(okumaAraligi, sinirBayt, adet = 2000) {
  let tohum = 20250731;
  const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
  const tampon = [];                               // aboneye yazilamamis iletiler
  let bayt = 0, zirve = 0, kopmaAdimi = 0, kopmaSonrasi = 0, okunan = 0;
  for (let adim = 1; adim <= adet; adim += 1) {
    const g = ILETI(adim, Math.floor(rast() * 3) + 1, Math.floor(rast() * 400) + 1);
    if (kopmaAdimi) { kopmaSonrasi += 1; continue; }   // kopan abone icin yayim kayiptir
    tampon.push(g); bayt += Buffer.byteLength(g);  // depo once tampona yazar
    zirve = Math.max(zirve, bayt);
    if (bayt > sinirBayt) { kopmaAdimi = adim; continue; }   // sinir asilinca abone dusurulur
    if (adim % okumaAraligi === 0) {               // abone her `okumaAraligi` adimda bir okur
      const c = tampon.shift();
      if (c !== undefined) { bayt -= Buffer.byteLength(c); okunan += 1; }
    }
  }
  return { okumaAraligi, okunan, zirve, bekleyen: tampon.length, kopmaAdimi, kopmaSonrasi };
}

console.log(`ileti govdesi: ${ILETI(1, 1, 1)}  (${Buffer.byteLength(ILETI(1, 1, 1))} bayt)`);
console.log(["okuma araligi", "okunan", "zirve bayt", "tamponda kalan", "kopma adimi", "kopma sonrasi"]
  .map((b, i) => (i === 0 ? b.padEnd(15) : b.padStart(15))).join(""));
for (const aralik of [1, 2, 4, 10]) {
  const r = kosu(aralik, 32768);
  console.log(`her ${String(aralik).padStart(2)} adimda bir`.padEnd(15) +
    [r.okunan, r.zirve, r.bekleyen, r.kopmaAdimi, r.kopmaSonrasi]
      .map((n) => String(n).padStart(15)).join(""));
}
```

```
ileti govdesi: {"olay":"odunc","adim":1,"sube":1,"kitapId":1}  (46 bayt)
okuma araligi           okunan     zirve bayt tamponda kalan    kopma adimi  kopma sonrasi
her  1 adimda bir           2000             51              0              0              0
her  2 adimda bir            651          32784            653           1304            696
her  4 adimda bir            219          32780            659            878           1122
her 10 adimda bir             73          32804            660            733           1267
```

Yetişen abone belleğe 51 bayt bindirir: tampon hiçbir zaman bir iletiden fazlasını tutmaz. Okuma
aralığı ikiye çıktığında aynı abone 32.784 bayta ulaşır ve 1304. adımda düşürülür; kalan 696
yayım onun için yoktur. Aralık ona çıktığında sınıra 733. adımda çarpılır ve kayıp 1267 iletiye
yükselir.

Buradaki bütçe ilişkisi tersine işler. Bir abonenin **yavaşlığı** deponun **belleğinden** ödenir
ve bu bedel abone başınadır: yüz yavaş abone aynı sınırla yüz ayrı tampon demektir, yani en kötü
durumda 3,2 MB. Sınır, deponun kendini koruma biçimidir; bedeli, sınıra çarpan abonenin sessizce
düşürülmesidir. Yayımlayan taraf bunun hiçbirini görmez, çünkü yayım çağrısı iletinin okunmasını
beklemez (**BK11**).

#### Kalıcı Olan ile Olmayan

Aynı olay akışı, deponun **akış** yapısıyla da kurulabilir. Fark tek bir noktadadır: akış her
girişi bir kimlikle saklar, dolayısıyla geç gelen ya da kopup dönen abone kaldığı yerden okuyabilir.
Bunun karşılığı bellektir. Aşağıdaki koşum 2000 olayı üç yolla geçirir; abone 800. adımda kopar,
1500. adımda döner. Giriş başına 16 bayt işaretçi/uzunluk payı varsayılır (**BK12**).

```js
// akis-karsilastirma.mjs — ayni olay akisi: kalici olmayan yayim ile kirpilmis akisin bayti
const EK_YUK = 16;                                 // giris basina isaretci/uzunluk payi (kendi hesabim)
const ILETI = (adim, sube, kitapId) => JSON.stringify({ olay: "odunc", adim, sube, kitapId });
const KOPMA = 800, DONUS = 1500, ADET = 2000;      // abone 800'de kopar, 1500'de geri gelir

function olaylar(adet) {
  let tohum = 20250731;
  const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
  return Array.from({ length: adet }, (_, i) =>
    ILETI(i + 1, Math.floor(rast() * 3) + 1, Math.floor(rast() * 400) + 1));
}

function yayimYolu() {                             // kalici olmayan: kanal kaydindan baskasi yok
  let bayt = 0;
  for (const ad of ["sube:etkinlik", "pano"]) bayt += Buffer.byteLength(ad) + EK_YUK;
  return { ad: "yayimla-abone ol", donusBayt: bayt, sonBayt: bayt, giris: 0, geriAlinan: 0 };
}

function akisYolu(govdeler, kirpma) {              // kalici: giris kimligi ve govde bellekte durur
  const akis = [];
  let bayt = 0, donusBayt = 0, geriAlinan = 0;
  const olc = (g) => Buffer.byteLength(g.kimlik) + Buffer.byteLength(g.govde) + EK_YUK;
  govdeler.forEach((govde, i) => {
    const giris = { kimlik: `${i + 1}-0`, govde };
    akis.push(giris); bayt += olc(giris);
    while (kirpma && akis.length > kirpma) bayt -= olc(akis.shift());   // en eski giris duser
    if (i + 1 === DONUS) {                         // abone tam bu adimda geri geliyor
      donusBayt = bayt;
      geriAlinan = akis.filter((g) => {            // yalniz bosluktaki girisler geri alinabilir
        const adim = Number(g.kimlik.split("-")[0]);
        return adim >= KOPMA && adim < DONUS;
      }).length;
    }
  });
  return { ad: kirpma ? `akis (kirpma=${kirpma})` : "akis (kirpmasiz)",
           donusBayt, sonBayt: bayt, giris: akis.length, geriAlinan };
}

const govdeler = olaylar(ADET);
const bosluk = DONUS - KOPMA;                      // abonenin bagli olmadigi olay sayisi
console.log(`olay=${ADET}  bosluk=${bosluk} olay (adim ${KOPMA}-${DONUS - 1})`);
console.log(["yol", "bayt@1500", "bayt@2000", "giris", "geri alinan", "kayip"]
  .map((b, i) => (i === 0 ? b.padEnd(20) : b.padStart(13))).join(""));
for (const r of [yayimYolu(), akisYolu(govdeler, 500), akisYolu(govdeler, 0)]) {
  console.log(r.ad.padEnd(20) +
    [r.donusBayt, r.sonBayt, r.giris, r.geriAlinan, bosluk - r.geriAlinan]
      .map((n) => String(n).padStart(13)).join(""));
}
```

```
olay=2000  bosluk=700 olay (adim 800-1499)
yol                     bayt@1500    bayt@2000        giris  geri alinan        kayip
yayimla-abone ol               49           49            0            0          700
akis (kirpma=500)           36365        36362          500          499          201
akis (kirpmasiz)           106881       143243         2000          700            0
```

Üç satır bu kursun kuralını olduğu gibi gösteriyor. Yayımla–abone ol yolu **49 bayt** tutuyor;
bu, kanal adı ile abone kaydından ibarettir ve olay sayısıyla hiç büyümez. Karşılığında kopan
abonenin 700 olayının tamamı kayıptır.

Kırpılmış akış 36.365 bayt tutuyor ve boşluğun 499'unu geri veriyor; kalan 201 olay, abonenin
dönüşünden önce kırpıldığı için kaybolmuştur. Kırpma uzunluğu burada doğrudan bir **kurtarma
penceresidir**: 500 giriş, geriye dönük 500 olay demektir.

Kırpmasız akış hiçbir şey kaybetmiyor ama 1500. adımda 106.881, 2000. adımda 143.243 bayt
tutuyor. Sayının iki adım arasında büyümesi asıl uyarıdır: kırpması olmayan bir akışın belleği
olay sayısıyla sınırsız büyür ve bir bellek sınırı devreye girdiğinde bu büyüme başka verilerin
tahliyesine yol açar.

#### Yayımın Yeri

Bu ölçümler yayımla–abone ol yolunu değersiz kılmaz; yerini belirler. Kaybı kabul edilebilir olan
iş, iletinin kaçırılmasının **düzeltilebilir** olduğu iştir: bir panonun anlık sayacı, bir
önbellek geçersizleştirme uyarısı, bir yapılandırma değişikliği duyurusu. Bunların hepsinde abone
kaçırdığını bir sonraki tam okumada telafi eder.

Kaybın kabul edilemez olduğu işlerde ölçülen bedel ödenir: ödünç kaydının muhasebeye geçmesi,
gecikme cezasının işlenmesi, bir kitabın rafa dönüşünün kaydı. Bu işler ya kalıcı bir akışa yazılır
ya da deponun dışına, teslim güvencesi veren bir yapıya bırakılır. Ayrım "hangisi hızlı"
sorusuyla değil, "kaçırılan ileti geri getirilebilir mi" sorusuyla yapılır.

#### Özet

- Yayımla–abone ol yolu iletiyi anahtar alanına yazmaz: 600 yayımın 74'ü o an kanalda abone
  bulunmadığı için hiçbir yere ulaşmadan yok oldu, panolar ayrıca 147 ileti kaçırdı.
- Teslim sayısı yayım sayısından bağımsızdır; 600 yayım 753 teslim kopyası üretti ve yayımlayan
  taraf bu sayıyı önceden bilmez.
- Geç katılan abone katılmadan önceki 300 iletinin hiçbirini göremez; geçmiş, deponun hiçbir
  yerinde durmaz.
- Yavaş abonenin bedeli deponun belleğidir: okuma aralığı ikiye çıktığında çıkış tamponu 32.784
  bayta ulaştı, abone 1304. adımda düşürüldü ve sonraki 696 yayım onun için kayboldu.
- Aynı akış kalıcı yapıyla kurulduğunda 49 bayt yerine 36.365 bayt (kırpma 500) ya da 143.243
  bayt (kırpmasız) tutulur; satın alınan şey sırasıyla 499 ve 700 olayın geri alınabilmesidir.

#### Sonraki Adım

Buraya kadarki dersler deponun tek tek yeteneklerini ölçtü: çoğaltma, devralma, bölümleme,
çakışma denetimi ve şimdi yayım. Her biri kendi maliyetiyle tanımlandı, ama hiçbiri "bu iş bu
depoda hangi yapıyla kurulur ve kaç bayt tutar" sorusuna uçtan uca yanıt vermedi. Sonraki ders
kütüphane sisteminin dört tipik işini — katalog önbelleği, oturum kaydı, hız sınırı ve popüler
kitap sıralaması — tek tek ele alır; her birini bir yapıya oturtur ve her birinin bellek
hesabını aynı tabloda toplar.

### Tipik Kullanım Senaryoları

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/tipik-kullanim-senaryolari

Katalog önbelleği, oturum kaydı, hız sınırı ve sıralama tablosunun bellek içi deponun yapılarına oturtulması: her senaryonun hangi yapıyla kurulduğu, giriş başına kaç bayt tuttuğu ve o baytın karşılığında hangi erişimi satın aldığı.

Önceki ders yayımla–abone ol yolunu ölçtü ve bir sınırla bitti: o yol hiçbir şey saklamadığı
için 49 bayt tutuyordu, karşılığında kopan abonenin 700 olayı geri gelmiyordu. Bu, bu kursun
tekrar eden sorusunun tek bir örneğiydi. Aynı soru deponun günlük işlerinin hepsinde sorulur.

Bu ders kütüphane sisteminin dört tipik işini alır — katalog önbelleği, oturum kaydı, hız sınırı,
popüler kitap sıralaması — ve her birini tek bir soruya indirger: **hangi yapıyla kurulur ve kaç
bayt tutar.** Önbellek stratejisinin ne olacağı, hız sınırının hangi algoritmayla işleyeceği
burada tartışılmaz; o seçimler başka kurslarda ölçüldü. Burada ölçülen şey yapının kendisidir.

#### Ortak Maliyet Modeli

Dört hesap da aynı sayma kuralını kullanır (**BK13**): her giriş için 16 bayt işaretçi/uzunluk
payı, karma yapıda her alan için 8 bayt ek pay, sıralı kümede her üye için 24 bayt dizin düğümü
payı, her sayısal değer için 8 bayt. Anahtarların ve dizgi değerlerinin baytı gerçek uzunluktan
okunur. Bu model bir ürünün iç yerleşimi değil, kendi deposunun sayma sözleşmesidir; önemli
olan iki seçeneğin **aynı** kuralla sayılmasıdır.

#### Katalog Önbelleği ve Oturum Kaydı

İlk iki senaryo aynı yapıyı, karma yapıyı, iki farklı nedenle seçer. Katalog önbelleğinde soru
kaydın bütün hâlinde mi yoksa alan alan mı tutulacağıdır. Oturum kaydında soru, kayıtların ne
kadar süreyle tutulacağıdır: oturum verisi süre sonuyla birlikte kurulur ve bellek, açılan oturum
sayısıyla değil **aynı anda canlı** oturum sayısıyla ölçeklenir.

```js
// senaryo-katalog-oturum.mjs — katalog onbellegi ve oturum kaydi: hangi yapi, kac bayt
const EK_GIRIS = 16, EK_ALAN = 8, SAYI = 8;        // kendi maliyet modelim (giris/alan/sayi payi)
const b = (s) => Buffer.byteLength(String(s));
const alanBayt = (ad, deger) =>                    // karma yapida bir alanin maliyeti
  b(ad) + (typeof deger === "number" ? SAYI : b(deger)) + EK_ALAN;

function kitaplar(adet) {
  let tohum = 20250731;
  const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
  const etiketler = ["roman", "tarih", "cocuk", "bilim"];
  return Array.from({ length: adet }, (_, i) => ({
    kitapId: i + 1, baslik: `Kitap ${i + 1}`,
    yazar: `Yazar ${Math.floor(rast() * 120) + 1}`,
    sube: Math.floor(rast() * 3) + 1, rafta: Math.floor(rast() * 5),
    etiket: etiketler[Math.floor(rast() * 4)],
  }));
}

// --- Senaryo 1: katalog onbellegi. Ayni kayit iki yapiyla tutulur.
const kayitlar = kitaplar(400);
let dizgiBayt = 0, karmaBayt = 0, dizgiYazma = 0, karmaYazma = 0;
for (const k of kayitlar) {
  const anahtar = `kitap:${k.kitapId}`;
  const govde = JSON.stringify(k);
  dizgiBayt += b(anahtar) + b(govde) + EK_GIRIS;   // tek dizgi: govde butun hâlinde
  karmaBayt += b(anahtar) + EK_GIRIS +             // karma yapi: alan alan
    Object.entries(k).reduce((t, [ad, d]) => t + alanBayt(ad, d), 0);
}
for (let i = 0; i < 1000; i += 1) {                // 1000 raf guncellemesi: yalniz `rafta` degisir
  const k = kayitlar[i % kayitlar.length];
  dizgiYazma += b(JSON.stringify(k));              // dizgide butun govde yeniden yazilir
  karmaYazma += SAYI;                              // karmada yalniz o alanin degeri
}
console.log("katalog onbellegi (400 kitap)");
console.log(`  dizgi  : tutulan=${dizgiBayt} bayt  giris basina=${Math.round(dizgiBayt / 400)}` +
  `  1000 guncellemede yazilan=${dizgiYazma} bayt`);
console.log(`  karma  : tutulan=${karmaBayt} bayt  giris basina=${Math.round(karmaBayt / 400)}` +
  `  1000 guncellemede yazilan=${karmaYazma} bayt`);

// --- Senaryo 2: oturum kaydi. Karma yapi + sure sonu; sure sonu etkin oturumu sinirlar.
const DAKIKA = 720, DK_GIRIS = 25, OMUR = 30;      // 12 saat, dakikada 25 giris, 30 dk sure sonu
const oturumAlanlari = { kullaniciId: 1, sube: 1, rol: "uye", sonErisim: 1 };
const oturumBayt = b("oturum:") + 32 + EK_GIRIS +  // anahtar: onek + 32 karakterlik kimlik
  Object.entries(oturumAlanlari).reduce((t, [ad, d]) => t + alanBayt(ad, d), 0);
const canli = new Map();                           // kimlik -> sonGecerlilik (dakika)
let zirveCanli = 0, acilan = 0;
for (let dk = 1; dk <= DAKIKA; dk += 1) {
  for (const [kimlik, son] of canli) if (son <= dk) canli.delete(kimlik);   // sure sonu temizler
  for (let i = 0; i < DK_GIRIS; i += 1) canli.set(`o${acilan++}`, dk + OMUR);
  zirveCanli = Math.max(zirveCanli, canli.size);
}
console.log("oturum kaydi (12 saat)");
console.log(`  giris basina=${oturumBayt} bayt  acilan oturum=${acilan}  zirve canli=${zirveCanli}`);
console.log(`  sure sonu var : tutulan=${zirveCanli * oturumBayt} bayt`);
console.log(`  sure sonu yok : tutulan=${acilan * oturumBayt} bayt` +
  `  (${(acilan / zirveCanli).toFixed(1)} kat)`);
```

```
katalog onbellegi (400 kitap)
  dizgi  : tutulan=46115 bayt  giris basina=115  1000 guncellemede yazilan=90454 bayt
  karma  : tutulan=60623 bayt  giris basina=152  1000 guncellemede yazilan=8000 bayt
oturum kaydi (12 saat)
  giris basina=141 bayt  acilan oturum=18000  zirve canli=750
  sure sonu var : tutulan=105750 bayt
  sure sonu yok : tutulan=2538000 bayt  (24.0 kat)
```

Katalog satırlarındaki takas nettir. Karma yapı 14.508 bayt fazla tutuyor (giriş başına 115
yerine 152), çünkü her alanın adı ve her alanın ek payı ayrı ayrı ödeniyor. Satın aldığı şey
ikinci sütundadır: bin raf güncellemesinde dizgi biçimi 90.454 bayt yeniden yazarken karma yapı
8.000 bayt yazıyor. Kayıt bütün hâlinde tutulduğunda tek bir sayının değişmesi bütün gövdenin
yeniden yazılması demektir; ayrıca gövdenin çözülüp yeniden kurulması gerekir.

Seçim, kaydın nasıl kullanıldığına bağlıdır. Kayıt her zaman bütün hâlinde okunuyor ve seyrek
güncelleniyorsa dizgi biçimi daha az bayt tutar ve tek erişimde biter. Alanları ayrı ayrı okunuyor
ya da güncelleniyorsa karma yapının fazladan 37 baytı, her güncellemede kazanılan 82 bayt yazmayla
karşılanır.

Oturum satırlarındaki takas başka bir eksende. Giriş başına 141 bayt ile on iki saatte açılan
18.000 oturumun tamamı tutulsa 2.538.000 bayt gerekirdi. Süre sonu, aynı anda canlı olan oturumu
750'de tutuyor ve maliyeti 105.750 bayta indiriyor: yirmi dört kat. Burada bellek, kullanıcı
sayısıyla değil **oturum ömrüyle** ölçeklenir. Kaybedilen şey de bellidir: süresi dolan oturumun
verisi geri getirilemez, kullanıcı yeniden kimlik doğrular (**BK14**).

#### Hız Sınırı ve Sıralama Tablosu

Son iki senaryo aynı sorunun iki ucudur: bir sayının kendisi yeterli mi, yoksa sayıyı oluşturan
tek tek girişler de gerekli mi. Hız sınırında sayaç yalnız pencerenin toplamını tutar; sıralı
küme her isteğin anını ayrı bir üye olarak tutar. Sıralama tablosunda karma yapı yalnız ödünç
sayılarını tutar; sıralı küme bunların üstüne sıralı bir dizin taşır.

```js
// senaryo-hiz-siralama.mjs — hiz siniri ve siralama tablosu: hangi yapi, kac bayt, kac giris
const EK_GIRIS = 16, EK_ALAN = 8, EK_DIZIN = 24, SAYI = 8;   // ayni maliyet modeli
const b = (s) => Buffer.byteLength(String(s));

// --- Senaryo 3: hiz siniri. Sayac pencerenin toplamini, sirali kume tek tek anlari tutar.
const KULLANICI = 2000, SINIR = 60;                // dakikada 60 istek
let sayacBayt = 0, kumeBayt = 0;
for (let u = 1; u <= KULLANICI; u += 1) {
  sayacBayt += b(`hiz:${u}:2025073114`) + SAYI + EK_GIRIS;   // anahtarda pencere var
  let uye = b(`hiz:${u}`) + EK_GIRIS;
  for (let i = 0; i < SINIR; i += 1)               // her istek icin bir uye: zaman damgasi + sira
    uye += b(`1753963200000-${i}`) + SAYI + EK_DIZIN;
  kumeBayt += uye;
}
console.log(`hiz siniri (${KULLANICI} kullanici, dakikada ${SINIR} istek)`);
console.log(`  sayac       : tutulan=${sayacBayt} bayt  kullanici basina=${(sayacBayt / KULLANICI).toFixed(1)}` +
  `  giris=${KULLANICI}`);
console.log(`  sirali kume : tutulan=${kumeBayt} bayt  kullanici basina=${(kumeBayt / KULLANICI).toFixed(1)}` +
  `  giris=${KULLANICI * SINIR}  (${(kumeBayt / sayacBayt).toFixed(1)} kat)`);

// --- Senaryo 4: siralama tablosu. Ayni sayaclar; fark, sirali dizinin tutulup tutulmadigi.
function oduncSayilari(adet) {
  let tohum = 20250731;
  const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
  return Array.from({ length: adet }, (_, i) => [`Kitap ${i + 1}`, Math.floor(rast() * 900) + 1]);
}
const sayimlar = oduncSayilari(400);
let karmaBayt = b("populer") + EK_GIRIS, kumeSiraBayt = b("populer") + EK_GIRIS;
for (const [uye] of sayimlar) {
  karmaBayt += b(uye) + SAYI + EK_ALAN;            // yalniz ad ve sayi
  kumeSiraBayt += b(uye) + SAYI + EK_DIZIN;        // ustune sirali dizin dugumu
}

const sirali = [...sayimlar].sort((x, y) => y[1] - x[1] || (x[0] < y[0] ? -1 : 1));
let karmaDokunulan = 0, kumeDokunulan = 0;
const ilkOn = sirali.slice(0, 10);                 // kume: dizinin basindan on giris
kumeDokunulan += ilkOn.length;
karmaDokunulan += sayimlar.length;                 // karma: hepsi taranip siralanir

const hedefSkor = sayimlar[136][1];                // "Kitap 137" kacinci sirada
let karmaUstun = 0;                                // karmada yanit tam taramayla bulunur
for (const [, s] of sayimlar) if (s > hedefSkor) karmaUstun += 1;
karmaDokunulan += sayimlar.length;
let alt = 0, ust = sirali.length - 1, adim = 0;    // sirali dizinde ikili arama
while (alt <= ust) {
  const orta = (alt + ust) >> 1; adim += 1;
  if (sirali[orta][1] > hedefSkor) alt = orta + 1; else ust = orta - 1;
}
kumeDokunulan += adim;
console.log(`siralama tablosu (${sayimlar.length} kitap)`);
console.log(`  karma yapi  : tutulan=${karmaBayt} bayt  ilk on + sira icin dokunulan=${karmaDokunulan}`);
console.log(`  sirali kume : tutulan=${kumeSiraBayt} bayt  ilk on + sira icin dokunulan=${kumeDokunulan}` +
  `  (${(kumeSiraBayt / karmaBayt).toFixed(2)} kat bayt)`);
console.log(`  ilk uc: ${ilkOn.slice(0, 3).map(([a, s]) => `${a}=${s}`).join("  ")}`);
console.log(`  "Kitap 137" skoru=${hedefSkor}; daha yuksek skorlu kitap: karma=${karmaUstun}` +
  ` (400 giris) kume=${alt} (${adim} giris)`);
```

```
hiz siniri (2000 kullanici, dakikada 60 istek)
  sayac       : tutulan=84893 bayt  kullanici basina=42.4  giris=2000
  sirali kume : tutulan=5786893 bayt  kullanici basina=2893.4  giris=120000  (68.2 kat)
siralama tablosu (400 kitap)
  karma yapi  : tutulan=9915 bayt  ilk on + sira icin dokunulan=800
  sirali kume : tutulan=16315 bayt  ilk on + sira icin dokunulan=19  (1.65 kat bayt)
  ilk uc: Kitap 141=900  Kitap 211=900  Kitap 125=897
  "Kitap 137" skoru=533; daha yuksek skorlu kitap: karma=146 (400 giris) kume=146 (9 giris)
```

Hız sınırındaki fark altmış sekiz kattır ve nedeni tek cümleyle söylenebilir: sayaç kullanıcı
başına **bir** giriş tutar, sıralı küme **altmış** giriş tutar. Kullanıcı başına 42,4 bayt yerine
2.893,4 bayt ödenir. Karşılığında satın alınan şey, pencerenin içindeki her isteğin anının
bilinmesidir; sayaç bunu bilmez, yalnız kaç tane olduğunu bilir. Bu bilginin gerekli olup olmadığı
sınırın nasıl işleyeceğine bağlıdır ve o karar bu dersin dışındadır; ölçülen şey **fiyatıdır**.

Sıralama tablosunda oran çok daha küçüktür: 1,65 kat, yani 6.400 bayt. Karşılığı son iki satırda
görünür. İlk on kitabı ve bir kitabın sırasını bulmak karma yapıda 800 giriş dokunmayı gerektiriyor
(iki kez tam tarama), sıralı kümede 19 giriş yetiyor (on giriş baştan okunuyor, sıra dokuz adımlık
ikili aramayla bulunuyor). İki yol da aynı yanıtı veriyor: "Kitap 137"den daha çok ödünç verilen
146 kitap var.

Sıralı kümenin dizin payı bir kez ödenir ama her sorguda geri kazanılır. Katalog 400 değil 400.000
kitap olduğunda karma yapının tam taraması 800.000 girişe çıkarken sıralı kümenin işi 10 + 19
girişte kalır; fazladan ödenen bayt ise girişle doğru orantılı büyür.

#### Dört Senaryo Tek Tabloda

| Senaryo | Yapı | Giriş | Tutulan bayt | Satın alınan | Kaybedilen |
|---|---|---|---|---|---|
| Katalog önbelleği | karma yapı | 400 | 60.623 | alan başına 8 baytlık yazma (dizgide 90 bayt) | dizgiye göre 14.508 bayt |
| Oturum kaydı | karma yapı + süre sonu | 750 canlı | 105.750 | belleğin oturum ömrüyle sınırlanması | süresi dolan oturumun verisi |
| Hız sınırı | sayaç | 2.000 | 84.893 | kullanıcı başına tek giriş | isteklerin tek tek anları |
| Sıralama tablosu | sıralı küme | 400 | 16.315 | ilk on ve sıra sorgusunda 19 giriş | karma yapıya göre 6.400 bayt |

Tablo dört ayrı iş gibi görünüyor ama tek bir karar biçimini tekrarlıyor. Her satırda yapı, o iş
için **hangi bilginin tutulması gerektiği** sorusuna verilen yanıttır. Bilgi ne kadar ayrıntılı
tutulursa erişim o kadar ucuzlar ve bellek o kadar pahalılaşır. Karma yapı alanları ayırdığı için
alan başına yazmayı, sıralı küme sırayı sakladığı için sıra sorgusunu ucuzlatır; sayaç ayrıntıyı
attığı için ucuzdur ve attığı ayrıntı geri gelmez.

#### Özet

- Aynı kaydın dizgi biçimi 46.115, karma biçimi 60.623 bayt tuttu; karma yapının 14.508 baytlık
  fazlası, bin güncellemede 90.454 yerine 8.000 bayt yazmayla karşılandı.
- Oturum kaydında bellek kullanıcı sayısıyla değil oturum ömrüyle ölçeklenir: süre sonu 18.000
  oturumu 750 canlı oturuma indirdi, 2.538.000 bayt yerine 105.750 bayt.
- Hız sınırında sayaç kullanıcı başına 42,4 bayt, sıralı küme 2.893,4 bayt tutar; altmış sekiz
  katlık fark, tek tek istek anlarının saklanmasının fiyatıdır.
- Sıralama tablosunda sıralı kümenin dizin payı 6.400 bayttır ve ilk on ile sıra sorgusunu 800
  giriş yerine 19 girişe indirir; iki yol da aynı yanıtı verir.
- Dört senaryoda da yapı seçimi tek bir soruya bakar: hangi bilgi tutulacak. Ayrıntı tutuldukça
  erişim ucuzlar, bellek pahalılaşır.

#### Sonraki Adım

Bu dersin bütün hesapları deponun **beklendiği gibi** çalıştığını varsaydı: her erişim sırası
geldiğinde yapılıyor, hiçbir istek bir başkasını beklemiyor. Bellek içi depoların çoğu komutları
tek bir iş parçacığında sırayla işler ve bu varsayım orada özel bir biçimde kırılır: 400.000
girişi tarayan tek bir komut, arkasında bekleyen bütün istekleri kendi süresi kadar geciktirir.
Sonraki ders bu gecikmeyi ölçer, bekleyen istek sayısını sayar ve yavaş komut günlüğünün eşiğini
tarayarak sorunlu komutun nasıl görünür kılındığını gösterir.

### Ölçüm ve Yavaş Komut Analizi

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/olcum-ve-yavas-komut-analizi

Tek iş parçacıklı bellek içi deponun başarım tanısı: uzun süren tek bir komutun arkasında bekleyen istek sayısının ve toplam gecikmenin sayılması, taramanın imleçle parçalanmasının kazandırdığı ile kaçırdığı giriş, yavaş komut günlüğü eşiğinin taranması.

Önceki dersin dört bellek hesabı da sessiz bir varsayıma yaslanıyordu: her erişim sırası geldiğinde
yapılıyor, hiçbir istek bir başkasını beklemiyor. Bellek içi depoların çoğunda bu varsayım özel
bir biçimde kırılır, çünkü komutlar tek bir iş parçacığında sırayla işlenir. Sıra, atomikliği
bedava getiren şeydir: iki komut hiçbir zaman iç içe geçmez, kilit gerekmez.

Aynı sıra bir tuzağı da getirir. Bir komut ne kadar sürerse arkasındaki bütün istekler o kadar
bekler. Diskteki bir veritabanında yavaş bir sorgu kendi istemcisini geciktirir; burada **bütün
istemcileri** geciktirir. Bu ders o gecikmeyi sayar.

#### Uzun Komutun Arkasında Bekleyenler

Düzenek tek kuyruklu ve tek iş parçacıklıdır. İstekler her iki iş biriminde bir gelir, sıradan bir
komut bir birim sürer; yük yarı doludur (**BK15**). Bir noktada bütün katalog önbelleğini tarayan
bir bakım komutu araya girer ve toplam 4.000 birimlik iş getirir. Aynı iş bir kez tek komut olarak,
sonra parçalara bölünmüş komutlar olarak verilir. Toplam iş her satırda aynıdır.

```js
// tek-parcacik.mjs — tek is parcacikli depo: uzun komut arkasinda bekleyen istekler sayilir
const GELIS = 2, ISTEK = 3000, TARAMA_ANI = 2000;  // her 2 birimde bir istek, komut maliyeti 1 birim

function kosum(toplamIs, parca) {                  // tarama isi `parca` komuta bolunur
  const parcaMaliyet = parca ? toplamIs / parca : 0;
  const sira = [];                                 // tek kuyruk, tek is parcacigi, sirayla
  let gelen = 0, kalanParca = parca, taramaGelisi = parca ? TARAMA_ANI : -1;
  let calisan = null, bitisAni = 0;
  let bekleyen = 0, enUzun = 0, toplamBekleme = 0, taramaBitis = 0, islenen = 0;

  for (let t = 0; islenen < ISTEK + parca && t < 200000; t += 1) {
    if (calisan && bitisAni === t) {               // 1) calisan komut bitti
      if (calisan.tarama) { taramaBitis = t; if (kalanParca > 0) taramaGelisi = t; }
      calisan = null; islenen += 1;
    }
    while (gelen < ISTEK && (gelen + 1) * GELIS === t) {             // 2) normal istek gelisi
      sira.push({ gelis: t, maliyet: 1, tarama: false }); gelen += 1;
    }
    if (kalanParca > 0 && taramaGelisi === t) {    // tarama parcasi: ilki takvimle, sonrakiler
      sira.push({ gelis: t, maliyet: parcaMaliyet, tarama: true });  // oncekinin bitisinde
      kalanParca -= 1; taramaGelisi = -1;
    }
    if (calisan === null && sira.length > 0) {     // 3) sirada bekleyen ilk komut alinir
      calisan = sira.shift();
      const b = t - calisan.gelis;
      if (b > 0) { bekleyen += 1; toplamBekleme += b; enUzun = Math.max(enUzun, b); }
      bitisAni = t + calisan.maliyet;
    }
  }
  return { parcaMaliyet, bekleyen, enUzun, toplamBekleme, taramaBitis };
}

console.log(`is yuku: ${ISTEK} istek, ${GELIS} birimde bir gelis, komut maliyeti 1 birim`);
console.log(["tarama", "parca maliyeti", "bekleyen istek", "en uzun bekleme", "toplam bekleme",
  "tarama bitisi"].map((b, i) => (i === 0 ? b.padEnd(18) : b.padStart(16))).join(""));
for (const [ad, toplam, parca] of [
  ["tarama yok", 0, 0], ["tek komut (4000)", 4000, 1],
  ["10 parca", 4000, 10], ["40 parca", 4000, 40], ["200 parca", 4000, 200],
]) {
  const r = kosum(toplam, parca);
  console.log(ad.padEnd(18) + [r.parcaMaliyet, r.bekleyen, r.enUzun, r.toplamBekleme, r.taramaBitis]
    .map((n) => String(n).padStart(16)).join(""));
}
```

```
is yuku: 3000 istek, 2 birimde bir gelis, komut maliyeti 1 birim
tarama              parca maliyeti  bekleyen istek en uzun bekleme  toplam bekleme   tarama bitisi
tarama yok                       0               0               0               0               0
tek komut (4000)              4000            2001            3999         5999001            6001
10 parca                       400            2007             786         1045801            8001
40 parca                       100            2022             198          289501            8001
200 parca                       20            2102              38           58681            8001
```

İlk satır ölçünün sıfır noktasıdır: tarama olmadığında hiçbir istek beklemez, çünkü gelme hızı
hizmet hızının yarısıdır. İkinci satırda tek bir komut 4.000 birim çalışır ve arkasında 2.001
istek birikir. En uzun bekleme 3.999 birimdir; yani sıraya taramanın hemen ardından giren istek,
kendi işi bir birim sürdüğü hâlde neredeyse dört bin birim bekler. Toplam bekleme 5.999.001
birimdir.

Aşağıdaki satırlarda **aynı 4.000 birimlik iş** parçalara bölünüyor. Parça maliyeti 4.000'den
400'e indiğinde en uzun bekleme 3.999'dan 786'ya, toplam bekleme 5.999.001'den 1.045.801'e düşüyor.
200 parçada en uzun bekleme 38 birim, toplam bekleme 58.681 birimdir: yüzde birinden azı. Yapılan
iş bir birim bile azalmadı; yalnız **bölünebilir** hâle geldi.

Son sütun bedeli gösteriyor. Tek komutta tarama 6.001. birimde bitiyor, parçalıda 8.001. birimde.
Parçalı tarama kendi bitiş süresinden vazgeçerek başkalarının beklemesini satın alır. Tek iş
parçacıklı bir depoda bu takas her uzun iş için yeniden yapılır: taramanın ne zaman biteceği
genellikle kimseyi ilgilendirmez, arada bekleyen üç bin istek ilgilendirir.

#### İmleçli Taramanın Kaçırdığı

Parçalama bedava değildir ve bedeli yalnız gecikmiş bitiş değildir. Tek komut bütün taramayı iş
parçacığını elinde tutarak yapar, dolayısıyla anahtar alanının değişmeyen bir hâlini görür. İmleçle
yürüyen tarama parçalar arasında iş parçacığını bırakır ve o aralıklarda anahtar alanı değişir.
Aşağıdaki koşum bunu 200.000 girişlik bir anahtar alanında ölçer; her parçadan sonra iki kitap
ödünç verilir ve önbellek girdisi silinir.

```js
// imlecli-tarama.mjs — ayni tarama: tek blokta mi, imlecle parca parca mi
function anahtarAlani(adet) {                      // ekleme sirasini koruyan anahtar listesi
  const depo = new Map();
  const anahtarlar = [];
  for (let i = 1; i <= adet; i += 1) {
    const a = `kitap:${i}`;
    depo.set(a, { rafta: i % 5, sube: (i % 3) + 1 });
    anahtarlar.push(a);
  }
  return { depo, anahtarlar };
}

const ADET = 200000, PARCA = 1000, SILME = 2;      // parca basina 2 silme (odunc verilen kitaplar)

function tekBlok() {                               // komut is parcacigini bastan sona tutar
  const { depo, anahtarlar } = anahtarAlani(ADET);
  let gorulen = 0, eslesen = 0;
  for (const a of anahtarlar) { gorulen += 1; if (depo.get(a).rafta === 0) eslesen += 1; }
  return { ad: "tek blok", enUzunBlok: gorulen, gorulen, eslesen, komut: 1, kacirilan: 0 };
}

function imlecli() {                               // her parca ayri bir komut; arada baskalari calisir
  const { depo, anahtarlar } = anahtarAlani(ADET);
  const ziyaret = new Set();
  let p = 0, gorulen = 0, eslesen = 0, komut = 0, silinen = 0;
  while (p < anahtarlar.length) {
    const son = Math.min(p + PARCA, anahtarlar.length);
    for (let i = p; i < son; i += 1) {             // bu parcanin isi
      const a = anahtarlar[i];
      ziyaret.add(a); gorulen += 1;
      if (depo.get(a).rafta === 0) eslesen += 1;
    }
    p = son; komut += 1;
    for (let s = 0; s < SILME && p < anahtarlar.length; s += 1) {   // parcalar arasinda silme olur
      const yer = (silinen * 977) % p;             // imlecten once bir anahtar silinir
      depo.delete(anahtarlar[yer]);
      anahtarlar.splice(yer, 1);                   // liste kayar: imlecteki giris atlanir
      silinen += 1;
    }
  }
  const kacirilan = anahtarlar.filter((a) => !ziyaret.has(a)).length;   // sonuna kadar duran ama gorulmeyen
  return { ad: `imlecli (${PARCA})`, enUzunBlok: PARCA, gorulen, eslesen, komut, kacirilan };
}

console.log(`anahtar alani=${ADET}  parca=${PARCA}  parca basina silme=${SILME}`);
console.log(["yol", "komut", "en uzun blok", "gorulen giris", "eslesen", "kacirilan"]
  .map((b, i) => (i === 0 ? b.padEnd(16) : b.padStart(15))).join(""));
for (const r of [tekBlok(), imlecli()]) {
  console.log(r.ad.padEnd(16) + [r.komut, r.enUzunBlok, r.gorulen, r.eslesen, r.kacirilan]
    .map((n) => String(n).padStart(15)).join(""));
}
```

```
anahtar alani=200000  parca=1000  parca basina silme=2
yol                       komut   en uzun blok  gorulen giris        eslesen      kacirilan
tek blok                      1         200000         200000          40000              0
imlecli (1000)              200           1000         199602          39921            398
```

Tek blok 200.000 girişi kesintisiz gezer ve tam sayıyı verir: 40.000 eşleşme, sıfır kaçırılan
giriş. İmleçli tarama en uzun bloğu 1.000 girişe indirir — az önceki ölçümde beklemeyi iki
mertebe düşüren şey buydu — ama 398 giriş hiç görülmez. Bu girişler tarama boyunca varlığını
sürdürmüştü; imlecin önünden bir anahtar silindiğinde liste kaydığı için atlandılar.

Bu, tek iş parçacıklı deponun tanı tablosundaki asıl ödünleşimdir. Kesintisiz tarama **tam** yanıt
verir ve herkesi bekletir; imleçli tarama kimseyi bekletmez ve **yaklaşık** yanıt verir. Bir bakım
işi için yaklaşık yanıt genellikle yeterlidir, bir sayım raporu için değildir.

#### Yavaş Komut Günlüğünün Eşiği

Yukarıdaki ölçümler düzeneğin içini görebildiği için yapılabildi. Çalışan bir depoda hangi komutun
uzun sürdüğü doğrudan görünmez; bunun için **yavaş komut günlüğü** tutulur: süresi bir eşiğin
üstünde kalan komutlar son N kaydı tutan bir halka tampona yazılır. Günlüğün kendisi de bellekten
yenir, dolayısıyla eşik iki şeyi birden belirler: ne görüleceğini ve kaç bayt tutulacağını.
Aşağıdaki koşum 128 kayıtlık bir halkayla beş eşiği tarar (**BK16**).

```js
// yavas-komut-gunlugu.mjs — esik taramasi: hangi esik sucluyu gosterir, gunluk kac bayt tutar
const HALKA = 128, EK_GIRIS = 16;                  // gunluk halka tampondur: son 128 kayit

function komutlar(adet) {                          // belirlenimli karisim, tohum gorunur
  let tohum = 20250731;
  const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
  return Array.from({ length: adet }, (_, i) => {
    if (i === 1500) return { ad: "tarama", maliyet: 4000 };         // sucluyu ariyoruz
    const r = rast();
    if (r < 0.95) return { ad: "oku", maliyet: 1 };
    if (r < 0.99) return { ad: "kume-uye", maliyet: 5 + Math.floor(rast() * 36) };
    return { ad: "sirali-kesit", maliyet: 200 };
  });
}

const akis = komutlar(3000);
const kayitBayt = (k, sira) =>                     // gunluk kaydi: komut adi, maliyet, sira
  Buffer.byteLength(JSON.stringify({ ad: k.ad, maliyet: k.maliyet, sira })) + EK_GIRIS;

console.log(`komut=${akis.length}  halka=${HALKA} kayit  toplam is=` +
  `${akis.reduce((t, k) => t + k.maliyet, 0)} birim`);
console.log(["esik", "kaydedilen", "halkada kalan", "tutulan bayt", "tarama halkada mi"]
  .map((b, i) => (i === 0 ? b.padEnd(8) : b.padStart(18))).join(""));
for (const esik of [1, 5, 20, 100, 1000]) {
  const halka = [];
  let kaydedilen = 0;
  akis.forEach((k, i) => {
    if (k.maliyet < esik) return;                  // esigin altindaki komut gunluge girmez
    kaydedilen += 1;
    halka.push({ ...k, sira: i, bayt: kayitBayt(k, i) });
    if (halka.length > HALKA) halka.shift();       // en eski kayit dusurulur
  });
  const bayt = halka.reduce((t, k) => t + k.bayt, 0);
  const var_ = halka.some((k) => k.maliyet === 4000) ? "evet" : "hayir";
  console.log(String(esik).padEnd(8) +
    [kaydedilen, halka.length, bayt, var_].map((n) => String(n).padStart(18)).join(""));
}
```

```
komut=3000  halka=128 kayit  toplam is=17267 birim
esik            kaydedilen     halkada kalan      tutulan bayt tarama halkada mi
1                     3000               128              6719             hayir
5                      159               128              7546              evet
20                     108               108              6422              evet
100                     40                40              2502              evet
1000                     1                 1                58              evet
```

Son sütun eşik seçiminin neden bir ölçüm kararı olduğunu gösteriyor. Eşik 1 iken günlük her komutu
kaydediyor: 3.000 kayıt üretiliyor, ama halka yalnız son 128'ini tutabildiği için 4.000 birimlik
tarama **günlükten düşmüş**. Bütün komutları kaydeden bir günlük, aranan komutu kaydetmeyen bir
günlüktür.

Eşik 5'te kayıt sayısı 159'a iniyor ve tarama halkada kalıyor. Eşik 20'de kayıt sayısı halkanın
altına düşüyor (108) ve artık hiçbir kayıt kaybolmuyor; günlük 6.422 bayt tutuyor. Eşik 1.000'de
tek kayıt kalıyor: suçlu görünür ama 200 birimlik sıralı kesit komutlarının varlığı görünmez —
oysa toplam 17.267 birimlik işin neredeyse yarısı onlardan geliyor.

Kullanılabilir eşik, kayıt sayısını halka boyunun altında tutan ve normal komutları dışarıda
bırakan aralıktır. Bu aralık iş yükünden okunur, önceden bilinemez: eşik önce geniş tutulup kayıt
sayısı halka boyunu aşıyorsa daraltılır. Günlüğün bütçesi de aynı tablodadır — 128 kayıt en fazla
7.546 bayt tuttu — ve bu bütçe, sorunun görünür olmasını satın alır.

#### Özet

- Tek iş parçacıklı depoda uzun komut yalnız kendi istemcisini geciktirmez: 4.000 birimlik tek
  komut arkasında 2.001 istek bekletti, en uzun bekleme 3.999, toplam bekleme 5.999.001 birim oldu.
- Aynı iş 200 parçaya bölündüğünde en uzun bekleme 38, toplam bekleme 58.681 birime indi; yapılan
  iş değişmedi, yalnız bölünebilir hâle geldi.
- Parçalamanın bedeli iki kalemdir: tarama 6.001 yerine 8.001. birimde bitti ve imleç önünden
  silinen anahtarlar yüzünden 398 giriş hiç görülmedi (tek blokta 0).
- Yavaş komut günlüğünde eşik hem ne görüleceğini hem kaç bayt tutulacağını belirler: eşik 1'de
  3.000 kayıt üretildi ve aranan komut 128 kayıtlık halkadan düştü.
- Kullanılabilir eşik kayıt sayısını halka boyunun altında tutandır; eşik 20'de 108 kayıt, 6.422
  bayt ve suçlu komut günlükte kaldı.

#### Sonraki Adım

Bu kursun bütün ölçümleri deponun yalnız kendi istemcileriyle konuştuğunu varsaydı: yayımlayan
taraf tanınan bir uygulama, tarayan komut bir bakım işi, ölçülen gecikme kendi iş yükünün
gecikmesiydi. Oysa şimdiye kadar sayılan her maliyet, komutu kimin gönderdiği sorulmadan ödendi.
Bütün anahtar alanını silen bir komutun, bütün belleği dolduran bir yazma dizisinin ya da yavaş
abone olup tamponu şişiren bir bağlantının önünde hiçbir engel kurulmadı. Sonraki ders bu boşluğu
ele alır: varsayılan olarak açık bir kurulumun yabancıya ne kadar yüzey açtığını, komut
kısıtlamasının neyi kapattığını ve şifrelemenin hangi maliyetle geldiğini ölçer.

### Güvenlik

Source: https://academia.sh/tr/kurslar/bellek-ici-depolar/guvenlik

Deponun yapılandırması bir güvenlik kararıdır: kimlik doğrulaması kapalı varsayılan kurulumda yabancıya açılan anahtar, bayt ve gizli bayt sayısının ölçülmesi, komut kısıtlamasının sunucu tarafı betikleme açık bırakıldığında kapattığı yıkıcı yüzeyi bütünüyle geri açtığının gösterilmesi, ağ yalıtımının yabancıyı yüzeyin dışına çıkarması ve şifrelemenin bedelinin bağlantı başına ek adım, çerçeve başına ek bayt ve bağlantı başına tampon olarak sayılıp doksan eş zamanlı bağlantıdan sonra tamponun verinin kendisini geçtiğinin hesaplanması.

Bu kursun bütün ölçümleri deponun yalnız kendi istemcileriyle konuştuğunu varsaydı; şimdiye kadar
sayılan her maliyet, komutu kimin gönderdiği sorulmadan ödendi. Bu ders o boşluğu kapatır ve
kimlik mekaniğini değil **deponun yapılandırmasını** ele alır: kim bağlanabiliyor, bağlanan hangi
komut sınıflarını çalıştırabiliyor ve bu kararların bellek bütçesindeki karşılığı ne.

#### Yapılandırma Bir Yüzeydir

Varsayılan kurulum sade tutulur: kimlik doğrulaması kapalı, bütün ağ arayüzlerinde dinleyen ve her
komut sınıfını kabul eden bir süreç. Bu kurulumun ölçülebilir bir yüzeyi vardır ve ölçüsü anahtar
sayısıyla bayttır.

Yüzeyi belirleyen üç karar birbirinin yerine geçmez. **Kimlik doğrulaması** kimin
bağlanabildiğine, **komut kısıtlaması** bağlananın ne yapabildiğine, **ağ yalıtımı** ise bağlantı
denemesinin depoya hiç ulaşıp ulaşmadığına karar verir. Dördüncü karar olan **şifreleme** yüzeyi
daraltmaz; aktarımdaki veriyi okunmaz kılar ve tek maliyet kalemi odur.

#### Düzenek

Düzenek bir **modeldir**: gerçek depo, ağ ya da bağlantı kurulmaz, erişim bir sayıdır. Komut
**adları** değil komut **sınıfları** sayılır; adım soyut bir gidiştir, ölçülmüş süre değildir.

**BK17 — anahtar alanı katalog (3.000 × 240 bayt), oturum (8.000 × 120), ödünç (12.000 × 96),
kuyruk (800 × 128) ve şube sayaçlarından (6 × 64) oluşur; oturum, ödünç ve kuyruk gizlidir.**
Gerekçe: bu üç alan okur kimliğine bağlı veri taşır, katalog ile sayaçlar taşımaz.
**BK18 — bağlantı başına şifreleme tamponu 32.768 bayt, çerçeve başına ek 29 bayt, bağlantı
başına ek adım 3'tür (el sıkışma 2 + kimlik 1).** Gerekçe: üç kalem üç ayrı ölçeğe bağlıdır —
tampon bağlantı başına ayrılır, ek bayt her çerçevede yinelenir, el sıkışma yalnız bağlantı
kurulurken ödenir. **BK19 — sunucu tarafı betikleme açıkken çağıranın erişebildiği her adım betik
içinden de yürütülebilir.** Gerekçe: betik ayrı bir yetki alanı değildir.

```js
// guvenlik/olc.mjs — acik yuzey ve yapilandirma bedeli SUREC ICI MODELDIR. Gercek depo, ag ya da
// baglanti kurulmaz; erisim bir sayidir. Komut ADLARI degil komut SINIFLARI sayilir. Adim soyut
// bir gidistir, olculmus sure degildir.
const ALAN = [                       // [ad, giris, giris bayti, gizli mi]
  ["katalog", 3000, 240, false], ["oturum", 8000, 120, true], ["odunc", 12000, 96, true],
  ["kuyruk", 800, 128, true], ["sayac", 6, 64, false]];
const SINIF = [                      // [ad, yikici mi, betik mi]
  ["okuma", false, false], ["yazma", false, false], ["silme", true, false],
  ["alan bosaltma", true, false], ["yapilandirma", true, false], ["betik", true, true]];
const TAMPON = 32768, CERCEVE = 29, ELSIKISMA = 2, KIMLIK_ADIM = 1;
const ROL = 4, ROL_BAYT = 128, IZIN_BAYT = 24;

const anahtar = ALAN.reduce((a, [, n]) => a + n, 0);
const bayt = ALAN.reduce((a, [, n, b]) => a + n * b, 0);
const gizli = ALAN.reduce((a, [, n, b, g]) => a + (g ? n * b : 0), 0);

// yapilandirma: [kimlik dogrulamasi, yikici siniflar acik, betik acik, ag yalitimi, sifreleme]
const AYAR = [
  ["varsayilan", false, true, true, false, false], ["kimlik", true, true, true, false, false],
  ["kisit+betik", true, false, true, false, false], ["kisit", true, false, false, false, false],
  ["yalitim", true, false, false, true, false], ["yalitim+sifreleme", true, false, false, true, true]];

function yuzey([, kimlik, yikici, betik, yalitim]) {
  const acik = SINIF.filter(([, y, b]) => (y ? yikici : true) && (b ? betik : true));
  // Yabanci: kimligi dogrulanmamis ag cagrisi. Yalitim varsa hic ulasamaz.
  const yabanciAcik = yalitim || kimlik ? 0 : 1;
  // Uygulama: kimligi dogrulanmis, kisitli rol. Betik acikken kapatilan siniflar geri acilir.
  const uygulamaYikici = yikici || betik ? bayt : 0;
  return { acik: acik.length, yabanciAnahtar: yabanciAcik * anahtar, yabanciBayt: yabanciAcik * bayt,
           yabanciGizli: yabanciAcik * gizli, uygulamaYikici };
}

const s = (x, n) => String(x).padStart(n);
const yuz = (x, n) => s((x * 100).toFixed(1) + "%", n);
console.log(`Anahtar alani: ${anahtar} anahtar, ${bayt} bayt; bunun ${gizli} bayti gizli`);
console.log("(oturum, odunc, kuyruk). Alti komut sinifi: okuma, yazma, silme, alan bosaltma,");
console.log("yapilandirma, betik. Yabanci = kimligi dogrulanmamis ag cagrisi.\n");
console.log("yapilandirma      | acik sinif | yabanci anahtar | yabanci bayt | gizli bayt | uygulamanin yikabildigi");
console.log("------------------|------------|-----------------|--------------|------------|------------------------");
for (const a of AYAR) {
  const r = yuzey(a);
  console.log(`${a[0].padEnd(17)} | ${s(r.acik + "/6", 10)} | ${s(r.yabanciAnahtar, 15)} | ` +
    `${s(r.yabanciBayt, 12)} | ${s(r.yabanciGizli, 10)} | ${s(r.uygulamaYikici, 18)} bayt`);
}

const ISTEK = 200000;
console.log(`\nAdim bedeli (${ISTEK} istek; el sikisma ${ELSIKISMA} + kimlik ${KIMLIK_ADIM} = 3 adim/baglanti):`);
console.log("baglanti basina istek | baglanti | ek adim | istek basina ek adim | cerceve ek bayti");
console.log("----------------------|----------|---------|----------------------|-----------------");
for (const k of [1, 10, 100, 1000]) {
  const b = Math.ceil(ISTEK / k), ek = b * (ELSIKISMA + KIMLIK_ADIM);
  console.log(`${s(k, 21)} | ${s(b, 8)} | ${s(ek, 7)} | ${s((ek / ISTEK).toFixed(3), 20)} | ` +
    `${s(ISTEK * 2 * CERCEVE, 11)} bayt`);
}

console.log("\nSifrelemenin BELLEK bedeli (baglanti basina 16 KiB okuma + 16 KiB yazma tamponu):");
console.log("es zamanli baglanti | tampon bayt | veri bayt | tampon / veri | rol+izin tablosu");
console.log("--------------------|-------------|-----------|---------------|-----------------");
for (const n of [8, 32, 128, 512]) {
  console.log(`${s(n, 19)} | ${s(n * TAMPON, 11)} | ${s(bayt, 9)} | ${yuz(n * TAMPON / bayt, 13)} | ` +
    `${s(ROL * ROL_BAYT + ROL * SINIF.length * IZIN_BAYT, 11)} bayt`);
}

const defter = ROL * ROL_BAYT + ROL * SINIF.length * IZIN_BAYT;
console.log("\nkosumdan bagimsiz nicelikler:");
console.log("  cerceve ek bayti  = istek x 2 x " + CERCEVE + " bayt; baglanti sayisindan bagimsiz");
console.log("  istek basina adim = 3 / (baglanti basina istek)");
console.log("  sifreleme tamponu = es zamanli baglanti x " + TAMPON + " bayt");
console.log("  rol+izin defteri  = " + ROL + "x" + ROL_BAYT + " + " + ROL + "x" + SINIF.length + "x" +
  IZIN_BAYT + " = " + defter + " bayt; baglantidan bagimsiz");
console.log("  tampon veriyi gecer: ceil(" + bayt + " / " + TAMPON + ") = " + Math.ceil(bayt / TAMPON) +
  " es zamanli baglanti");
```

```
Anahtar alani: 23806 anahtar, 2934784 bayt; bunun 2214400 bayti gizli
(oturum, odunc, kuyruk). Alti komut sinifi: okuma, yazma, silme, alan bosaltma,
yapilandirma, betik. Yabanci = kimligi dogrulanmamis ag cagrisi.

yapilandirma      | acik sinif | yabanci anahtar | yabanci bayt | gizli bayt | uygulamanin yikabildigi
------------------|------------|-----------------|--------------|------------|------------------------
varsayilan        |        6/6 |           23806 |      2934784 |    2214400 |            2934784 bayt
kimlik            |        6/6 |               0 |            0 |          0 |            2934784 bayt
kisit+betik       |        2/6 |               0 |            0 |          0 |            2934784 bayt
kisit             |        2/6 |               0 |            0 |          0 |                  0 bayt
yalitim           |        2/6 |               0 |            0 |          0 |                  0 bayt
yalitim+sifreleme |        2/6 |               0 |            0 |          0 |                  0 bayt

Adim bedeli (200000 istek; el sikisma 2 + kimlik 1 = 3 adim/baglanti):
baglanti basina istek | baglanti | ek adim | istek basina ek adim | cerceve ek bayti
----------------------|----------|---------|----------------------|-----------------
                    1 |   200000 |  600000 |                3.000 |    11600000 bayt
                   10 |    20000 |   60000 |                0.300 |    11600000 bayt
                  100 |     2000 |    6000 |                0.030 |    11600000 bayt
                 1000 |      200 |     600 |                0.003 |    11600000 bayt

Sifrelemenin BELLEK bedeli (baglanti basina 16 KiB okuma + 16 KiB yazma tamponu):
es zamanli baglanti | tampon bayt | veri bayt | tampon / veri | rol+izin tablosu
--------------------|-------------|-----------|---------------|-----------------
                  8 |      262144 |   2934784 |          8.9% |        1088 bayt
                 32 |     1048576 |   2934784 |         35.7% |        1088 bayt
                128 |     4194304 |   2934784 |        142.9% |        1088 bayt
                512 |    16777216 |   2934784 |        571.7% |        1088 bayt

kosumdan bagimsiz nicelikler:
  cerceve ek bayti  = istek x 2 x 29 bayt; baglanti sayisindan bagimsiz
  istek basina adim = 3 / (baglanti basina istek)
  sifreleme tamponu = es zamanli baglanti x 32768 bayt
  rol+izin defteri  = 4x128 + 4x6x24 = 1088 bayt; baglantidan bagimsiz
  tampon veriyi gecer: ceil(2934784 / 32768) = 90 es zamanli baglanti
```

#### Açık Yüzeyin Okunması

**Varsayılan kurulumda yüzey veri kümesinin tamamıdır.** İlk satır 23.806 anahtar ve 2.934.784
bayt gösteriyor; bunun 2.214.400 baytı hangi okurun neyi ödünç aldığı bilgisidir. Altı komut
sınıfının altısı da açıktır: aynı çağrı bu baytları okumakla kalmaz, anahtar alanını boşaltabilir.

**Kimlik doğrulaması yabancıyı çıkarır, uygulamayı sınırlamaz.** İkinci satırda yabancı sütunları
sıfıra iner ama son sütun değişmez: 2.934.784 bayt hâlâ yıkılabilir. Kimlik doğrulaması "kim"
sorusunu yanıtlar, "ne yapabilir" sorusunu yanıtlamaz. Katalog güncelleyen toplu iş ile ödünç
kaydı yazan uygulama aynı kimlikle bağlanıyorsa, birindeki hata bütün anahtar alanını götürür.

**Komut kısıtlaması betikleme açıkken hiçbir şey kapatmaz.** Üçüncü satır dersin asıl bulgusudur:
açık sınıf 6'dan 2'ye inmiş, yıkıcı sınıflar kapatılmıştır, ama uygulamanın yıkabildiği bayt
2.934.784'te kalır. Sunucu tarafı betikleme çağıranın adımlarını yürütür; kapı kapatılmış, pencere
açık bırakılmıştır. Dördüncü satırda betikleme de kısıtlandığında yıkıcı yüzey sıfırlanır.

**Ağ yalıtımı bir sonraki katmandır, yerine geçen değil.** Beşinci satır dördüncüden farklı bir
sayı üretmiyor; yine de yalıtım, kimlik doğrulaması devre dışı kaldığında ilk satıra dönülmesini
engeller. Altıncı satır da şifrelemenin yüzeyi hiç daraltmadığını gösteriyor.

#### Kimliğin Ucuzluğu, Şifrelemenin Pahalılığı

İkinci ve üçüncü tablolar faturayı kalem kalem yazıyor ve üç kalem üç ayrı ölçeğe bağlı.

**Rol ve izin defteri pratik olarak bedavadır: 1.088 bayt**, üstelik bağlantı sayısından
bağımsızdır. Yüzeyi 2.934.784 bayttan sıfıra indiren karar, bellek bütçesinde binde birlik bir
yer tutmaz.

**El sıkışma bağlantı başınadır, bu yüzden bedeli yeniden kullanımla erir.** Bağlantı başına tek
istek düştüğünde 200.000 bağlantı ve 600.000 ek adım ödenir — istek başına 3 adım, işin
kendisinden pahalı. Bağlantı başına bin istekte aynı kalem 600 adıma, istek başına 0,003'e iner.
Şifrelemenin adım maliyeti bir şifreleme sorunu değil, bağlantı yönetimi sorunudur.

**Çerçeve eki yeniden kullanımdan etkilenmez:** 200.000 istek için 11.600.000 bayt, dört satırda
da aynı.

**Asıl bellek kalemi bağlantı tamponudur.** Tampon 8 eş zamanlı bağlantıda verinin yüzde 8,9'u,
128 bağlantıda yüzde 142,9'u, 512 bağlantıda yüzde 571,7'sidir. Koşumdan bağımsız satır dönüm
noktasını veriyor — **90 eş zamanlı bağlantıdan sonra şifreleme tamponları saklanan verinin
kendisinden fazla yer tutar.** Bellek içi bir depoda bu, tahliye politikasının ne zaman devreye
gireceğini değiştiren bir sayıdır.

#### Özet

- Varsayılan kurulumun yüzeyi veri kümesinin tamamıdır: 23.806 anahtar, 2.934.784 bayt
  (2.214.400'ü gizli) ve altı komut sınıfının altısı da açıktır.
- Kimlik doğrulaması yabancının eriştiği baytı sıfırlar ama doğrulanmış çağrının yıkabildiği
  2.934.784 baytı bırakır; iki karar ayrıdır.
- Komut kısıtlaması betikleme açıkken yıkıcı yüzeyi hiç küçültmedi: açık sınıf 6'dan 2'ye indiği
  hâlde yıkılabilir bayt 2.934.784'te kaldı, betikleme kısıtlanınca sıfırlandı.
- Rol ve izin defteri 1.088 bayttır ve bağlantı sayısından bağımsızdır: yüzeyi sıfırlayan karar
  bellekte yer tutmaz.
- Şifrelemenin adım bedeli yeniden kullanımla erir (istek başına 3,000 → 0,003) ama çerçeve eki
  sabittir: 200.000 istek için 11.600.000 bayt.
- Şifrelemenin bellek bedeli bağlantı başınadır; 90 bağlantıdan sonra tamponlar saklanan veriyi
  geçer, 512 bağlantıda oran yüzde 571,7'dir.

#### Kurs Kapanışı

| Ders | Tutulan bayt | Satın alınan | Kaybedilen |
|---|---|---|---|
| Anahtar–Değer Temelleri | 20.000 giriş, 2.397.230 bayt (120 bayt/giriş) | süre sonu zirveyi 262.936 bayta indirdi | 16.951 fazladan yeniden giriş |
| Dizgiler ve Sayaçlar | 200.000 sayaç, 15.545.901 bayt (sayı 77,7 bayt) | sorgu 119.993 → 1 adım; atomik artırmada kayıp sıfır | bayt başına 77,2 adım (kısmi sayaçta 6.410) |
| Listeler | bağlı düğüm giriş başına 43,0 bayt (dizi 19,0) | işlem 1.673 → 1 adım | sıra sorgusu yine tarama (14.500 adım); kırpma pencereyi iki saate daraltır |
| Karma Yapılar | 200.000 kayıt 91,2 MB (bütünsel 43,8 MB) | günlük yazma 39,2 → 5,4 MB, okuma 105,8 → 9,3 MB | kayıt başına 237 bayt üstveri |
| Kümeler ve Sıralı Kümeler | 38.000 üye 2.052.056 bayt (liste 532.056) | üyelik 28.464,2 → 1,0 adım | sıra bilgisi; atlamalı listede 3.135.704 bayt fazla |
| Bit Eşlemler ve Olasılıksal Yapılar | bit eşlem 250.001, kestirim 4.096 bayt | tam kümeye göre 16 ve 1.024 kat az yer | %2,53 yanlış olumlu; hangi okurun sayıldığı |
| Akışlar | uzunluk sınırında 5.078, güvenli kırpmada 9.031 KiB | okuma konumu 16 bayt; üç grubun defteri 48 bayt | sınırlı kırpmada 38.920 ve 46.220 giriş kaçtı |
| Uzamsal Dizinler | hücre kovası 5,09, sıralı küme 25,48 bayt/üye | taranan aday 120.000 → 821,22 | hücre kovasında tane ekleme anında sabitlenir |
| Sunucu Tarafı Betikleme | betik önbelleği 642, kilit tablosu tepede 88.000 bayt | kopya sayısının izin verdiğinden fazlası hiç verilmedi (istemcide 31,93) | uzun betik 8.000 komutu kuyrukta bekletti |
| Anlık Görüntü Kalıcılığı | dosya 717.260 bayt (bellekte 1.593.100) | ortalama kayıp 10.001 → 501 yazma | disk yazımı 2 → 30 MB; kopyalamada tepe 59.565 bayt |
| Ekleme Günlüğü Kalıcılığı | yazma başına 39,7 bayt, dosya yazmayla doğrusal | her yazmada eşzamanlamada kayıp sıfır | 60.000 eşzamanlama; görüntünün 3,32 katı dosya |
| Kalıcılık Seçimi | birlikte kullanım 4.229.117 bayt, 23 eşzamanlama | kurtarma 55.000 → 32.330 adım, okunan bayt 1.247.842 | yalnız görüntüyle 9.816 giriş eksik ya da eski |
| Bellek Sınırı ve Tahliye Politikaları | 400.000 baytlık bütçe, üstveri payı %9,9–%24,7 | rastgele 4.938, en az sık kullanılan 4.123 giriş | dar bütçede 4,4 puan isabet; ölü giriş 709.241 bayt |
| Süre Sonu Yönetimi | tembelde 4.027.669, etkin temizlemede 1.464.802 bayt | ölü pay %73,6 → %27,4 | erişim başına 0,774 giriş dokunuşu |
| Çoğaltma | dört kopyada 2.343,8 KiB (tek düğüm 468,8) + tampon 7,5–60 KiB | düğüm başına okuma 400 → 80 | 11.420 bayat okuma; gecikme 8'de oran %47,96 |
| Otomatik Devralma | sıcak yedek 937,5 KiB (soğuk 468,8) | kesinti 17 → 5 tur, kayıp 760 → 80 kayıt | devralma başına 40/80/160/320 kabul edilmiş yazma |
| Kümeleme | etiketsiz 1.539,6, şube etiketiyle 2.899,0 KiB | çok anahtarlı işlem %2,32 → %100 | yuva 8.709 → 6, dengesizlik 1,108 → 3,006 |
| İşlemler ve İyimser Kilitleme | izleme üstverisi 768 bayt (sayaçlar 384) | sınır aşımı 86 → 0 | 480 → 4.314 tik; blok 16'da 96.414 istemci-tik bekleme |
| Yayımla–Abone Ol | kanal başına 49 bayt (kalıcı yapıda 36.365–143.243) | 600 yayım 753 teslim kopyası, defter tutulmaz | abonesiz 74 yayım yok oldu; yavaş abonede 696 yayım |
| Tipik Kullanım Senaryoları | karma biçim 60.623 bayt (dizgi 46.115) | bin güncellemede yazma 90.454 → 8.000 bayt | 14.508 bayt fazla; sıralı küme 2.893,4 bayt/kullanıcı |
| Ölçüm ve Yavaş Komut Analizi | yavaş komut günlüğü eşik 20'de 108 kayıt, 6.422 bayt | en uzun bekleme 3.999 → 38, toplam 58.681 birim | tarama 6.001 yerine 8.001 birim, 398 giriş görülmedi |
| Güvenlik | rol+izin defteri 1.088 bayt; tampon 128 bağlantıda 4.194.304 | yabancıya açık 2.934.784 bayt ve yıkıcı yüzey sıfırlandı | bağlantı başına 3 adım, 11.600.000 bayt çerçeve eki |

Yirmi iki dersin tamamı aynı kuralın uygulamasıdır: **bellek bir bütçedir; her yapı bir şey satın
alır ve bir şey kaybettirir.** Tablonun ikinci sütununda hiç sıfır yoktur — kestirim yapan yapı
bile 4.096 bayt ister. Üçüncüde de yoktur; en ucuz kazanç bile bir kesinliği, bir sırayı, bir
pencereyi ya da bir garantiyi geri verir. Deponun hızlı olması bu kursun konusu değil
varsayımıydı; konu o hızın neye mal olduğuydu.

Buraya kadarki bütün erişimler tek bir ortak varsayımı taşıdı: **anahtar biliniyordu.** En karmaşık
ölçümde bile ilk adım bilinen bir anahtardı; uzamsal dizin bunu yalnız görünüşte esnetti, orada da
koordinat bir anahtara çevrildi. Oysa kütüphane katalogunda en sık sorulan soru budur: okur kitabın
kimliğini değil, adında geçen bir sözcüğü bilir. Metnin içinde geçen bir sözcükten belgeye gitmek
bu kursta hiç ele alınmadı ve anahtar–değer erişimiyle ele alınamaz.
**M17/K07 Arama Motorları ve Metin Erişimi** bu soruyla açılır.

