---
title: 'Sunucu Tarafı Betikleme'
source: 'https://academia.sh/tr/kurslar/bellek-ici-depolar/sunucu-tarafi-betikleme'
course: 'Bellek İçi Depolar ve Önbellek Sistemleri'
language: tr
updated: '2026-08-17T18:08:58+00:00'
license: 'CC BY-SA 4.0'
---

# Sunucu Tarafı 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 her satırda üç ödünç verdi, hiç fazla vermedi. 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 her eş zamanlılıkta tam üç ödünç verilir 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.
