---
title: 'Uygulama Katmanı ve Servis Etkileşimi'
source: 'https://academia.sh/tr/kurslar/uygulama-katmani'
language: tr
updated: '2026-08-17T04:43:02+00:00'
license: 'CC BY-SA 4.0'
lessons: 15
outcomes:
    - 'Durumsuz servis tasarımıyla yatay ölçeklemeyi mümkün kılar'
    - 'Eşzamanlı ve eşzamansız iletişim arasında seçim yapar'
    - 'İş akışlarını koreografi veya orkestrasyon ile kurar'
---

# Uygulama Katmanı ve Servis Etkileşimi

İsteğin uygulamaya ulaştıktan sonrası: durumun servis dışına taşınarak yatay ölçeklemenin mümkün kılınması, servislerin birbirini bulması ve konuşma biçimini seçmesi, sözleşmenin uyumluluğu koruyarak evrilmesi, zincirleme çağrıda zaman aşımı ile deneme bütçesinin toplam gecikmeye etkisi, yükün kuyruklarla zamana yayılması ve birden çok servise yayılan iş akışlarının koreografi ya da orkestrasyonla kurulması.

## Servis Tasarımı

Bir servisin ölçeklenebilir ve değiştirilebilir olmasını belirleyen kararlar: durumun servis dışına taşınması, dinamik adresleme ve sağlık bilgisiyle servis keşfi, uzak yordam, kaynak temelli ve sorgu temelli çağrının seçim ölçütleri, şema evrimi ile uyumluluğun korunması ve zincirleme çağrıda zaman aşımı ile deneme bütçesinin toplam gecikmeye etkisi.

### Durumsuz Servisler

Source: https://academia.sh/tr/kurslar/uygulama-katmani/durumsuz-servisler

Durumun servis sürecinin dışına taşınmasının yatay ölçeklemeye etkisi: aynı yükün iki kipte yerel süreçlerle ölçülmesi, kopya belleğinde tutulan oturumun kopyalar arasında bölünmesi ve yeniden başlatmada kaybolması, trafik katmanında ölçülen kopya sayısının geri alınması ve durumun dışarı taşınmasının durum deposuna ve gövde baytına ödettiği bedelin hesaplanması.

Trafik Katmanı kursu isteği uygulamanın kapısına kadar getirdi: adı çözüldü, kenar
önbelleğinden geçti, bir kopyaya düştü, ağ geçidinden ve kapıdan geçti. Kapının arkasında ise
tasarlanmamış bir alan duruyor. Servislerin durumsuz olup olmadığı, birbirlerinin adresini
nasıl buldukları, bir çağrı yanıt vermediğinde ne kadar bekleyip kaç kez deneyecekleri ve
sözleşmelerinin nasıl değişeceği o kursta sessizce varsayıldı. Bu kurs o varsayımları açar.

Sıra en alttakinden başlar: **servisin kendi belleğinde ne tuttuğu.** Önceki kursun son ölçümü
bunun neden ilk soru olduğunu göstermişti — oturum belirli bir kopyanın belleğinde durduğunda
dengeleyicinin isteği dağıtma yetkisi kalmıyor, 0,50 doluluk hedefi için gereken kopya sayısı
3'ten 4'e çıkıyor ve eklenen kopyalar en yüklü kopyanın yükünü düşürmüyordu. Bu ders o bağın
kaynağını ele alır. **Durumsuz servis** (stateless service), iki isteği birbirine bağlayan hiçbir
veriyi kendi süreç belleğinde tutmayan servistir. Durumsuz süreç ve on iki etmen ilkesi Sunucu
Tarafı Temelleri kursunda kuruldu; burada tanım tekrarlanmaz, **ölçülür**.

#### Hangi Durum Taşınır

Bir süreçte duran her veri taşınmak zorunda değildir. Ayrımı yapan soru şudur: bu veri
kaybolduğunda ya da başka bir kopya bu veriyi göremediğinde **yanıt değişir mi**.

İki isteği birbirine bağlayan veri — oturum, çok adımlı bir işin ara sonucu, sayfalama imleci,
tekillik anahtarı kaydı — taşınmak zorundadır; kaybolduğunda yanıt değişir. Yalnız hız için
tutulan veri — sıcak kayıt önbelleği, açık bağlantı havuzu, derlenmiş yapılandırma — kalabilir;
kaybolduğunda yanıt aynıdır, yalnız daha yavaş gelir. Bu ayrım bir kopyanın yeniden
başlatılmasının ne anlama geldiğini de belirler: ilk kümede yeniden başlatma bir veri kaybıdır,
ikincisinde bir ısınma süresidir.

Ölçüm birinci kümeyi ele alır. Gönderi takip servisinde bir satıcı entegrasyonu oturum açar ve
art arda takip sorguları gönderir; servis o oturumda hangi gönderilerin izlendiğini tutar.

#### Düzenek

Ölçüm yerel süreçlerle kurulur. İki kopya aynı işi yapar ve tek parametreyle iki kipte çalışır:
`bellek` kipinde oturum listesi kopyanın kendi belleğindedir, `disari` kipinde ayrı bir durum
deposu sürecindedir. Gerçek bir küme, bulut ortamı ya da oturum deposu ürünü kurulmaz.

```js
// durum/depo.mjs — oturum listesini surec disinda tutan durum deposu (tek surec, bellek ici)
import { createServer } from "node:http";

const oturumlar = new Map();
const nokta = Number(process.argv[2]);

if (Number.isInteger(nokta) === false) console.log("kullanim: node durum/depo.mjs <nokta>");
else createServer((istek, yanit) => {
  const ad = new URL(istek.url, "http://yerel").pathname.split("/").pop();
  if (istek.method === "PUT") {
    let m = "";
    istek.on("data", (p) => (m += p));
    istek.on("end", () => { oturumlar.set(ad, JSON.parse(m)); yanit.writeHead(204).end(); });
    return;
  }
  yanit.writeHead(200, { "content-type": "application/json" });
  yanit.end(JSON.stringify(oturumlar.get(ad) ?? null));
}).listen(nokta, "127.0.0.1");
```

```js
// durum/kopya.mjs — takip servisi kopyasi; oturum listesi bellekte ya da durum deposunda durur.
// Kullanim: node durum/kopya.mjs <nokta> <ad> <bellek|disari> <depo-noktasi>
import { createServer } from "node:http";

const [nokta, ad, kip, depo] = process.argv.slice(2);
const yerel = new Map();

const oku = async (o) => (kip === "bellek" ? yerel.get(o) ?? null
  : (await fetch(`http://127.0.0.1:${depo}/oturum/${o}`)).json());
const yaz = async (o, liste) => {
  if (kip === "bellek") { yerel.set(o, liste); return; }
  await fetch(`http://127.0.0.1:${depo}/oturum/${o}`, { method: "PUT", body: JSON.stringify(liste) });
};

if (Number.isInteger(Number(nokta)) === false) console.log("kullanim: node durum/kopya.mjs <nokta> <ad> <bellek|disari> <depo-noktasi>");
else createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://yerel");
  const oturum = u.searchParams.get("oturum") ?? "-";
  const json = (kod, govde) => yanit.writeHead(kod, { "content-type": "application/json" }).end(JSON.stringify({ ad, ...govde }));
  if (u.pathname === "/oturum") { await yaz(oturum, []); return json(201, { izlenen: 0 }); }
  const liste = await oku(oturum);
  if (liste === null) return json(409, { hata: "oturum yok" });
  if (u.pathname === "/takip") { liste.push(u.searchParams.get("no")); await yaz(oturum, liste); }
  json(200, { izlenen: liste.length });
}).listen(Number(nokta), "127.0.0.1");
```

İstemci oturumu ilk kopyada açar, sonra sekiz takip sorgusunu kopyalara sırayla dağıtır. Sıralı
gönderim dağılımı belirlenimli kılar; dağıtım kuralı Trafik Katmanı kursunun konusuydu ve burada
en yalın biçimiyle alınmıştır.

```js
// durum/istemci.mjs — oturumu ilk kopyada acar, sekiz istegi kopyalara sirayla dagitir, sonra ozet alir
// Kullanim: node durum/istemci.mjs <etiket> <nokta...>
const [etiket, ...nokta] = process.argv.slice(2);
const al = async (n, yol) => {
  const y = await fetch(`http://127.0.0.1:${n}${yol}`);
  return { kod: y.status, ...(await y.json()) };
};

if (nokta.length === 0) console.log("kullanim: node durum/istemci.mjs <etiket> <nokta...>");
else {
  await al(nokta[0], "/oturum?oturum=S1");
  let bulan = 0;
  for (let i = 0; i < 8; i += 1) {
    const s = await al(nokta[i % nokta.length], `/takip?oturum=S1&no=TR-${4820 + i}`);
    if (s.kod === 200) bulan += 1;
  }
  const ozet = [];
  for (const n of nokta) {
    const s = await al(n, "/izleme?oturum=S1");
    ozet.push(`${s.ad}=${s.kod === 200 ? s.izlenen : s.hata}`);
  }
  console.log(`${etiket.padEnd(24)} oturumu bulan ${bulan}/8   ozet ${ozet.join("  ")}`);
}
```

Sürücü betik aynı yükü iki kipte koşturur ve her kipte bir kopyayı yeniden başlatır. Bağlantı
noktası numaraları ortama bağlıdır; makinede kullanımdaysa değiştirilmelidir.

```bash
# olc.sh — ayni yuk iki kipte: durum kopyanin belleginde ve durum deposunda
node durum/depo.mjs 8891 & D=$!
node durum/kopya.mjs 8881 k1 bellek 8891 & A=$!
node durum/kopya.mjs 8882 k2 bellek 8891 & B=$!
sleep 1
node durum/istemci.mjs "bellekte" 8881 8882
kill $A; sleep 1
node durum/kopya.mjs 8881 k1 bellek 8891 & A=$!
sleep 1
echo "bellekte,  k1 yeniden baslatildi -> $(curl -s '127.0.0.1:8881/izleme?oturum=S1')"
kill $A $B; sleep 1

node durum/kopya.mjs 8881 k1 disari 8891 & A=$!
node durum/kopya.mjs 8882 k2 disari 8891 & B=$!
sleep 1
node durum/istemci.mjs "durum deposunda" 8881 8882
kill $A; sleep 1
node durum/kopya.mjs 8881 k1 disari 8891 & A=$!
sleep 1
echo "deposunda, k1 yeniden baslatildi -> $(curl -s '127.0.0.1:8881/izleme?oturum=S1')"
kill $A $B $D
```

```
bellekte                 oturumu bulan 4/8   ozet k1=4  k2=oturum yok
bellekte,  k1 yeniden baslatildi -> {"ad":"k1","hata":"oturum yok"}
durum deposunda          oturumu bulan 8/8   ozet k1=8  k2=8
deposunda, k1 yeniden baslatildi -> {"ad":"k1","izlenen":8}
```

#### Ölçülen Etki

Bu sayılar **ölçüm** sınıfındadır ve yerel süreçlerden alınmıştır; belirlenimlidir, çünkü
istekler sırayla gönderilir. Bağlantı noktaları ortama bağlıdır.

Birinci satır bağın kendisini gösteriyor: `bellek` kipinde sekiz isteğin yalnız dördü oturumu
buluyor. Bulanların hepsi oturumu açan kopyaya düşen isteklerdir; ötekiler `oturum yok` alıyor.
Özet satırı bunun sonucunu taşıyor — `k1` dört numara izliyor, `k2` oturumu hiç görmüyor. Yani
liste kaybolmuş değil, **bölünmüş**. Bölünen listeyi hiçbir kopya doğru veremez.

İkinci satır ikinci maliyeti veriyor: `k1` yeniden başlatıldığında dört numaralı liste de gitti.
Bir kopyanın yeniden başlatılması burada yayın, ölçek değişikliği ya da arıza sonrası kurtarma
demektir; üçü de olağan işlerdir ve üçünde de veri kaybı doğuyor.

Üçüncü ve dördüncü satırlar aynı yükü `disari` kipinde veriyor. Sekiz isteğin sekizi oturumu
buluyor, iki kopya da aynı sekiz numarayı sayıyor ve yeniden başlatılan kopya listeyi eksiksiz
okuyor. Kopyalar birbirinin eşi hâline geldi: hangi kopyanın yanıtladığı artık yanıtı
değiştirmiyor.

Ölçümün gösterdiği şey bir başarım kazancı değildir — `disari` kipi daha çok iş yapar. Gösterdiği
şey bir **koşuldur**: yükün kopyalara serbestçe bölünebilmesi, iki isteği birbirine bağlayan
verinin süreç dışında olmasına bağlıdır.

#### Hesaba Geri Dönüş

Koşulun sağlanmasının karşılığı Trafik Katmanı kursunun son dersinde sayıya dökülmüştü. O ders
yapışkan düzende dengesizliği ölçmüş, 0,50 doluluk hedefi için gereken kopya sayısını 4 bulmuş ve
en büyük oturumun payını bir seri pay olarak yazmıştı. Durumsuz düzende bu sayılar geri alınır.

```js
// durum/kapasite.mjs — durumsuzlugun kopya sayisina etkisi ve durumun disariya tasima maliyeti
const TEPE_UC = 513.89;              // K01 Kabaca Buyukluk Hesabi: tepe ucta istek/s
const DOYUM = 400, GUVENLI = 200;    // K02 Yuk Dengeleyici Rolu: varsayim Y1 ve Y1 x Y2
const SERI_PAY = 0.1980;             // K02 Oturum Yapiskanligi: en buyuk oturumun payi
const DEPO_ISTEK = 138.89;           // K01: depoya ulasan istek/s
const YAZMA_GIRIS = 0.17;            // K01: yazma giris Mbit/s
const DUZEN = [["yapiskan (K02)", 3, 1.173], ["yapiskan (K02)", 4, 1.505],
  ["durumsuz", 3, 1.000], ["durumsuz", 4, 1.000]];

console.log(`${"duzen".padEnd(16)}${"kopya".padStart(6)}${"dengesizlik".padStart(12)}` +
  `${"en yuklu istek/s".padStart(18)}${"doluluk".padStart(9)}${"gereken kopya".padStart(15)}`);
for (const [ad, n, d] of DUZEN) {
  const enYuklu = (d * TEPE_UC) / n;
  console.log(`${ad.padEnd(16)}${String(n).padStart(6)}${d.toFixed(3).padStart(12)}` +
    `${enYuklu.toFixed(2).padStart(18)}${(enYuklu / DOYUM).toFixed(3).padStart(9)}` +
    `${String(Math.ceil((d * TEPE_UC) / GUVENLI)).padStart(15)}`);
}
console.log(`\nyapiskan duzende olcekleme ust siniri = 1/${SERI_PAY} = ${(1 / SERI_PAY).toFixed(2)} kat;` +
  ` durumsuz duzende boyle bir ust sinir yok`);

// U1 maliyeti: her takip istegi oturum listesinden bir okuma ve bir yazma yapiyor (duzenekteki davranis)
const islem = 2 * TEPE_UC;
console.log(`\nU1 -> durum deposu islem/s = ${islem.toFixed(2)}, K01'in depoya ulasan ` +
  `${DEPO_ISTEK} istek/s'sinin ${(islem / DEPO_ISTEK).toFixed(2)} kati`);
console.log(`U1 yalniz okuma olsaydi = ${TEPE_UC.toFixed(2)} islem/s (${(TEPE_UC / DEPO_ISTEK).toFixed(2)} kat)`);

// Ikinci yol: durum istekte tasinir; govde buyur
const liste = (k) => JSON.stringify(Array.from({ length: k }, (_, i) => `TR-${4820 + i}`));
console.log(`\n${"listedeki numara".padEnd(18)}${"oturum govdesi bayt".padStart(21)}` +
  `${"ek giris Mbit/s".padStart(17)}${"K01 yazma girisine oran".padStart(25)}`);
for (const k of [8, 40]) {
  const bayt = Buffer.byteLength(liste(k));
  const mbit = (TEPE_UC * bayt * 8) / 1e6;
  console.log(`${String(k).padEnd(18)}${String(bayt).padStart(21)}${mbit.toFixed(3).padStart(17)}` +
    `${(mbit / YAZMA_GIRIS).toFixed(2).padStart(25)}`);
}
```

```
duzen            kopya dengesizlik  en yuklu istek/s  doluluk  gereken kopya
yapiskan (K02)       3       1.173            200.93    0.502              4
yapiskan (K02)       4       1.505            193.35    0.483              4
durumsuz             3       1.000            171.30    0.428              3
durumsuz             4       1.000            128.47    0.321              3

yapiskan duzende olcekleme ust siniri = 1/0.198 = 5.05 kat; durumsuz duzende boyle bir ust sinir yok

U1 -> durum deposu islem/s = 1027.78, K01'in depoya ulasan 138.89 istek/s'sinin 7.40 kati
U1 yalniz okuma olsaydi = 513.89 islem/s (3.70 kat)

listedeki numara    oturum govdesi bayt  ek giris Mbit/s  K01 yazma girisine oran
8                                    81            0.333                     1.96
40                                  401            1.649                     9.70
```

Üstteki tablonun sayıları **hesap** sınıfındadır. Durumsuz düzende dengesizlik 1,000 olduğu için
uçtaki 513,89 istek/s üç kopyaya eşit bölünüyor: kopya başına 171,30 istek/s ve 0,428 doluluk.
Gereken kopya sayısı 4'ten 3'e iniyor — yapışkan düzenin faturası bir kopyaydı ve geri alındı.
Alttaki iki satır daha önemlisini söylüyor: durumsuz düzende kopya eklemek işe yarıyor. Dördüncü
kopya en yüklü kopyayı 171,30'dan 128,47 istek/s'ye indiriyor, oysa yapışkan düzende aynı ekleme
200,93'ten 193,35'e indirmişti. Ölçekleme üst sınırı da kalkıyor: seri pay 0,1980 iken üst sınır
5,05 katıydı, durumsuz düzende böyle bir sınır yok.

Bedel alt bölümlerdedir ve **bu kursun varsayımıyla** yazılır; K01'in tablosuna eklenmez.

**U1 — her takip isteği oturum durumundan bir okuma ve bir yazma yapar.** Gerekçe: düzenekteki
davranış budur, izleme listesi her sorguda okunup güncelleniyor. Duyarlılık aşağıda hesaplandı.

U1 ile durum deposuna saniyede 1027,78 işlem düşüyor. Bu, K01'in gönderi deposuna ulaşan 138,89
istek/s'sinin 7,40 katıdır. Sayının büyüklüğü bir uyarıdır: durumu süreç dışına taşımak onu yok
etmez, **başka bir yere taşır** ve o yer artık sistemin en çok istek alan bileşenidir. Duyarlılık
tek yönlüdür — oturum yalnız okunsaydı 513,89 işlem/s, yani 3,70 kat olurdu; yazma isteğe bağlı
olduğu için bu, tasarımla küçültülebilen bir sayıdır.

İkinci yol durumu hiç saklamamaktır: liste isteğin kendisinde taşınır. Alttaki tablo bunun
faturasını veriyor. Sekiz numaralı bir liste 81 bayttır ve 513,89 istek/s'de 0,333 Mbit/s ek
giriş demektir — K01'in yazma girişinin 1,96 katı. Liste kırk numaraya çıktığında 401 bayt ve
1,649 Mbit/s, yani 9,70 kat. İki yolun ayrımı buradan okunur: durum küçük ve sınırlıysa istekte
taşınabilir, büyüyebiliyorsa taşınamaz.

#### Özet

- Durumsuz servis, iki isteği birbirine bağlayan hiçbir veriyi kendi süreç belleğinde tutmayan
  servistir; ayrımı yapan soru, veri kaybolduğunda yanıtın değişip değişmediğidir.
- `bellek` kipinde sekiz isteğin dördü oturumu buldu ve liste iki kopya arasında bölündü (k1=4,
  k2 görmüyor); kopya yeniden başlatıldığında liste tümüyle kayboldu.
- `disari` kipinde sekiz isteğin sekizi oturumu buldu, iki kopya da aynı sekiz numarayı saydı ve
  yeniden başlatılan kopya listeyi eksiksiz okudu.
- Durumsuz düzende dengesizlik 1,000: gereken kopya 4'ten 3'e, kopya başına yük 200,93'ten 171,30
  istek/s'ye iniyor ve dördüncü kopya artık işe yarıyor (128,47 istek/s).
- Ölçekleme üst sınırı kalkıyor: yapışkan düzende seri pay 0,1980 üst sınırı 5,05 katta tutuyordu.
- Bedel taşınıyor, yok olmuyor: U1 ile durum deposuna 1027,78 işlem/s (K01'in depo yükünün 7,40
  katı), ya da durum istekte taşınırsa 81 bayt için 0,333 Mbit/s ek giriş, kırk numaralı listede
  1,649 Mbit/s.

#### Sonraki Adım

Kopyalar artık birbirinin eşi ve yük serbestçe bölünebiliyor. Ama ölçümün bir parçası el yazısıyla
duruyor: sürücü betik kopyaların bağlantı noktalarını komut satırından aldı, istemci de aynı
sayıları elle taşıdı. Trafik Katmanı kursunun dengeleyicisi de kopya listesini aynı biçimde
almıştı. Kopya sayısı değiştiğinde, bir kopya düştüğünde ya da yeni bir kopya ayağa kalktığında bu
listeyi kim güncelliyor. Sonraki ders o soruyu ele alır: adres bilgisinin nerede durduğunu, bir
kopyanın listeye girip çıkmasının ne kadar sürdüğünü ve o pencerede kaç isteğin yanlış adrese
gittiğini ölçer — ve bu işin yük dengeleyicinin sağlık denetiminden neden ayrı bir karar olduğunu
gösterir.

### Servis Keşfi

Source: https://academia.sh/tr/kurslar/uygulama-katmani/servis-kesfi

Kopya adreslerinin elle yazılan bir listeden çıkarılıp kayıt defterine taşınması: servis keşfinin yük dengeleyicinin sağlık denetiminden ayrılması, kayıt yaşam süresi ile çözümleme önbelleğinin bir arızayı fark etme penceresini ne kadar uzattığının modelle ölçülmesi, zarif kapanmanın pencereyi sıfırlaması ve pencerenin giriş kursundaki kesinti bütçesine çevrilmesi.

Önceki ders kopyaları birbirinin eşi hâline getirdi: durum süreç dışına taşındı, yük serbestçe
bölünebilir oldu ve gereken kopya sayısı 3'e indi. Ama ölçüm düzeneğinin bir parçası hâlâ el
yazısıyla duruyordu — sürücü betik kopyaların bağlantı noktalarını komut satırından aldı, istemci
aynı sayıları elle taşıdı. Trafik Katmanı kursunun yük dengeleyicisi de kopya listesini aynı
biçimde almıştı. Kopya sayısı değiştiğinde, bir kopya düştüğünde ya da yeni bir kopya ayağa
kalktığında bu listeyi kimin güncellediği hiçbir yerde yazılı değil.

**Servis keşfi** (service discovery), bir servisin o anda ayakta olan kopyalarının adreslerinin
çalışma zamanında bulunmasıdır. Yük dengeleyiciyle karıştırılmaya açıktır ve ikisi ayrı işler
yapar: **dengeleyici isteği dağıtır, keşif adresi bulur.** Dengeleyicinin havuzu, keşfin
ürettiği listedir; keşif olmadan o liste bir yapılandırma dosyasında durur. Bu ders listenin
nereden geldiğini, güncellenmesinin ne kadar sürdüğünü ve o sürede kaç isteğin ölü bir adrese
gittiğini ölçer.

#### İki Yerleşim

**Statik liste.** Adresler her çağıran servisin yapılandırmasında yazılıdır. Bir kopya
düştüğünde hiçbir çağıran bunu kendiliğinden fark etmez; liste değiştiğinde her çağıranın
yapılandırması ayrı ayrı güncellenir.

**Kayıt defteri.** Kopyalar ayağa kalkınca kendilerini bir kayıt defterine yazar ve düzenli
aralıklarla **kalp atışı** (heartbeat) gönderir. Kaydın bir **yaşam süresi** vardır; kalp atışı
kesildiğinde kayıt bu süre dolunca düşer. Çağıran adresi çağrı anında defterden çözer.

İki yerleşimde de bir gecikme vardır ve dersin ölçtüğü sayı odur: bir kopya susmasıyla listeden
düşmesi arasında geçen **fark etme penceresi**.

#### Model

Aşağıdaki düzenek bir **modeldir**: kayıt defteri, kalp atışı ve çözümleme birer modüldür,
süreler mantıksal saatle ilerler, arıza ve yeni kopya birer parametreyle tetiklenir. Gerçek bir
keşif altyapısı, hizmet ağı ürünü ya da bulut ortamı kurulmaz. Modelin iki sayısı **bu kursun
varsayımıdır** ve K01'in tablosuna eklenmez.

**U2 — kalp atışı aralığı 1000 ms, kayıt yaşam süresi 3000 ms.** Gerekçe: yaşam süresi, tek bir
kayıp kalp atışının kaydı düşürmemesi için aralığın birkaç katı seçilir; burada üç katı alındı.
Duyarlılığı aşağıda hesaplandı.

```js
// kesif/defter.mjs — kayit defteri MODELI: kayit, kalp atisi, yasam suresi ve zarif kapanma.
// Sure gercek degil, mantiksal saattir (ms); ariza ve yeni kopya parametreyle tetiklenir.
export const KALP = 1000;              // varsayim U2: kalp atisi araligi (ms)
export const YASAM = 3000;             // varsayim U2: kaydin yasam suresi (ms)

export function defter() {
  const kayit = new Map();
  return {
    kalpAtisi: (ad, adres, t) => kayit.set(adres, { ad, son: t }),
    kaydiSil: (adres) => kayit.delete(adres),
    coz: (ad, t) => [...kayit.entries()]
      .filter(([, k]) => k.ad === ad && t - k.son < YASAM)
      .map(([adres]) => adres),
  };
}

// Cozumleme onbellegi: cagiran cozdugu listeyi sure kadar elinde tutar.
export function cozucu(d, ad, onbellekMs) {
  let liste = [], tazeleme = -Infinity;
  return (t) => {
    if (t - tazeleme >= onbellekMs) { liste = d.coz(ad, t); tazeleme = t; }
    return liste;
  };
}
```

Koşum aynı arızayı dört yerleşimde tekrarlar. `k2` kopyası beşinci saniyede susar, `k4` kopyası
altıncı saniyede ayağa kalkar; çağıran on milisaniyede bir istek gönderir ve listeden sırayla bir
adres seçer.

```js
// kesif/olc.mjs — ayni arizada dort yerlesim: statik liste, kayit defteri, cozumleme onbellegi, zarif kapanma
import { defter, cozucu, KALP, YASAM } from "./defter.mjs";

const KOPYA = ["k1", "k2", "k3"];      // baslangicta ayakta olan kopyalar
const ARIZA = 5000;                    // k2 bu anda susuyor (model parametresi)
const YENI = 6000;                     // k4 bu anda ayaga kalkiyor (model parametresi)
const BAS = 5000, BIT = 11000, ADIM = 10;   // istek penceresi ve istek araligi (ms)
const TUKETICI = 4;                    // cagiran servis sayisi: gecit, ucretlendirme, teslimat-op, toplu isci

function kosum({ onbellek = 0, zarifKapanma = false, statik = false }) {
  const d = defter();
  const coz = cozucu(d, "takip", onbellek);
  let sira = 0, dusen = 0, yeniyeGiden = 0, sonDusen = null, sondaListede = false;
  for (let t = 0; t <= BIT; t += ADIM) {
    for (const k of KOPYA) {                       // kalp atislari
      if (t % KALP === 0 && (k !== "k2" || t <= ARIZA)) d.kalpAtisi("takip", k, t);
    }
    if (t % KALP === 0 && t >= YENI) d.kalpAtisi("takip", "k4", t);
    if (zarifKapanma && t === ARIZA) d.kaydiSil("k2");
    if (t < BAS) continue;
    const liste = statik ? [...KOPYA] : coz(t);    // statik liste arizadan haberdar olmaz
    sondaListede = liste.includes("k2");
    const secilen = liste[sira++ % liste.length];
    if (secilen === "k2") { dusen += 1; sonDusen = t; }
    if (secilen === "k4") yeniyeGiden += 1;
  }
  const pencere = sondaListede ? "elle" : sonDusen === null ? "0" : String(sonDusen - ARIZA + ADIM);
  return { dusen, yeniyeGiden, pencere };
}

const YERLESIM = [
  ["statik liste", { statik: true }, TUKETICI],
  ["kayit defteri", {}, 1],
  ["defter + onbellek 800", { onbellek: 800 }, 1],
  ["defter + zarif kapanma", { zarifKapanma: true }, 1],
];

console.log(`kalp atisi ${KALP} ms, yasam suresi ${YASAM} ms; k2 ${ARIZA} ms'de susuyor, ` +
  `k4 ${YENI} ms'de kaydoluyor`);
console.log(`istek penceresi ${BAS}-${BIT} ms, ${ADIM} ms'de bir istek = ${(BIT - BAS) / ADIM + 1} istek\n`);
console.log(`${"yerlesim".padEnd(26)}${"fark etme penceresi(ms)".padStart(24)}` +
  `${"olu adrese giden".padStart(18)}${"yeni kopyaya giden".padStart(20)}${"yapilandirma noktasi".padStart(22)}`);
for (const [ad, secenek, nokta] of YERLESIM) {
  const r = kosum(secenek);
  console.log(`${ad.padEnd(26)}${r.pencere.padStart(24)}${String(r.dusen).padStart(18)}` +
    `${String(r.yeniyeGiden).padStart(20)}${String(nokta).padStart(22)}`);
}
```

```
kalp atisi 1000 ms, yasam suresi 3000 ms; k2 5000 ms'de susuyor, k4 6000 ms'de kaydoluyor
istek penceresi 5000-11000 ms, 10 ms'de bir istek = 601 istek

yerlesim                   fark etme penceresi(ms)  olu adrese giden  yeni kopyaya giden  yapilandirma noktasi
statik liste                                  elle               200                   0                     4
kayit defteri                                 2980                83                 150                     1
defter + onbellek 800                         3180                93                 134                     1
defter + zarif kapanma                           0                 0                 167                     1
```

#### Ölçülen Etki

Bu sayılar modelden çıkar ve belirlenimlidir: mantıksal saat ve sabit istek aralığı aynı sonucu
her koşumda verir.

**Statik liste hiç fark etmiyor.** Ölü adrese 601 isteğin 200'ü gidiyor ve pencere kapanmıyor;
kapanması için bir kişinin dört ayrı yapılandırmayı değiştirmesi gerekir. `yeni kopyaya giden`
sütunu ikinci yarıyı veriyor: `k4` ayağa kalktı ve statik düzende hiç istek almadı. Ölçek
artışının yararı da aynı gecikmeyle bekliyor.

**Kayıt defteri pencereyi kapatıyor ama anında değil.** Pencere 2980 milisaniye, yani yaşam
süresinin neredeyse tamamı. Seksen üç isteğin ölü adrese gitmesi bir kusur değil, seçilen yaşam
süresinin doğrudan sonucudur: kayıt, kalp atışı kesildikten sonra üç saniye daha listede durur.
`k4` bu düzende 150 istek alıyor — kayıt defteri yalnız düşeni çıkarmakla kalmıyor, yeni geleni
de tanıtıyor.

**Çözümleme önbelleği pencereyi uzatıyor.** Çağıran listeyi 800 milisaniye elinde tuttuğunda
pencere 2980'den 3180'e, ölü adrese giden istek 83'ten 93'e çıkıyor. Önbellek defterin yükünü
düşürür ve bunu pencereyi büyüterek yapar; iki sayı aynı kararın iki yüzüdür.

**Zarif kapanma pencereyi sıfırlıyor.** Kopya kapanmadan önce kaydını kendisi siliyor ve ölü
adrese hiç istek gitmiyor. Bu satır önemlidir çünkü kopyaların çoğu **planlı** olarak kapanır —
yayın, ölçek küçültme, taşınma. Planlı kapanmalarda pencereyi ödemek gerekmiyor; yaşam süresi
yalnız plansız susmalar için bir sigortadır.

Son sütun ayrı bir ölçüdür: yapılandırma noktası statik düzende 4, defterle 1. Sayı çağıran
servis sayısıyla büyür; kopya listesinin her tüketicide tekrarlanması, kopya sayısı değiştiğinde
yapılacak işin tüketici sayısıyla çarpılması demektir.

#### Hesaba Geri Dönüş

Pencere bir süredir ve K01 ile K02'nin sayıları onu isteğe çevirir. Trafik Katmanı kursu aynı
hesabı sağlık denetimi için yapmış, arıza başına 34,26 istek bulmuştu.

```js
// kesif/butce.mjs — fark etme penceresinin K01 istek hizi ve kesinti butcesindeki karsiligi
const TEPE_UC = 513.89;                          // K01 Kabaca Buyukluk Hesabi: tepe ucta istek/s
const KOPYA = 3;                                 // K02 Yuk Dengeleyici Rolu: en az kopya sayisi
const ARIZA_AY = 2.82;                           // K01 Erisilebilirligin Sayisal Ifadesi: ayda butceye sigan ariza
const AY_ISTEK = (12_000_000 + 2_800_000) * 30;  // K01: gunluk takip sorgusu + durum olayi
const SAGLIK = 0.2, DEFTER = 2.98;               // K02 varsayim Y3 penceresi ve modelin olctugu pencere
const PENCERE = [
  ["saglik denetimi (K02 Y3)", SAGLIK], ["kayit defteri (U2)", DEFTER],
  ["defter + onbellek 800", 3.18], ["zarif kapanma", 0], ["elle mudahale (K01)", 600],
];

const payHiz = TEPE_UC / KOPYA;
console.log(`kopya basina hiz = ${TEPE_UC} / ${KOPYA} = ${payHiz.toFixed(2)} istek/s\n`);
console.log(`${"pencere kaynagi".padEnd(26)}${"sn".padStart(7)}${"ariza basina dusen".padStart(20)}` +
  `${"aylik dusen".padStart(13)}${"istek tabanli erisilebilirlik".padStart(31)}`);
for (const [ad, sn] of PENCERE) {
  const bir = payHiz * sn, aylik = bir * ARIZA_AY;
  console.log(`${ad.padEnd(26)}${String(sn).padStart(7)}${bir.toFixed(2).padStart(20)}` +
    `${aylik.toFixed(0).padStart(13)}${`%${(100 * (1 - aylik / AY_ISTEK)).toFixed(5)}`.padStart(31)}`);
}

console.log(`\nikisi birlikte: etkin pencere = min(${SAGLIK}, ${DEFTER}) = ${Math.min(SAGLIK, DEFTER)} sn ` +
  `-> ${(payHiz * Math.min(SAGLIK, DEFTER)).toFixed(2)} istek; kesif tek basina ${(DEFTER / SAGLIK).toFixed(1)} kat kotu`);
const kalp = (yasam) => (KOPYA * 1000) / (yasam / 3);
console.log(`duyarlilik: U2'nin yasam suresi 3000 -> 1000 ms olsaydi pencere ${(DEFTER - 2).toFixed(2)} sn, ` +
  `ariza basina ${(payHiz * (DEFTER - 2)).toFixed(2)} istek`);
console.log(`  defterin kendi yuku ${kalp(3000).toFixed(2)} -> ${kalp(1000).toFixed(2)} atis/s ` +
  `(K02'nin saglik denetimi yuku 15 istek/s)`);
```

```
kopya basina hiz = 513.89 / 3 = 171.30 istek/s

pencere kaynagi                sn  ariza basina dusen  aylik dusen  istek tabanli erisilebilirlik
saglik denetimi (K02 Y3)      0.2               34.26           97                      %99.99998
kayit defteri (U2)           2.98              510.46         1440                      %99.99968
defter + onbellek 800        3.18              544.72         1536                      %99.99965
zarif kapanma                   0                0.00            0                     %100.00000
elle mudahale (K01)           600           102778.00       289834                      %99.93472

ikisi birlikte: etkin pencere = min(0.2, 2.98) = 0.2 sn -> 34.26 istek; kesif tek basina 14.9 kat kotu
duyarlilik: U2'nin yasam suresi 3000 -> 1000 ms olsaydi pencere 0.98 sn, ariza basina 167.87 istek
  defterin kendi yuku 3.00 -> 9.00 atis/s (K02'nin saglik denetimi yuku 15 istek/s)
```

Bu sayılar **hesap** sınıfındadır. Tablo bir sıralama veriyor: sağlık denetimi 34,26, kayıt
defteri 510,46, elle müdahale 102.778 istek. Keşif, elle tutulan listeye göre iki yüz kat iyi,
sağlık denetimine göre on beş kat kötü. Bu iki karşılaştırma birlikte okunmalıdır, çünkü dersin
asıl savı buradadır: **servis keşfi bir arıza algılama aracı değildir.** İşi, listenin doğru
kaynaktan gelmesini sağlamaktır; arızayı hızlı fark etme işi hâlâ sağlık denetiminindir.

İkisi birlikte kullanıldığında etkin pencere küçük olanla belirlenir. Dengeleyici düşen kopyayı
200 milisaniyede havuzundan çıkarır; defterin kaydı üç saniye daha durur ama o kayıt zaten
dengeleyicinin havuzuna girmez. Bir katman diğerinin yerine geçmiyor, biri ötekinin girdisini
üretiyor.

Duyarlılık satırı U2'nin fiyatını gösteriyor. Yaşam süresi 3000 milisaniyeden 1000'e indirilirse
pencere 0,98 saniyeye ve arıza başına düşen istek 167,87'ye iner, ama kalp atışı aralığı da
üçte birine inmek zorundadır: defterin kendi yükü 3,00 atıştan 9,00 atış/s'ye çıkar. Karşılaştırma
noktası K02'nin sağlık denetimi yüküdür — 15 istek/s. Keşif katmanının gözlem yükü, sağlık
denetiminin yükünün altındadır ve pencereyi kısaltmak onu doğrusal büyütür.

#### Özet

- Servis keşfi ile yük dengeleme ayrı işlerdir: dengeleyici isteği dağıtır, keşif adresi bulur;
  keşfin ürettiği liste dengeleyicinin havuzudur.
- Statik listede ölü adrese 601 isteğin 200'ü gitti ve pencere kendiliğinden kapanmadı; yeni
  ayağa kalkan kopya hiç istek almadı ve yapılandırma noktası tüketici sayısı kadar (4) oldu.
- Kayıt defterinde pencere 2980 ms, ölü adrese giden 83 istek; yeni kopya 150 istek aldı ve
  yapılandırma noktası 1'e indi.
- Çözümleme önbelleği defterin yükünü düşürürken pencereyi 3180 ms'ye ve düşen isteği 93'e
  çıkarıyor; zarif kapanma pencereyi 0'a indiriyor, çünkü kopya kaydını kendisi siliyor.
- K01'e dönüş: arıza başına düşen istek sağlık denetiminde 34,26, kayıt defterinde 510,46, elle
  müdahalede 102.778; keşif elle tutulan listeye göre iki yüz kat iyi, sağlık denetimine göre on
  beş kat kötüdür.
- U2 duyarlılığı: yaşam süresi 1000 ms'ye inerse pencere 0,98 saniyeye düşer, defterin kendi yükü
  3,00'ten 9,00 atış/s'ye çıkar (K02'nin sağlık denetimi yükü 15 istek/s).

#### Sonraki Adım

Adres artık çalışma zamanında bulunuyor ve liste kendini güncelliyor. Bu, iki servisin
birbirine ulaşabilmesi için gereken son parçaydı; ama ulaştıktan sonra ne konuşacakları
seçilmedi. Ağ geçidi teslimat operasyonu ile ücretlendirme servisini paralel çağırıp yanıtlarını
birleştiriyordu ve o çağrıların biçimi hiçbir yerde tartışılmadı. Sonraki ders o seçimi ele alır:
aynı iç çağrı üç ayrı biçimde yazılır ve üç ölçüyle karşılaştırılır — bir dış istek için yapılan
iç çağrı sayısı, iç sınırdan geçen bayt ve çağıranın bilmek zorunda olduğu ad sayısı.

### İletişim Biçimleri

Source: https://academia.sh/tr/kurslar/uygulama-katmani/iletisim-bicimleri

Aynı iç çağrının uzak yordam, kaynak temelli ve sorgu temelli biçimlerde yazılıp servis sınırına özgü üç ölçüyle karşılaştırılması: bir dış istek için yapılan iç çağrı sayısı, iç sınırdan geçen bayt ve çağıranın bilmek zorunda olduğu ad sayısı; iç bayt hacminin giriş kursunun tepe okuma hızıyla çarpılıp iç sınır bant genişliğine çevrilmesi.

Adres artık çalışma zamanında bulunuyor ve liste kendini güncelliyor. İki servisin birbirine
ulaşabilmesi için gereken parçalar tamam; ama ulaştıktan sonra ne konuşacakları seçilmedi. Trafik
Katmanı kursunun ağ geçidi teslimat operasyonu ile ücretlendirme servisini paralel çağırıp
yanıtlarını tek gövdede birleştiriyordu ve o iki çağrının biçimi hiçbir yerde tartışılmadı.

Üç biçim Web API Tasarımı kursunda kurulmuştu: **uzak yordam çağrısı**, **kaynak temelli** ve
**sorgu temelli**. O kurs üçünü **istemci sınırında** karşılaştırdı ve ölçüleri oraya aitti —
istek sayısı, aşırı veri çekme oranı, gereksinim değiştiğinde hangi tarafın değişmek zorunda
kaldığı. Servis sınırı farklı bir yerdir: iki uç da bizimdir, aşırı veri çekme elle
düzeltilebilen bir kusurdur ve istemci sürümünü bekleme sorunu yoktur. Protokol ve şema dili bu
derste yeniden anlatılmaz; ölçülen şey seçimin sistem üzerindeki üç sonucudur — bir dış istek için
yapılan **iç çağrı sayısı**, **iç sınırdan geçen bayt** ve çağıranın bilmek zorunda olduğu **ad
sayısı**.

#### Düzenek

İki alan servisi ayrı süreçlerdir ve aynı veriyi üç biçimde sunar. Alan kümeleri M18'de kurulan
iki bağlamı sürdürür: teslimat operasyonu rota ve taşıyıcı bilgisini, ücretlendirme tarife ve
sözleşme bilgisini taşır. Ağ geçidinin yanıttan okuduğu alanlar K01'in V5 varsayımından gelir —
durum, bölge, güncellenme zamanı, son rota adımları — ve buna ücretin net tutarı eklenir.

```js
// iletisim/servis.mjs — iki alan servisi (teslimat operasyonu 8471, ucretlendirme 8472),
// her biri ayni veriyi uc bicimde sunuyor: kaynak temelli, uzak yordam ve sorgu temelli.
import { createServer } from "node:http";

const GONDERI = { "TR-4821": { takipNo: "TR-4821", durum: "dagitimda", bolge: "35",
  guncellenme: "2026-03-11T08:24:00Z", rota: ["34", "41", "35"], tasiyici: "T3",
  agirlikGram: 2400, hacimDm3: 12, sozlesmeNo: "S7", cikisSubesi: "34-021", varisSubesi: "35-114" } };
const UCRET = { "TR-4821": { takipNo: "TR-4821", tarife: 9600, indirim: 1440, net: 8160,
  sozlesmeNo: "S7", donem: "2026-03", kalem: 3, vergiOrani: 0.2 } };

const YORDAM = {                                   // amaca ozel, sunucunun sabitledigi yanitlar
  takipOzeti: (k) => ({ durum: k.durum, bolge: k.bolge, guncellenme: k.guncellenme, rota: k.rota }),
  netUcret: (k) => ({ net: k.net }),
};

const govdeOku = (istek) => new Promise((coz) => {
  let m = ""; istek.on("data", (p) => (m += p)); istek.on("end", () => coz(m));
});

const sun = (port, ad, tablo) => createServer(async (istek, yanit) => {
  const yol = new URL(istek.url, "http://yerel").pathname;
  let govde;
  if (yol === "/rpc") {
    const { yordam, no } = JSON.parse(await govdeOku(istek));
    govde = YORDAM[yordam]?.(tablo[no]) ?? { hata: "bilinmeyen yordam" };
  } else if (yol === "/sorgu") {
    const { no, alanlar } = JSON.parse(await govdeOku(istek));
    govde = Object.fromEntries(alanlar.map((a) => [a, tablo[no][a]]));
  } else {
    govde = tablo[yol.split("/").pop()] ?? { hata: "bulunamadi" };
  }
  const metin = JSON.stringify(govde);
  yanit.writeHead(200, { "content-type": "application/json" }).end(metin);
}).listen(port, "127.0.0.1", () => console.log(`${ad} 127.0.0.1:${port}`));

sun(8471, "teslimat-op  ", GONDERI);
sun(8472, "ucretlendirme", UCRET);
```

Ölçüm ağ geçidinin yerini alır: her biçimde aynı takip yanıtını kurar ve dört sayı toplar. Bayt
sayıları yalnız gövdeyi sayar; başlık ve protokol yükü K01'in hesabında da dışarıda bırakılmıştı.
Ad sayısı, çağıranın kodunda geçen karşı taraf adlarıdır — yol adı, yordam adı ve okunan alan
adı. Servis adları üç biçimde de aynıdır ve önceki dersin keşif katmanından gelir, bu yüzden
sayılmaz.

```js
// iletisim/olc.mjs — ayni ic cagriyi uc bicimde olcer, sonra K01'in tepe okuma hizina uygular
const OKUNAN = ["durum", "bolge", "guncellenme", "rota", "net"];   // gecidin yanittan okudugu alanlar
let cagri = 0, gonderilen = 0, alinan = 0;

async function cagir(port, yol, govde) {
  cagri += 1;
  const metin = govde === undefined ? undefined : JSON.stringify(govde);
  if (metin) gonderilen += Buffer.byteLength(metin);
  const y = await fetch(`http://127.0.0.1:${port}${yol}`, metin ? { method: "POST", body: metin } : {});
  const cevap = await y.text();
  alinan += Buffer.byteLength(cevap);
  return JSON.parse(cevap);
}
const sifirla = () => { cagri = gonderilen = alinan = 0; };

const BICIM = {
  "kaynak temelli": { yol: ["/gonderi", "/ucret"], yordam: [],
    calis: async (n) => [await cagir(8471, `/gonderi/${n}`), await cagir(8472, `/ucret/${n}`)] },
  "uzak yordam": { yol: ["/rpc"], yordam: ["takipOzeti", "netUcret"],
    calis: async (n) => [await cagir(8471, "/rpc", { yordam: "takipOzeti", no: n }),
      await cagir(8472, "/rpc", { yordam: "netUcret", no: n })] },
  "sorgu temelli": { yol: ["/sorgu"], yordam: [],
    calis: async (n) => [await cagir(8471, "/sorgu", { no: n, alanlar: OKUNAN.slice(0, 4) }),
      await cagir(8472, "/sorgu", { no: n, alanlar: ["net"] })] },
};

const OLCU = ["ic cagri", "gonderilen bayt", "alinan bayt", "toplam ic bayt",
  "cagiranin bildigi ad", "  yol adi", "  yordam adi", "  alan adi"];
const sonuc = [];
for (const [ad, b] of Object.entries(BICIM)) {
  sifirla();
  const [a, c] = await b.calis("TR-4821");
  const okunan = OKUNAN.filter((k) => a[k] !== undefined || c[k] !== undefined).length;
  sonuc.push([ad, { "ic cagri": cagri, "gonderilen bayt": gonderilen, "alinan bayt": alinan,
    "toplam ic bayt": gonderilen + alinan, "  yol adi": b.yol.length, "  yordam adi": b.yordam.length,
    "  alan adi": okunan, "cagiranin bildigi ad": b.yol.length + b.yordam.length + okunan }]);
}
console.log(`${"olcu".padEnd(22)}${sonuc.map(([a]) => a.padStart(16)).join("")}`);
for (const o of OLCU) console.log(`${o.padEnd(22)}${sonuc.map(([, s]) => String(s[o]).padStart(16)).join("")}`);

// K01 Kabaca Buyukluk Hesabi: tepe okuma 416.67 istek/s; K02 ag gecidi toplamasi bu hizi koruyor
const OKUMA_TEPE = 416.67, V5 = 480, K01_CIKIS = 1.6;
console.log(`\n${"bicim".padEnd(22)}${"ic istek/s".padStart(12)}${"ic sinir Mbit/s".padStart(17)}` +
  `${"K01 okuma cikisina oran".padStart(25)}`);
for (const [ad, s] of sonuc) {
  const mbit = (OKUMA_TEPE * s["toplam ic bayt"] * 8) / 1e6;
  console.log(`${ad.padEnd(22)}${(OKUMA_TEPE * s["ic cagri"]).toFixed(2).padStart(12)}` +
    `${mbit.toFixed(3).padStart(17)}${(mbit / K01_CIKIS).toFixed(2).padStart(25)}`);
}
const kaynak = sonuc[0][1]["toplam ic bayt"], yordam = sonuc[1][1]["toplam ic bayt"];
console.log(`kaynak temelli / uzak yordam bayt orani = ${(kaynak / yordam).toFixed(2)};` +
  ` K01'in disa verdigi takip yaniti ${V5} bayt (V5)`);
```

```bash
node iletisim/servis.mjs & p=$!
curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null http://127.0.0.1:8472/ucret/TR-4821
node iletisim/olc.mjs
kill $p
```

```
teslimat-op   127.0.0.1:8471
ucretlendirme 127.0.0.1:8472
olcu                    kaynak temelli     uzak yordam   sorgu temelli
ic cagri                             2               2               2
gonderilen bayt                      0              74              99
alinan bayt                        352             107             107
toplam ic bayt                     352             181             206
cagiranin bildigi ad                 7               8               6
  yol adi                            2               1               1
  yordam adi                         0               2               0
  alan adi                           5               5               5

bicim                   ic istek/s  ic sinir Mbit/s  K01 okuma cikisina oran
kaynak temelli              833.34            1.173                     0.73
uzak yordam                 833.34            0.603                     0.38
sorgu temelli               833.34            0.687                     0.43
kaynak temelli / uzak yordam bayt orani = 1.94; K01'in disa verdigi takip yaniti 480 bayt (V5)
```

#### Değişmeyen Sayı

Tablonun ilk satırı üç kolonda da 2. Bu, dersin en önemli sonucudur: **çağrı biçimi iç çağrı
sayısını değiştirmiyor.** İki çağrı yapılmasının nedeni protokol değil, verinin iki ayrı bağlamda
durmasıdır — durum teslimat operasyonunda, tutar ücretlendirmede. Biçim seçimi bu bölünmeyi ne
kurar ne kaldırır.

Sayının önemi bir sonraki derste ortaya çıkacak: zincirdeki adım sayısı gecikme bütçesinin
bölüneceği parça sayısıdır ve o sayı burada, alan bölünmesiyle belirlenmiştir. Bir biçim seçerek
küçültülemez.

#### Bayt ve Ad

İkinci grup satır iki ölçüyü karşı karşıya koyuyor ve ikisi ters yönde hareket ediyor.

**Bayt.** Kaynak temelli biçim 352 bayt taşıyor, uzak yordam 181, sorgu temelli 206. Kaynak
temelli biçim iki tam kaydı getiriyor: taşıyıcı, ağırlık, hacim, çıkış şubesi ve varış şubesi de
telde geçiyor, çünkü adres bir **kayda** karşılık gelir. Uzak yordam biçiminde sunucu yanıtı
çağrının amacına göre sabitlemiş; sorgu temelli biçim aynı alanları getiriyor ama istenen alan
adları istekte de yazıldığı için gönderilen bayt 99'a çıkıyor. Oran 1,94: aynı iş, iki katına
yakın bayt.

**Ad.** Çağıranın bilmek zorunda olduğu ad sayısı sorgu temelli biçimde 6, kaynak temelli biçimde
7, uzak yordam biçiminde 8. Sıralama bayt sıralamasıyla aynı değil. Uzak yordam biçimi en az baytı
taşıyor ama en çok adı bilmeyi gerektiriyor, çünkü alan adlarının üstüne bir de yordam adı katmanı
ekliyor: `takipOzeti` ve `netUcret` çağıranın koduna yazılmış iki addır ve ikisi de değişebilir.

Ad sayısı bir bağ ölçüsüdür ve bir sonraki dersin doğrudan girdisidir: bir adın değişmesi, o adı
okuyan her tüketicinin değişmesi demektir. Ama adların **sahibi** de sayılmalıdır. Kaynak temelli
biçimdeki beş alan adı kaydın kendisine aittir ve bütün tüketiciler tarafından paylaşılır;
`takipOzeti` yordamı ise tek bir çağıran için yazılmıştır. Aynı sayıda ad, değiştirilmesi aynı
zorlukta olmayabilir.

#### Hesaba Geri Dönüş

Alttaki tablo ölçülen baytı K01'in hızına bağlıyor. Girdiler: tepe okuma 416,67 istek/s (K01) ve
ağ geçidinin toplama kararı, ki o karar dış istek hızını değiştirmiyordu (K02).

İç istek hızı üç biçimde de 833,34 istek/s — dış hızın iki katı, çünkü her dış istek iki iç çağrı
doğuruyor. Bu sayı K01'in tablosunda yoktu; K01 uçtaki hızı sayıyordu, uygulamanın kendi içindeki
çağrıları değil. Servis bölünmesinin sistem üzerindeki ilk sonucu budur: **iç istek hızı dış istek
hızından bağımsız bir sayıdır ve bölünmeyle çarpılır.**

İç sınır bant genişliği kaynak temelli biçimde 1,173 Mbit/s, uzak yordam biçiminde 0,603, sorgu
temelli biçimde 0,687. Karşılaştırma noktası K01'in okuma çıkışıdır: 1,60 Mbit/s. Yani iç sınırdan
geçen trafik, en pahalı biçimde bile dışa verilen trafiğin 0,73 katında kalıyor. Sayı iki şey
söylüyor. Birincisi, iki servisin arasındaki bant genişliği bu ölçekte bir kısıt değildir; biçim
seçimi bayta bakılarak yapılamaz. İkincisi, oran ölçekle büyür — bölünme derinleşip her dış istek
dört ya da altı iç çağrı doğurduğunda iç trafik dış trafiği geçer ve o noktada aynı tablo başka
bir karar verdirir.

#### Özet

- Üç biçim Web API Tasarımı kursunda istemci sınırında karşılaştırılmıştı; servis sınırında iki uç
  da bizim olduğu için ölçüler değişir: iç çağrı sayısı, iç bayt ve çağıranın bildiği ad sayısı.
- İç çağrı sayısı üç biçimde de 2 çıktı: çağrı sayısını belirleyen şey protokol değil, verinin iki
  ayrı bağlamda durmasıdır.
- İç bayt kaynak temelli biçimde 352, uzak yordamda 181, sorgu temellide 206; oran 1,94 ve farkın
  kaynağı adresin bir kayda karşılık gelmesidir.
- Çağıranın bildiği ad sorgu temellide 6, kaynak temellide 7, uzak yordamda 8; uzak yordam en az
  baytı taşırken en çok adı gerektiriyor, çünkü alan adlarına bir yordam adı katmanı ekliyor.
- Adların sayısı kadar sahibi de önemlidir: kayıt alanları bütün tüketicilerle paylaşılır, amaca
  özel bir yordam adı tek çağırana aittir.
- K01'e dönüş: iç istek hızı 833,34 istek/s (dış hızın iki katı) ve iç sınır 0,603–1,173 Mbit/s,
  yani K01'in 1,60 Mbit/s'lik okuma çıkışının 0,38–0,73 katı; oran bölünme derinleştikçe büyür.

#### Sonraki Adım

Bu ders çağıranın bilmek zorunda olduğu adları saydı ve sayının altı ile sekiz arasında değiştiğini
gösterdi. Sayının kendisi tek başına bir şey söylemez; anlamı, o adlardan biri değiştiğinde ortaya
çıkar. Ağ geçidi tek çağıran değildir: aynı iki servisi gün sonu ücretlendirme işi, ön yüz için
kurulan arayüz ve rapor akışı da çağırır ve her biri farklı bir alan altkümesi okur. Sonraki ders
bir alan adı değiştiğinde kaç tüketicinin kırıldığını sayar, aynı değişikliğin sıraya bölünerek
kırılan tüketici sayısının sıfıra indirilebildiğini gösterir ve bunun geçiş penceresinde iç sınıra
ne ödettiğini hesaplar.

### Servisler Arası Sözleşme

Source: https://academia.sh/tr/kurslar/uygulama-katmani/servisler-arasi-sozlesme

Şema değişikliğinin maliyetinin kırılan tüketici sayısıyla ölçülmesi: beş iç tüketicinin okuma kümeleriyle tek adımda yayının kaç servisi birlikte yayımlamaya zorladığının sayılması, aynı değişikliğin genişlet–daralt sırasına bölünerek kırılan tüketicinin sıfıra indirilmesi ve geçiş penceresinde iki adın birden taşınmasının kayıt baytına ve iç sınır bant genişliğine ödettiğinin hesaplanması.

Önceki ders çağıranın bilmek zorunda olduğu adları saydı ve sayının altı ile sekiz arasında
değiştiğini gösterdi. Sayının kendisi tek başına bir şey söylemez; anlamı, o adlardan biri
değiştiğinde ortaya çıkar. Ağ geçidi de tek çağıran değildir — aynı gönderi kaydını gün sonu
ücretlendirme işi, ön yüz için kurulan arayüz, rapor akışı ve teslimat planlayıcı da okur ve her
biri farklı bir alan altkümesine bakar.

Kırıcı değişikliğin tanımı, şemanın iki yönü ve sürüm numarasının farktan türetilmesi Web API
Tasarımı kursunda kuruldu; burada yeniden anlatılmaz. O kurs maliyeti **tek bir eski istemcide**
ölçmüştü. Servis sınırında ölçü değişir: tüketiciler bizim servislerimizdir, sayıları bellidir ve
okuma kümeleri okunabilir. Bu dersin ölçtüğü sayı **kaç tüketicinin kırıldığı**, karşılığında
ölçtüğü sayı **kaç birimin birlikte yayımlanmak zorunda kaldığıdır**.

#### Şema, Tüketiciler ve Değişiklik

Ele alınan şema önceki dersin gönderi kaydıdır. Değişiklik önerisi dört adı düzeltiyor ve bir alan
ekliyor: bölge alanı hangi bölge olduğunu söylemiyordu, ağırlık gram cinsindendi, rota yalnız
bölge kodları taşıyordu ve taşıyıcı alanının bir kod olduğu adından anlaşılmıyordu.

```js
// sozlesme/sema.mjs — gonderi kaydinin iki semasi, bes ic tuketici ve kirilma kurali
export const V1 = { takipNo: "dizgi", durum: "dizgi", bolge: "dizgi", guncellenme: "dizgi",
  rota: "dizi-dizgi", tasiyici: "dizgi", agirlikGram: "tamsayi", hacimDm3: "tamsayi",
  sozlesmeNo: "dizgi", cikisSubesi: "dizgi", varisSubesi: "dizgi" };

// V2 dort adi degistiriyor ve bir alan ekliyor; bolge->varisBolge, agirlikGram->agirlikKg,
// rota->rotaAdimlari (tip de degisiyor), tasiyici->tasiyiciKodu.
export const V2 = { takipNo: "dizgi", durum: "dizgi", varisBolge: "dizgi", guncellenme: "dizgi",
  rotaAdimlari: "dizi-nesne", tasiyiciKodu: "dizgi", agirlikKg: "sayi", hacimDm3: "tamsayi",
  sozlesmeNo: "dizgi", cikisSubesi: "dizgi", varisSubesi: "dizgi", teslimTahmini: "dizgi" };

export const ESLEME = { bolge: "varisBolge", agirlikGram: "agirlikKg",
  rota: "rotaAdimlari", tasiyici: "tasiyiciKodu" };

export const TUKETICI = {
  "ag-gecidi": ["durum", "bolge", "guncellenme", "rota"],
  "on-yuz-arayuzu": ["durum", "guncellenme", "varisSubesi"],
  "rapor-akisi": ["takipNo", "durum", "bolge", "tasiyici"],
  "toplu-ucretlendirme": ["takipNo", "sozlesmeNo", "agirlikGram", "hacimDm3"],
  "teslimat-planlayici": ["rota", "tasiyici", "cikisSubesi", "varisSubesi"],
};

// Bir tuketici, okudugu ad semada yoksa ya da tipi degistiyse kirilir.
export const kirilan = (sema, okuma) => Object.entries(okuma)
  .filter(([, alanlar]) => alanlar.some((a) => sema[a] === undefined || sema[a] !== V1[a]))
  .map(([ad]) => ad);

export const ORNEK = {
  takipNo: "TR-4821", durum: "dagitimda", bolge: "35", guncellenme: "2026-03-11T08:24:00Z",
  rota: ["34", "41", "35"], tasiyici: "T3", agirlikGram: 2400, hacimDm3: 12,
  sozlesmeNo: "S7", cikisSubesi: "34-021", varisSubesi: "35-114",
  varisBolge: "35", rotaAdimlari: [{ bolge: "34", an: "T08" }, { bolge: "41", an: "T14" },
    { bolge: "35", an: "T20" }], tasiyiciKodu: "T3", agirlikKg: 2.4, teslimTahmini: "2026-03-12",
};
export const kayit = (sema) => Object.fromEntries(Object.keys(sema).map((a) => [a, ORNEK[a]]));
```

**Genişlet–daralt** (expand–contract), aynı değişikliği üç adıma bölen yayın sırasıdır. Birinci
adımda sağlayıcı yeni adları eskilerin **yanına** ekler ve ikisini birden doldurur. İkinci adımda
tüketiciler tek tek yeni ada geçer. Üçüncü adımda sağlayıcı eski adları kaldırır. Aşağıdaki koşum
iki yayın düzenini aynı değişiklik üzerinde karşılaştırır.

```js
// sozlesme/olc.mjs — ayni degisiklik iki yayin duzeninde: tek adimda ve genislet-daralt sirasiyla
import { V1, V2, ESLEME, TUKETICI, kirilan, kayit } from "./sema.mjs";

const GECIS = { ...V1, ...V2 };                       // genislet adimi: eski ve yeni adlar bir arada
const gocmus = Object.fromEntries(Object.entries(TUKETICI)
  .map(([ad, a]) => [ad, a.map((x) => ESLEME[x] ?? x)]));
const tuketici = Object.keys(TUKETICI).length;

console.log(`sema alani V1 ${Object.keys(V1).length}, gecis ${Object.keys(GECIS).length}, ` +
  `V2 ${Object.keys(V2).length}; ic tuketici ${tuketici}`);
const sayim = {};
for (const alanlar of Object.values(TUKETICI)) for (const a of alanlar) sayim[a] = (sayim[a] ?? 0) + 1;
const paylasilan = Object.entries(sayim).sort((x, y) => y[1] - x[1]).slice(0, 4);
console.log(`en cok okunan alanlar: ${paylasilan.map(([a, n]) => `${a}=${n}`).join(", ")}\n`);

const k1 = kirilan(V2, TUKETICI);
console.log(`tek adimda yayin -> kirilan tuketici ${k1.length}/${tuketici}: ${k1.join(", ")}`);
console.log(`  birlikte yayimlanmasi gereken birim = ${k1.length + 1} (saglayici + kirilanlar)\n`);

const ADIM = [
  ["1 genislet (iki ad birden)", GECIS, TUKETICI, 1],
  ["2 tuketiciler yeni ada gecer", GECIS, gocmus, Object.entries(TUKETICI)
    .filter(([ad, a]) => a.some((x) => ESLEME[x])).length],
  ["3 daralt (eski adlar kalkar)", V2, gocmus, 1],
];
console.log(`${"adim".padEnd(30)}${"semadaki alan".padStart(14)}${"kirilan tuketici".padStart(18)}` +
  `${"yayimlanan birim".padStart(18)}${"kayit bayt".padStart(12)}`);
let birim = 0;
for (const [ad, sema, okuma, n] of ADIM) {
  birim += n;
  const kirik = Object.entries(okuma).filter(([, alanlar]) =>
    alanlar.some((a) => sema[a] === undefined)).length;
  console.log(`${ad.padEnd(30)}${String(Object.keys(sema).length).padStart(14)}` +
    `${`${kirik}/${tuketici}`.padStart(18)}${String(n).padStart(18)}` +
    `${String(Buffer.byteLength(JSON.stringify(kayit(sema)))).padStart(12)}`);
}
console.log(`toplam: 3 yayin adimi, ${birim} birim yayini, 0 kirilan tuketici\n`);

// K01 Kabaca Buyukluk Hesabi: tepe okuma 416.67 istek/s ve okuma cikisi 1.60 Mbit/s
const OKUMA_TEPE = 416.67, K01_CIKIS = 1.6;
const mbit = (bayt) => (OKUMA_TEPE * bayt * 8) / 1e6;
console.log(`${"sema".padEnd(10)}${"kayit bayt".padStart(12)}${"ic sinir Mbit/s".padStart(17)}` +
  `${"K01 okuma cikisina oran".padStart(26)}`);
for (const [ad, s] of [["V1", V1], ["gecis", GECIS], ["V2", V2]]) {
  const b = Buffer.byteLength(JSON.stringify(kayit(s)));
  console.log(`${ad.padEnd(10)}${String(b).padStart(12)}${mbit(b).toFixed(3).padStart(17)}` +
    `${(mbit(b) / K01_CIKIS).toFixed(2).padStart(26)}`);
}
const gB = Buffer.byteLength(JSON.stringify(kayit(GECIS)));
const vB = Buffer.byteLength(JSON.stringify(kayit(V1)));
console.log(`gecis penceresinin bedeli = ${gB - vB} bayt/kayit, ` +
  `${(mbit(gB) - mbit(vB)).toFixed(3)} Mbit/s (kayit ${(gB / vB).toFixed(2)} katina cikiyor)`);
```

```
sema alani V1 11, gecis 16, V2 12; ic tuketici 5
en cok okunan alanlar: durum=3, bolge=2, guncellenme=2, rota=2

tek adimda yayin -> kirilan tuketici 4/5: ag-gecidi, rapor-akisi, toplu-ucretlendirme, teslimat-planlayici
  birlikte yayimlanmasi gereken birim = 5 (saglayici + kirilanlar)

adim                           semadaki alan  kirilan tuketici  yayimlanan birim  kayit bayt
1 genislet (iki ad birden)                16               0/5                 1         406
2 tuketiciler yeni ada gecer              16               0/5                 4         406
3 daralt (eski adlar kalkar)              12               0/5                 1         334
toplam: 3 yayin adimi, 6 birim yayini, 0 kirilan tuketici

sema        kayit bayt  ic sinir Mbit/s   K01 okuma cikisina oran
V1                 228            0.760                      0.48
gecis              406            1.353                      0.85
V2                 334            1.113                      0.70
gecis penceresinin bedeli = 178 bayt/kayit, 0.593 Mbit/s (kayit 1.78 katina cikiyor)
```

#### Kırılan Tüketici Sayısı

Bu sayılar **hesap** sınıfındadır: okuma kümelerinden ve şema tanımlarından aritmetikle çıkar.

Tek adımda yayında beş tüketicinin dördü kırılıyor. Kırılmayan tek tüketici, adı değişen dört
alandan hiçbirini okumayan ön yüz arayüzü. Bu satırın sonucu bir sürüm numarası değil, bir yayın
kısıtıdır: sağlayıcı ve dört tüketici **aynı anda** yayımlanmak zorundadır. Beş birimin tek bir
yayında birlikte hareket etmesi, servislere bölmenin gerekçesini geri alır — bölünmenin amacı
birimlerin ayrı ayrı yayımlanabilmesiydi.

İkinci satır maliyetin nereden geldiğini gösteriyor: `durum` alanını üç tüketici, `bolge`,
`guncellenme` ve `rota` alanlarını ikişer tüketici okuyor. Bir alanın değiştirilme maliyeti onu
okuyan tüketici sayısıdır. Önceki dersin ayrımı burada sayıya dönüşüyor — paylaşılan bir kayıt
alanı, tek bir çağıran için yazılmış bir yordam adından pahalıdır.

#### Sıraya Bölmenin Sonucu

Alttaki tablo aynı değişikliği üç adıma böldüğünde kırılan tüketicinin her adımda 0 kaldığını
gösteriyor. Birinci adımda şema on bir alandan on altıya çıkıyor ve eski adlar yerinde kaldığı için
hiçbir tüketici değişmiyor. İkinci adımda dört tüketici yeni adlara geçiyor ve bunu **ayrı ayrı**
yapabiliyor; sıraları önemsizdir, çünkü iki ad da doludur. Üçüncü adımda eski adlar kalkıyor ve
şema on iki alana iniyor.

İki düzenin karşılaştırması iki sayıyla yapılır. Tek adımda yayın: 1 yayın adımı, 5 birim aynı
anda, 4 kırılan tüketici. Genişlet–daralt: 3 yayın adımı, 6 birim yayını, 0 kırılan tüketici. Bir
birim fazla yayımlanıyor ve karşılığında hiçbir birim ötekini beklemiyor.

Sıranın gerektirdiği bir kural vardır ve tabloda görünmez: **birinci adım yayımlandıktan sonra
tüketicilerin gerçekten göç ettiğini bilmek gerekir.** Yoksa üçüncü adım tek adımda yayının
kendisine dönüşür. Bunu bilmenin yolu Web API Tasarımı kursunda kurulan sözleşme sınamalarıdır;
tüketici beklentileri yazılıysa üçüncü adımın güvenli olup olmadığı yayından önce sınanabilir.

#### Hesaba Geri Dönüş

Alttaki tablo geçişin faturasını veriyor. Gönderi kaydı V1'de 228 bayt; geçiş penceresinde iki ad
birden taşındığı için 406 bayta çıkıyor, yani 1,78 katına. V2'ye inildiğinde 334 baytta kalıyor —
V1'in üstünde, çünkü rota adımları artık nesne taşıyor ve bir alan eklendi.

K01'in tepe okuma hızıyla çarpıldığında iç sınırdan geçen bant genişliği 0,760 Mbit/s'den 1,353
Mbit/s'ye çıkıyor: geçiş penceresinin bedeli 0,593 Mbit/s. Karşılaştırma noktası K01'in okuma
çıkışıdır (1,60 Mbit/s); geçiş penceresinde iç sınır bu sayının 0,85 katına yaklaşıyor, oysa V1'de
0,48 katındaydı.

Buradan çıkan sonuç geçiş penceresinin bir **süre kararı** olmasıdır. Pencere açık kaldığı sürece
her kayıt yüzde yetmiş sekiz fazla bayt taşır; kapatmak için bütün tüketicilerin göç etmesi
beklenir. Uzun pencere kırılma riskini sıfırda tutar ve sürekli bir bant genişliği vergisi öder;
kısa pencere vergiyi keser ve göçü tamamlamayan bir tüketici kalırsa kırılmayı geri getirir. İki
uçtan hangisinin seçileceği, üçüncü adımın güvenli olduğunu gösteren sınamaya bağlıdır.

#### Özet

- Servis sınırında şema değişikliğinin ölçüsü kırılan tüketici sayısı ve birlikte yayımlanmak
  zorunda kalan birim sayısıdır; kırıcılık tanımı ve sürüm türetimi Web API Tasarımı kursunda
  kuruldu ve burada tekrarlanmaz.
- Dört adı değiştiren bir öneri beş iç tüketicinin dördünü kırıyor ve sağlayıcıyla birlikte beş
  birimin aynı anda yayımlanmasını gerektiriyor.
- Bir alanın değiştirilme maliyeti onu okuyan tüketici sayısıdır: `durum` üç, `bolge`,
  `guncellenme` ve `rota` ikişer tüketici tarafından okunuyor.
- Genişlet–daralt sırası aynı değişikliği 3 yayın adımı ve 6 birim yayınıyla, 0 kırılan tüketiciyle
  yapıyor; ikinci adımda tüketiciler birbirini beklemeden göç ediyor.
- Sıranın güvenliği üçüncü adıma bağlıdır: göçün tamamlandığı sınanmadan eski adlar kaldırılırsa
  düzen tek adımda yayına döner.
- K01'e dönüş: kayıt 228 baytken geçiş penceresinde 406 bayta (1,78 katı) çıkıyor ve iç sınır
  0,760'tan 1,353 Mbit/s'ye yükseliyor — 0,593 Mbit/s'lik bir pencere vergisi, K01'in 1,60 Mbit/s
  okuma çıkışının 0,85 katı.

#### Sonraki Adım

Dört ders sonunda çağrı yapılabilir durumda: servisler durumsuz, adresleri bulunuyor, konuşma
biçimi seçilmiş ve sözleşmeleri kırılmadan evrilebiliyor. Bir soru hâlâ açık ve o soru bu konunun
en baştaki ölçüsüne geri döner. Ağ geçidi iki servisi çağırdığında birinden yanıt gelmezse ne
kadar bekleyecek, kaç kez deneyecek ve bu bekleme uçtan uca yanıta ne ekleyecek. K01 takip
yanıtı için 200 milisaniyelik bir eşik koymuştu ve zincirdeki adım sayısı üçüncü derste 2 olarak
ölçülmüştü. Sonraki ders o eşiği bir bütçe olarak ele alır: zaman aşımı ile deneme sayısının
çarpımının en kötü durumda ne verdiğini, bütçenin adımlara nasıl bölüşüldüğünü ve deneme
bütçesi konmadığında iç istek hızının kaça katlandığını ölçer.

### Zaman Aşımı ve Yeniden Deneme Bütçesi

Source: https://academia.sh/tr/kurslar/uygulama-katmani/zaman-asimi-ve-yeniden-deneme-butcesi

Uçtan uca gecikme eşiğinin zincirdeki adımlara bölüştürülmesi: zaman aşımı ile deneme sayısının çarpımının en kötü durumda bütçeyi kaç katına çıkardığının hesaplanması, son tarih aktarımının eşik aşımını erken başarısızlığa çevirmesi, kısa zaman aşımının geçici yavaşlığı soğurması ve deneme bütçesi konmadığında yaygın yavaşlamada iç istek hızının kaça katlandığının modelle ölçülmesi.

Dört ders sonunda çağrı yapılabilir durumda: servisler durumsuz, adresleri çalışma zamanında
bulunuyor, konuşma biçimi seçilmiş ve sözleşmeleri kırılmadan evrilebiliyor. Bir soru hâlâ açık ve
o soru bu konunun ilk ölçüsüne geri döner. Ağ geçidi iki servisi çağırdığında birinden yanıt
gelmezse ne kadar bekleyecek, kaç kez deneyecek ve bu bekleme uçtan uca yanıta ne ekleyecek.

Yeniden deneme kararı, üstel geri çekilme, sarsıntı ve devre kesici Uygulama Mimarisi kursunda
kuruldu; algoritmalar burada yeniden anlatılmaz. Bu dersin ele aldığı şey bir **bütçedir**.
Bütçenin kaynağı K01'dir: takip okumasının sıkı eşiği **200 milisaniye** ve o eşiğin kaynağı
alıcının takip sayfasından beklediği yanıt süresidir. Zincirdeki adım sayısı da ölçülmüştü —
üçüncü ders bir dış istek için iki iç çağrı saydı ve bunun protokolden değil alan bölünmesinden
geldiğini gösterdi.

#### Bütçenin Bölüştürülmesi

Zincir şudur: ağ geçidi iki servisi paralel çağırır, her servis kendi deposunu çağırır. Derinlik
2, kol 2, toplam dört adım çağrısı. Ağ geçidinin kendi işi de bir pay ister; bu **bu kursun
varsayımıdır**.

**U3 — ağ geçidinin kendi payı 20 ms.** Gerekçe: doğrulama, kapı denetimleri ve iki yanıtın
birleştirilmesi K02'de kenara taşınmıştı ve bu işler bütçeden yer tutar. Duyarlılığı aşağıda
hesaplandı; K01'in tablosuna eklenmez.

```js
// zincir/butce.mjs — uctan uca butcenin zincire bolusturulmesi; belirlenimli aritmetik
const UCTAN_UCA = 200;      // K01 Islevsel Olmayan Gereksinimler: okuma esigi (siki) 200 ms
const GECIT_PAYI = 20;      // varsayim U3: ag gecidinin kendi isine ayirdigi pay (ms)
const ADIM_ORTANCA = 8;     // model parametresi: bir adim cagrisinin olagan suresi (ms)
const kalan = UCTAN_UCA - GECIT_PAYI;

console.log(`uctan uca ${UCTAN_UCA} ms, gecidin payi ${GECIT_PAYI} ms, adimlara kalan ${kalan} ms\n`);
console.log(`${"derinlik".padStart(9)}${"deneme".padStart(8)}${"butcesiz en kotu(ms)".padStart(22)}` +
  `${"butceye oran".padStart(14)}${"bolusturulmus zaman asimi(ms)".padStart(31)}${"ortancaya oran".padStart(16)}`);
for (const k of [1, 2, 3, 4]) {
  for (const d of [1, 3]) {
    const butcesiz = k * d * UCTAN_UCA + GECIT_PAYI;
    const t = kalan / (k * d);
    console.log(`${String(k).padStart(9)}${String(d).padStart(8)}${String(butcesiz).padStart(22)}` +
      `${`x${(butcesiz / UCTAN_UCA).toFixed(1)}`.padStart(14)}${t.toFixed(1).padStart(31)}` +
      `${`x${(t / ADIM_ORTANCA).toFixed(2)}`.padStart(16)}`);
  }
}
const enDerin = Math.floor(kalan / (3 * ADIM_ORTANCA));
console.log(`\ndeneme 3 ve adim ortancasi ${ADIM_ORTANCA} ms iken zaman asimi ortancanin altina ` +
  `dusmeden en cok ${enDerin} adim derinlige inilebilir`);
console.log(`duyarlilik: gecit payi ${GECIT_PAYI} -> ${GECIT_PAYI * 2} ms olsaydi derinlik 2, deneme 3 icin ` +
  `zaman asimi ${(kalan / 6).toFixed(1)} -> ${((UCTAN_UCA - 2 * GECIT_PAYI) / 6).toFixed(1)} ms`);
```

```
uctan uca 200 ms, gecidin payi 20 ms, adimlara kalan 180 ms

 derinlik  deneme  butcesiz en kotu(ms)  butceye oran  bolusturulmus zaman asimi(ms)  ortancaya oran
        1       1                   220          x1.1                          180.0          x22.50
        1       3                   620          x3.1                           60.0           x7.50
        2       1                   420          x2.1                           90.0          x11.25
        2       3                  1220          x6.1                           30.0           x3.75
        3       1                   620          x3.1                           60.0           x7.50
        3       3                  1820          x9.1                           20.0           x2.50
        4       1                   820          x4.1                           45.0           x5.63
        4       3                  2420         x12.1                           15.0           x1.88

deneme 3 ve adim ortancasi 8 ms iken zaman asimi ortancanin altina dusmeden en cok 7 adim derinlige inilebilir
duyarlilik: gecit payi 20 -> 40 ms olsaydi derinlik 2, deneme 3 icin zaman asimi 30.0 -> 26.7 ms
```

Bu sayılar **hesap** sınıfındadır. Üçüncü kolon en sık yapılan hatayı gösteriyor: her adıma
uçtan uca eşiğin kendisi zaman aşımı olarak verilirse, derinlik 2 ve deneme 3'te en kötü süre 1220
milisaniyedir — bütçenin 6,1 katı. Zaman aşımını eşiğe eşitlemek bütçeyi korumaz; **her adım tek
başına bütçenin tamamını harcayabilir hâle gelir.**

Beşinci kolon doğru bölüştürmeyi veriyor ve bir sınır getiriyor. Adıma düşen zaman aşımı, derinlik
ve deneme sayısının çarpımına bölünür: derinlik 2 ve deneme 3'te 30 milisaniye, derinlik 4'te 15
milisaniye. Son kolon bunun neden bir sınır olduğunu söylüyor — zaman aşımı adımın olağan süresine
yaklaştıkça olağan istekler de zaman aşımına uğramaya başlar. Sekiz milisaniyelik bir adım
ortancasında ve üç denemede bütçe en çok yedi adım derinliğe dayanır.

Bu, servis bölünmesi ile gecikme eşiği arasındaki bağdır: **zincire eklenen her adım bütçeyi
böler, sonsuza kadar bölünemez.**

#### Model

Aşağıdaki koşum bir **modeldir**: mantıksal saat kullanır, adım süreleri bir döngüden okunur ve
yavaşlama parametreyle tetiklenir. İki rejimde çalışır — olağan rejimde adımların çoğu hızlıdır ve
arada bir yanıt vermeyen bir adım geçer; yaygın yavaşlama rejiminde adımların çoğu yanıt vermez.
Gerçek bir servis kümesi ya da ağ kurulmaz.

```js
// zincir/kosum.mjs — zincirin MODELI: mantiksal saat, adim sureleri parametre, iki rejim.
// Zincir: ag gecidi -> (teslimat-op, ucretlendirme) paralel -> her biri kendi deposu. Derinlik 2, kol 2.
const N = 40, ESIK = 200, GECIT_PAYI = 20, DERINLIK = 2, KOL = 2, ADIM = DERINLIK * KOL;
const REJIM = {                                   // model parametresi: adim cagrisinin suresi (ms)
  olagan: [6, 7, 6, 8, 6, 7, 6, 45, 6, 7, 6, 8, 6, 7, 6, 1000],
  "yaygin yavaslama": [6, 1000, 1000, 1000, 6, 1000, 1000, 1000],
};

function kosum({ zamanAsimi, deneme, sonTarih, denemeButcesi }, rejim) {
  let s = 0, birincil = 0, ek = 0;
  const sayim = { yanit: 0, esikDisi: 0, dusen: 0 }, uctanUca = [];
  const sure = () => REJIM[rejim][s++ % REJIM[rejim].length];
  const izin = () => denemeButcesi === null || ek < denemeButcesi * birincil;

  const adim = (kalan) => {                       // tek adim: zaman asimi ve yeniden deneme
    let gecen = 0;
    for (let i = 0; i < deneme; i += 1) {
      const zt = sonTarih ? Math.min(zamanAsimi, kalan - gecen) : zamanAsimi;
      if (zt <= 0) return { ok: false, gecen };
      if (i === 0) birincil += 1; else ek += 1;
      const su = sure();
      if (su <= zt) return { ok: true, gecen: gecen + su };
      gecen += zt;
      if (i + 1 < deneme && izin() === false) return { ok: false, gecen };
    }
    return { ok: false, gecen };
  };
  const kol = () => {                             // bir kol: DERINLIK adet ardisik adim
    let gecen = 0, ok = true;
    for (let d = 0; d < DERINLIK && ok; d += 1) {
      const r = adim(ESIK - GECIT_PAYI - gecen);
      gecen += r.gecen; ok = r.ok;
    }
    return { ok, gecen };
  };

  for (let i = 0; i < N; i += 1) {
    const kollar = Array.from({ length: KOL }, kol);   // paralel: sure kollarin en buyugudur
    const gecen = GECIT_PAYI + Math.max(...kollar.map((k) => k.gecen));
    uctanUca.push(gecen);
    if (kollar.every((k) => k.ok) === false) sayim.dusen += 1;
    else if (gecen > ESIK) sayim.esikDisi += 1;
    else sayim.yanit += 1;
  }
  const sirali = [...uctanUca].sort((a, b) => a - b);
  return { ...sayim, ortanca: sirali[N >> 1], enKotu: sirali[N - 1],
    carpan: (birincil + ek) / (N * ADIM) };
}

const YAPILANDIRMA = [
  ["butce yok (200/3)", { zamanAsimi: 200, deneme: 3, sonTarih: false, denemeButcesi: null }],
  ["+ son tarih aktarimi", { zamanAsimi: 200, deneme: 3, sonTarih: true, denemeButcesi: null }],
  ["+ bolusturulmus (30/3)", { zamanAsimi: 30, deneme: 3, sonTarih: true, denemeButcesi: null }],
  ["+ deneme butcesi 0.30", { zamanAsimi: 30, deneme: 3, sonTarih: true, denemeButcesi: 0.3 }],
];
const OKUMA_TEPE = 416.67, IC_TABAN = OKUMA_TEPE * ADIM;   // K01: tepe okuma; 03. ders: adim sayisi

console.log(`${N} dis istek, derinlik ${DERINLIK}, kol ${KOL}, adim ${ADIM}; ` +
  `uctan uca esik ${ESIK} ms (K01), gecit payi ${GECIT_PAYI} ms (U3)`);
console.log(`ic istek/s tabani = ${OKUMA_TEPE} x ${ADIM} = ${IC_TABAN.toFixed(2)}`);
for (const rejim of Object.keys(REJIM)) {
  console.log(`\n-- ${rejim}: ${REJIM[rejim].filter((v, i, a) => a.indexOf(v) === i).join("/")} ms dongusu --`);
  console.log(`${"yapilandirma".padEnd(24)}${"esik ici".padStart(9)}${"esik disi".padStart(10)}` +
    `${"dusen".padStart(7)}${"ortanca".padStart(9)}${"en kotu".padStart(9)}${"carpan".padStart(8)}` +
    `${"ic istek/s".padStart(12)}`);
  for (const [ad, y] of YAPILANDIRMA) {
    const r = kosum(y, rejim);
    console.log(`${ad.padEnd(24)}${String(r.yanit).padStart(9)}${String(r.esikDisi).padStart(10)}` +
      `${String(r.dusen).padStart(7)}${String(r.ortanca).padStart(9)}${String(r.enKotu).padStart(9)}` +
      `${`x${r.carpan.toFixed(2)}`.padStart(8)}${(IC_TABAN * r.carpan).toFixed(2).padStart(12)}`);
  }
}
console.log(`\nsinirsiz denemenin ust siniri: her adim 3 deneme -> ${(IC_TABAN * 3).toFixed(2)} istek/s`);
```

```
40 dis istek, derinlik 2, kol 2, adim 4; uctan uca esik 200 ms (K01), gecit payi 20 ms (U3)
ic istek/s tabani = 416.67 x 4 = 1666.68

-- olagan: 6/7/8/45/1000 ms dongusu --
yapilandirma             esik ici esik disi  dusen  ortanca  en kotu  carpan  ic istek/s
butce yok (200/3)              30        10      0       71      233   x1.06     1770.85
+ son tarih aktarimi           30         0     10       71      200   x1.00     1666.68
+ bolusturulmus (30/3)         40         0      0       62       63   x1.14     1895.85
+ deneme butcesi 0.30          40         0      0       62       63   x1.14     1895.85

-- yaygin yavaslama: 6/1000 ms dongusu --
yapilandirma             esik ici esik disi  dusen  ortanca  en kotu  carpan  ic istek/s
butce yok (200/3)               0         0     40      626      626   x2.00     3333.36
+ son tarih aktarimi            0         0     40      200      200   x0.67     1114.59
+ bolusturulmus (30/3)          0         0     40      116      116   x2.00     3333.36
+ deneme butcesi 0.30           0         0     40       86       86   x0.97     1614.60

sinirsiz denemenin ust siniri: her adim 3 deneme -> 5000.04 istek/s
```

#### Dört Satırın Okunması

**Son tarih aktarımı eşik aşımını erken başarısızlığa çevirir.** Olağan rejimde bütçesiz düzen
kırk isteğin otuzunu eşik içinde yanıtlıyor, onunu eşiğin dışında — en kötü 233 milisaniye.
Çağıran kalan bütçeyi çağrıyla birlikte geçirdiğinde en kötü süre tam 200 milisaniyede kesiliyor
ve o on istek eşik dışı olmaktan çıkıp düşen istek oluyor. Değiş tokuş açıktır: geç bir yanıt ile
zamanında bir başarısızlık arasında seçim yapılıyor. Kullanıcının 233 milisaniyede aldığı yanıt
zaten eşiği karşılamıyordu; onu üretmek için harcanan iş de karşılıksızdı.

**Kısa zaman aşımı geçici yavaşlığı soğuruyor.** Bölüştürülmüş düzende zaman aşımı 30
milisaniyeye indiğinde kırk isteğin kırkı eşik içinde yanıtlanıyor ve en kötü süre 63 milisaniye
oluyor. Nedeni modelin varsayımıdır: yavaşlık geçicidir, aynı adımın ikinci çağrısı hızlı döner.
Uzun zaman aşımı bu geçici yavaşlığı bekleyerek uçtan uca yanıta taşıyor, kısa zaman aşımı ise onu
kesip yeniden deniyor. Karşılığı çağrı çarpanının 1,00'den 1,14'e çıkması: iç istek hızı 1666,68'den
1895,85 istek/s'ye yükseliyor.

**Deneme bütçesi olağan rejimde bağlamıyor.** Dördüncü satır üçüncüyle aynı. Gözlenen çarpan 1,14
ve bütçe 1,30 olduğu için sınır hiç devreye girmiyor. Bir bütçenin olağan koşulda görünmez olması
doğru boyutlandırıldığının işaretidir.

#### Yaygın Yavaşlamada Ne Değişiyor

İkinci tablo aynı yapılandırmaları adımların çoğunun yanıt vermediği bir rejimde koşturuyor ve
sıralama tersine dönüyor.

Bütçesiz düzende kırk isteğin kırkı düşüyor ve çağıran bunu öğrenmek için 626 milisaniye
bekliyor. Son tarih aktarımı bu süreyi 200 milisaniyede kesiyor; çarpan 0,67'ye iniyor, çünkü
bütçe tükendiğinde yapılmayan birincil çağrılar da var. İç istek hızı 3333,36'dan 1114,59
istek/s'ye düşüyor.

Üçüncü satır dersin en önemli sayısıdır. Zaman aşımı 30 milisaniyeye indirildiğinde uçtan uca
süre 116 milisaniyeye iniyor — ama çarpan yeniden 2,00'ye çıkıyor ve iç istek hızı 3333,36 istek/s
oluyor. Kısa zaman aşımı, aynı bütçeye daha çok deneme sığdırır; **zaten yavaşlamış olan servis,
kendisini yavaşlatan sistemden iki kat istek alır.** Olağan rejimde iyi olan karar, arıza
rejiminde zararlıdır.

Dördüncü satır bunu kesiyor. Deneme bütçesi ek denemeleri birincil çağrıların yüzde otuzuyla
sınırladığında çarpan 0,97'ye, iç istek hızı 1614,60 istek/s'ye iniyor: iki kat yükseltme
yerine taban hızın altında bir yük. Uçtan uca süre de 116'dan 86 milisaniyeye iniyor, çünkü
boşa giden denemeler yapılmıyor. Sınırsız denemenin üst sınırı karşılaştırma noktasıdır — her
adım üç kez denenirse 5000,04 istek/s, K01'in uçtaki tepe hızının on iki katı bir iç yük.

Bütün bunlar yalnız yeniden denenebilir çağrılar için geçerlidir. Durum değiştiren bir çağrının
yeniden denenebilmesi, aynı çağrının iki kez işlenmesinin tek kez işlenmesiyle aynı sonucu
vermesine bağlıdır; bu özelliğe **etkisiz** (idempotent) denir ve HTTP yöntemlerinin etkisizlik
özelliğiyle aynı kavramdır. Etkisiz olmayan bir çağrı ancak istemcinin ürettiği bir **tekillik
anahtarı** sözleşmeye yazılmışsa yeniden denenebilir; ikisi de Web API Tasarımı kursunda kuruldu.

#### Özet

- Uçtan uca eşik bir bütçedir ve zincire bölüştürülür; her adıma eşiğin tamamı zaman aşımı olarak
  verilirse derinlik 2 ve deneme 3'te en kötü süre bütçenin 6,1 katına çıkar.
- Bölüştürülmüş zaman aşımı derinlik ile deneme sayısının çarpımına bölünür (derinlik 2, deneme
  3'te 30 ms) ve adımın olağan süresine yaklaştığında sınıra dayanır: 8 ms ortanca ve 3 denemeyle
  bütçe en çok 7 adım derinliğe dayanır.
- Son tarih aktarımı en kötü süreyi eşikte kesiyor: olağan rejimde 233 ms ve 10 eşik dışı yanıt,
  200 ms ve 10 düşen isteğe dönüşüyor — geç yanıt yerine zamanında başarısızlık.
- Kısa zaman aşımı geçici yavaşlığı soğuruyor (40/40 eşik içi, en kötü 63 ms) ve bedeli çağrı
  çarpanının 1,00'den 1,14'e, iç istek hızının 1666,68'den 1895,85 istek/s'ye çıkmasıdır.
- Yaygın yavaşlamada aynı karar tersine dönüyor: kısa zaman aşımı çarpanı 2,00'ye ve iç istek
  hızını 3333,36 istek/s'ye çıkarıyor; deneme bütçesi 0,30 bunu 0,97 ve 1614,60 istek/s'ye
  indiriyor ve uçtan uca süreyi 116'dan 86 ms'ye düşürüyor.
- Yeniden deneme yalnız etkisiz çağrılar için serbesttir; etkisiz olmayan bir çağrı ancak
  sözleşmede bir tekillik anahtarı varsa denenebilir.

#### Sonraki Adım

Bu konu **eşzamanlı** çağrıyı tasarladı: çağıran bekler, bütçe işler, sonunda ya yanıt gelir ya
hata. Bütçe hesabı bir sınırı görünür kıldı — zincir uzadıkça ya bütçe parçalara bölünüp her adım
daralır ya da uçtan uca süre büyür. İki seçenek de aynı varsayımın içindedir: çağıran işin
sonucunu bekliyor. Bazı işler için üçüncü bir yol vardır ve o yol varsayımı kaldırır — çağıran hiç
beklemez. Taşıyıcıdan gelen durum olayının deponun içine yazılması, isteği yanıtlamak için
tamamlanmak zorunda değildir. Sonraki konu isteği bir kuyruğa bırakmanın ne değiştirdiğini ölçer
ve bunu bir kapasite sorusu olarak sorar: eşzamanlı yolda kapasite anlık tepeye göre seçilmek
zorundadır, kuyruk konduğunda ortalamaya yaklaştırılabilir; kazancın bekleyen iş ve bekleme süresi
cinsinden ne ödettiği ve K01'in hangi sayısının değiştiği orada hesaplanır.

## Kuyruklar ve İş Akışları

Yükün zamana yayılması ve çok adımlı işlerin yürütülmesi: mesaj kuyruklarıyla tamponlama, uzun işlerin arka plana alınması, rekabet eden tüketicilerle verim artırma, ani yükün kuyrukla düzlenmesi, farklı hizmet düzeyleri için öncelik, sıra gerektiren işlerin yönetimi, dağıtık iş akışının koreografi ve orkestrasyon modelleri, dayanıklı iş akışı kalıbı, tekil sorumluluk için lider seçimi ve uzun süren işlemde eşzamansız yanıt döndürme.

### Mesaj Kuyrukları

Source: https://academia.sh/tr/kurslar/uygulama-katmani/mesaj-kuyruklari

Kuyruğun bir kapasite kararı olarak ele alınması: eşzamanlı yazma yolunda kapasitenin anlık tepeye göre seçilmesi, kuyruk konduğunda işleme kapasitesinin ortalamaya yaklaştırılması, kazancın birikme ve bekleme cinsinden ödenmesi, deponun kısa bir kesintisinde iki tasarımın kayıp sayısıyla karşılaştırılması ve depoya ulaşan istek hızının yeniden hesaplanması.

Önceki konu eşzamanlı çağrıyı tasarladı: çağıran bekler, bir zaman aşımı bütçesi işler, sürenin
sonunda ya bir yanıt gelir ya bir hata. Konunun son dersi o bütçenin aritmetiğini çıkardı ve bir
sınırı görünür kıldı — zincir uzadıkça ya bütçe parçalara bölünür ve her adım daralır, ya da
uçtan uca süre büyür. İki seçenek de aynı varsayımın içindedir: çağıran işin sonucunu bekliyor.
Bazı işler için üçüncü bir yol vardır ve o yol varsayımı kaldırır — çağıran hiç beklemez.
Taşıyıcıdan gelen durum olayının deponun içine yazılması, isteği yanıtlamak için tamamlanmak
zorunda değildir.

Bu konu o yolu ele alır. **Mesaj kuyruğu** (message queue) mekaniği Önbellekleme, Kuyruklar ve
Eşzamansız İşleme kursunda kuruldu ve ölçüldü: üreticinin yazması, tüketicinin alması, onay,
görünmezlik süresi, teslim garantileri, tüketici grupları, sıralama ve ölü mektup kuyruğu. Burada
hiçbiri yeniden anlatılmaz. Bu kursun sorusu başkadır ve tek cümleyle söylenir: **kuyruk bir
kapasite kararıdır.** Yük zamana yayıldığında ne kazanılır, ne ödenir ve giriş kursunun hangi
sayısı değişir.

#### Kapasite Hangi Sayıya Göre Seçilir

Eşzamanlı yazma yolunda bir kural vardır ve tartışılmaz: kapasite **anlık tepe** hızına göre
seçilir. Taşıyıcı bir durum olayı gönderip yanıt bekliyorsa, o anda gelen her isteğin işlenecek
bir yeri olmalıdır; olmayan istek reddedilir. Sistem Tasarımına Giriş kursunun Kabaca Büyüklük
Hesabı dersi bu sayıyı veriyor: **tepe yazma 97,22 istek/s** — hesap sınıfı, günde 2.800.000
durum olayından, gün ortalaması 32,41 istek/s ve V8 tepe çarpanı 3'ten çıkıyor.

Kuyruk bu kuralı ikiye böler. Taşıyıcının konuştuğu uç yine tepeyi kabul etmek zorundadır, ama
orada yaptığı iş tek bir ekleme işlemine iner: olayı kuyruğa yaz, onayı dön. Olayın asıl işlenmesi
— gönderi kaydının güncellenmesi, rota adımının yazılması, ücretlendirme satırının hazırlanması —
kuyruğun arkasına geçer ve orada kapasite **seçilebilir** hale gelir. Seçim özgürlüğü kuyruğun
kazancıdır; sınırı da kuyruğun kendisidir, çünkü tüketilmeyen her olay birikir.

Birikmeyi hesaplamak için K01'in tablosunda olmayan bir sayı gerekir. V8 tepenin ne kadar
**yüksek** olduğunu söylüyor, ne kadar **sürdüğünü** söylemiyor.

**K1 — tepe penceresi: 3 saat/gün.** Bu kursun kendi varsayımıdır ve K01'in tablosuna eklenmez.
Gerekçe: durum olaylarını taşıyıcıların dağıtım turu üretir ve tur günün belirli bir dilimine
sıkışır. Günlük toplam K01'den geldiği için tepe dışı hız artık serbest değildir: üç saat 97,22
istek/s ile 1.050.000 olay üretir, kalan 1.750.000 olay yirmi bir saate yayılır ve tepe dışı hız
23,15 istek/s olur. Duyarlılığı aşağıdaki ikinci tabloda ölçülüyor.

#### Birikme

Aşağıdaki düzenek kuyruğu, üreticiyi ve tüketiciyi süreç içi birer modül olarak kurar. Tur bir
saniyedir; bir günü saniye saniye yürütür ve seçilen işleme kapasitesine göre birikmeyi izler.
**Bu bir modeldir**, ölçüm değildir: içindeki bütün sayılar K01'den ve K1'den aritmetikle çıkar,
sonuçlar belirlenimlidir ve makineden bağımsızdır — yani hesap sınıfındadır. Başlangıç birikmesi
dönemsel olsun diye dört gün koşulur ve sayılar üçüncü günden alınır.

```js
// kuyruk/yayma.mjs — gunluk olay profilinin sabit kapasiteli bir tuketiciye verilmesi.
// MODEL: kuyruk ve tuketici surec ici birer modul, tur bir saniyedir. Sonuclar belirlenimlidir
// (hesap sinifi) ve makineden bagimsizdir; olculmus sure yoktur.
const GUN = 86_400;
const GUNLUK_OLAY = 2_800_000;     // K01 Kabaca Buyukluk Hesabi: gunluk durum olayi
const TEPE_CARPANI = 3;            // K01 varsayim V8
const TEPE_BAS = 9 * 3600;         // tepe penceresinin gun icindeki yeri
const ESIK = 300;                  // "geciken olay" esigi (s)

function profil(tepeSaat) {
  const ortalama = GUNLUK_OLAY / GUN;
  const tepe = ortalama * TEPE_CARPANI;
  const tepeSn = tepeSaat * 3600;
  const disi = (GUNLUK_OLAY - tepe * tepeSn) / (GUN - tepeSn);
  return { ortalama, tepe, disi,
    varis: (t) => (t >= TEPE_BAS && t < TEPE_BAS + tepeSn ? tepe : disi) };
}

function kosum(kapasite, p) {       // dort gun kosar; ilk iki gun baslangic birikmesini
  let birikme = 0;                  // donemsel hale getirir, sayilar ucuncu gunden alinir
  let enYuksek = 0, toplam = 0, geciken = 0, bosalma = -1;
  const OLC = 2 * GUN;              // olcum gununun baslangici
  for (let t = 0; t < 4 * GUN; t += 1) {
    const gelen = p.varis(t % GUN);
    if (t >= OLC && t < OLC + GUN) {
      if (birikme / kapasite > ESIK) geciken += gelen;
      toplam += gelen;
    }
    birikme += gelen;
    birikme -= Math.min(birikme, kapasite);
    if (t >= OLC && t < OLC + GUN && birikme > enYuksek) enYuksek = birikme;
    if (bosalma < 0 && t > OLC + TEPE_BAS && birikme < 1) bosalma = t - OLC - TEPE_BAS;
  }
  return { enYuksek, bosalma, geciken, oran: geciken / toplam,
    bekleme: enYuksek / kapasite };
}

function tablo(tepeSaat) {
  const p = profil(tepeSaat);
  console.log(`K1 = ${tepeSaat} saat tepe penceresi -> tepe ${p.tepe.toFixed(2)}/s, ` +
    `tepe disi ${p.disi.toFixed(2)}/s, ortalama ${p.ortalama.toFixed(2)}/s`);
  console.log("kapasite  tepeye oran  en yuksek birikme  en uzun bekleme  bosalma  geciken olay  geciken oran");
  for (const oran of [1, 2 / 3, 0.5, 0.4, 0.35, 1 / 3]) {
    const k = p.tepe * oran;
    const r = kosum(k, p);
    const b = r.bekleme < 3600 ? `${(r.bekleme / 60).toFixed(1)} dk` : `${(r.bekleme / 3600).toFixed(2)} sa`;
    const s = r.bosalma < 0 ? "yok" : `${(r.bosalma / 3600).toFixed(2)} sa`;
    console.log(`${k.toFixed(2).padStart(8)}  ${oran.toFixed(2).padStart(11)}  ` +
      `${r.enYuksek.toFixed(0).padStart(17)}  ${b.padStart(15)}  ${s.padStart(7)}  ` +
      `${r.geciken.toFixed(0).padStart(12)}  ${r.oran.toFixed(3).padStart(11)}`);
  }
}

console.log(`gunde ${GUNLUK_OLAY} durum olayi (K01), tepe carpani ${TEPE_CARPANI} (V8)`);
console.log(`"geciken olay" = ${ESIK} saniyeden uzun bekleyen olay\n`);
tablo(3);
console.log();
tablo(6);
```

```
gunde 2800000 durum olayi (K01), tepe carpani 3 (V8)
"geciken olay" = 300 saniyeden uzun bekleyen olay

K1 = 3 saat tepe penceresi -> tepe 97.22/s, tepe disi 23.15/s, ortalama 32.41/s
kapasite  tepeye oran  en yuksek birikme  en uzun bekleme  bosalma  geciken olay  geciken oran
   97.22         1.00                  0           0.0 dk  0.00 sa             0        0.000
   64.81         0.67             350000          1.50 sa  5.33 sa       1175324        0.420
   48.61         0.50             525000          3.00 sa  8.73 sa       1484861        0.530
   38.89         0.40             630000          4.50 sa  14.12 sa       1939884        0.693
   34.03         0.35             682500          5.57 sa  20.43 sa       2464667        0.880
   32.41         0.33             700000          6.00 sa  24.00 sa       2761134        0.986

K1 = 6 saat tepe penceresi -> tepe 97.22/s, tepe disi 10.80/s, ortalama 32.41/s
kapasite  tepeye oran  en yuksek birikme  en uzun bekleme  bosalma  geciken olay  geciken oran
   97.22         1.00                  0           0.0 dk  0.00 sa             0        0.000
   64.81         0.67             700000          3.00 sa  9.60 sa       2177789        0.778
   48.61         0.50            1050000          6.00 sa  13.71 sa       2366670        0.845
   38.89         0.40            1260000          9.00 sa  18.46 sa       2560693        0.915
   34.03         0.35            1365000         11.14 sa  22.33 sa       2714390        0.969
   32.41         0.33            1400000         12.00 sa  24.00 sa       2780556        0.993
```

#### Ne Kazanıldı, Ne Ödendi

Birinci tablonun ilk satırı eşzamanlı tasarımın kendisidir: kapasite tepeye eşitse birikme sıfır,
bekleme sıfır. Aşağı inen her satır kapasiteden vazgeçip bekleme satın alıyor.

**Kazanç doğrusal.** Kapasite tepenin yarısına indiğinde işleme tarafı yarı yarıya küçülür;
üçte birine indiğinde üçte birine iner. Alt sınır tesadüf değildir: tepenin üçte biri gün
ortalamasıdır ve V8'in tanımı gereği tepe ortalamanın üç katıdır. **Kuyruğun kapasite kazancının
üst sınırı tepe çarpanının kendisidir** — tepeye göre değil ortalamaya göre boyutlanmak, V8 kadar
kazanç demektir ve daha fazlası yoktur.

**Bedel doğrusal değil.** Kapasite 1,00'den 0,67'ye inerken en yüksek birikme 0'dan 350.000'e
çıkıyor; 0,50'ye inerken 525.000'e, 0,40'a inerken 630.000'e. Kapasitenin son yüzde on yediyi
vermesi birikmeye 105.000 eklerken, ilk yüzde otuz üç 350.000 ekledi — ama beklemeye bakıldığında
sıra tersine döner: en uzun bekleme 1,50 saatten 3,00, sonra 4,50 saate çıkıyor. Bekleme,
birikmenin kapasiteye bölümü olduğu için pay büyürken payda küçülüyor ve iki etki çarpışıyor.
Ortalamaya en yakın satırda bekleme altı saattir.

**Boşalma sütunu bir sınır çiziyor.** Kapasite 0,35 katında birikme tepeden 20,43 saat sonra
sıfırlanıyor; 0,33 katında tam 24,00 saat sürüyor, yani ertesi günün tepesi başladığı anda
sıfırlanıyor. Bu bir bıçak sırtıdır: ortalamaya eşit kapasite seçen bir tasarımda tek bir yoğun
gün, ertesi güne devreden bir birikme bırakır ve birikme günden güne büyür. **Ortalamaya göre
boyutlanmak kuramsal sınırdır, seçilebilir bir tasarım değildir.**

**Geciken olay sütunu bedelin kimi etkilediğini söylüyor.** Beş dakikadan uzun bekleyen olay
sayısı 0,67 katında 1.175.324, yani günün olaylarının yüzde 42'si. Yarım kapasitede yüzde 53'e,
0,40 katında yüzde 69'a çıkıyor. Bu sayı doğrudan takip sorgusunun gördüğü tazeliktir: taşıyıcı
"dağıtıma çıktı" olayını gönderdiğinde alıcının takip sayfası onu ancak birikme boşaldığında
gösterir.

**İkinci tablo K1'in duyarlılığıdır.** Tepe penceresi üç saatten altı saate çıktığında tepe hızı
değişmiyor — o sayı V8'den geliyor — ama tepe dışı hız 23,15'ten 10,80 istek/s'ye iniyor ve
birikmeler tam iki katına çıkıyor: 0,50 katında 525.000 yerine 1.050.000, bekleme üç saat yerine
altı saat. Varsayımın tasarıma etkisi doğrudandır ve tek yönlüdür: **tepenin süresi, tepenin
yüksekliği kadar belirleyicidir** ve K01'in tablosu bu bilgiyi taşımıyordu.

#### Kesinti Karşısında İki Tasarım

Kapasite kararı tabloya bakarak kuyruk aleyhine sonuçlanabilir. Eksik olan bir ölçü var: kuyruk
yalnız yükü zamana yaymaz, üreticiyi tüketicinin arızasından da ayırır. İkinci düzenek deponun
tepe penceresinin ortasında on beş dakika yazma kabul etmediği durumu ekler. Kesinti bir
parametredir.

```js
// kuyruk/kesinti.mjs — deponun 15 dakika yazma kabul etmedigi durumda iki tasarimin
// karsilastirilmasi. MODEL: tur bir saniyedir, kesinti parametreyle tetiklenir.
// Sonuclar belirlenimlidir (hesap sinifi).
const GUN = 86_400;
const GUNLUK_OLAY = 2_800_000;     // K01: gunluk durum olayi
const TEPE = (GUNLUK_OLAY / GUN) * 3;               // K01 hesabi + V8: tepe yazma /s
const TEPE_BAS = 9 * 3600, TEPE_SN = 3 * 3600;      // K1: 3 saatlik tepe penceresi
const DISI = (GUNLUK_OLAY - TEPE * TEPE_SN) / (GUN - TEPE_SN);
const KESINTI_BAS = 10 * 3600, KESINTI_SN = 900;    // kesinti tepe penceresinin ortasinda

const varis = (t) => (t >= TEPE_BAS && t < TEPE_BAS + TEPE_SN ? TEPE : DISI);
const kesik = (t) => t >= KESINTI_BAS && t < KESINTI_BAS + KESINTI_SN;

function kosum(kapasite, kuyrukVar, kesintiVar) {
  let birikme = 0, kayip = 0, enYuksek = 0, bosalma = -1;
  for (let t = 0; t < 2 * GUN; t += 1) {
    const gun = t % GUN;
    const gelen = varis(gun);
    const kap = kesintiVar && kesik(gun) ? 0 : kapasite;
    if (kuyrukVar) birikme += gelen;
    else if (gelen > kap) { kayip += (gelen - kap) * (t >= GUN ? 1 : 0); }
    birikme -= Math.min(birikme, kap);
    if (t >= GUN) {
      if (birikme > enYuksek) enYuksek = birikme;
      if (bosalma < 0 && gun > TEPE_BAS && birikme < 1) bosalma = gun - TEPE_BAS;
    }
  }
  return { kayip, enYuksek, bosalma, bekleme: enYuksek / kapasite };
}

const bicim = (r, kapasite) => `${kapasite.toFixed(2).padStart(8)}  ` +
  `${r.kayip.toFixed(0).padStart(15)}  ${r.enYuksek.toFixed(0).padStart(17)}  ` +
  `${(r.bekleme / 3600).toFixed(2).padStart(13)}  ` +
  `${(r.bosalma < 0 ? NaN : r.bosalma / 3600).toFixed(2).padStart(13)}`;

console.log(`kesinti: ${KESINTI_SN} s, tepe penceresinin icinde; o sirada gelen olay ` +
  `${(TEPE * KESINTI_SN).toFixed(0)}\n`);
console.log("tasarim                 kapasite  kaybedilen olay  en yuksek birikme  bekleme (sa)  bosalma (sa)");
console.log(`${"eszamanli yazma".padEnd(22)} ${bicim(kosum(TEPE, false, false), TEPE)}`);
console.log(`${"eszamanli + kesinti".padEnd(22)} ${bicim(kosum(TEPE, false, true), TEPE)}`);
for (const oran of [0.5, 0.4]) {
  const k = TEPE * oran;
  console.log(`${`kuyruk ${oran.toFixed(2)}x tepe`.padEnd(22)} ${bicim(kosum(k, true, false), k)}`);
  console.log(`${`kuyruk ${oran.toFixed(2)}x + kesinti`.padEnd(22)} ${bicim(kosum(k, true, true), k)}`);
}

const ONBELLEK_ARKASI = 41.67;     // K01: onbellek arkasi okuma/s
const DEPO_TABAN = 138.89;         // K01: depoya ulasan istek/s
console.log(`\nK01 donusu: depoya ulasan istek/s, taban ${DEPO_TABAN} (okuma ${ONBELLEK_ARKASI} + yazma ${TEPE.toFixed(2)})`);
for (const oran of [1, 0.5, 0.4]) {
  const k = TEPE * oran, depo = ONBELLEK_ARKASI + k;
  console.log(`  isleme kapasitesi ${k.toFixed(2).padStart(6)}/s -> depoya ulasan ${depo.toFixed(2).padStart(6)}/s, ` +
    `tabanin ${(depo / DEPO_TABAN).toFixed(2)} kati, depoda yazma/okuma ${(k / ONBELLEK_ARKASI).toFixed(2)}`);
}
```

```
kesinti: 900 s, tepe penceresinin icinde; o sirada gelen olay 87500

tasarim                 kapasite  kaybedilen olay  en yuksek birikme  bekleme (sa)  bosalma (sa)
eszamanli yazma           97.22                0                  0           0.00           0.00
eszamanli + kesinti       97.22            87500                  0           0.00           0.00
kuyruk 0.50x tepe         48.61                0             525000           3.00           8.73
kuyruk 0.50x + kesinti    48.61                0             568750           3.25           9.20
kuyruk 0.40x tepe         38.89                0             630000           4.50          14.12
kuyruk 0.40x + kesinti    38.89                0             665000           4.75          14.73

K01 donusu: depoya ulasan istek/s, taban 138.89 (okuma 41.67 + yazma 97.22)
  isleme kapasitesi  97.22/s -> depoya ulasan 138.89/s, tabanin 1.00 kati, depoda yazma/okuma 2.33
  isleme kapasitesi  48.61/s -> depoya ulasan  90.28/s, tabanin 0.65 kati, depoda yazma/okuma 1.17
  isleme kapasitesi  38.89/s -> depoya ulasan  80.56/s, tabanin 0.58 kati, depoda yazma/okuma 0.93
```

On beş dakikalık kesinti eşzamanlı tasarımda **87.500 olay** düşürüyor. Sayı doğrudan aritmetiktir:
900 saniye × 97,22 istek/s. Taşıyıcı yeniden denese bile o denemeler ayrı bir tasarım kararıdır
ve Dayanıklılık ve Güvenilirlik kursunda ele alındı; burada ölçülen, tasarımın kendi başına
kaybettiğidir.

Aynı kesinti kuyruklu tasarımda **sıfır olay** düşürüyor. Ödenen şey birikmeye eklenen 43.750
olay ve beklemeye eklenen on beş dakikadır: yarım kapasitede en yüksek birikme 525.000'den
568.750'ye, bekleme 3,00 saatten 3,25 saate, boşalma 8,73 saatten 9,20 saate çıkıyor. Bu bir
kıyaslama değil bir **dönüştürme**dir: kuyruk kaybı gecikmeye çevirir. Kaybın kabul edilemez
olduğu ve gecikmenin taşınabildiği akışlarda kazanç, tersi durumda zarar.

#### Hesaba Geri Dönüş

Son üç satır K01'in bir hesabını yeniden yazıyor. Kabaca Büyüklük Hesabı dersi depoya ulaşan tepe
istek hızını 138,89 istek/s bulmuştu: önbelleğin arkasındaki 41,67 okuma artı 97,22 yazma. O
toplamdaki yazma payı eşzamanlı tasarımın varsayımıydı — her olay geldiği anda depoya yazılıyor.

Kuyruk yazma payını seçilebilir yapıyor. İşleme kapasitesi tepenin yarısında seçildiğinde depoya
ulaşan istek hızı 90,28 istek/s'ye iniyor, tabanın 0,65 katı; 0,40 katında 80,56 istek/s, tabanın
0,58 katı. Depodaki yazma/okuma oranı da değişiyor: K01'in 2,33 değeri sırasıyla 1,17 ve 0,93
oluyor. K01, deponun gördüğü yerde sistemin yazma ağırlıklı olduğunu bulmuştu; kuyruk bu dengeyi
tersine çevirebilecek bir karardır ve deponun boyutlandırılması bu yeni sayıdan başlar.

Karar tek satıra iner. Kuyruk üç koşul birlikte sağlandığında kapasite kazandırır: tepe ile
ortalama arasında gerçek bir fark vardır (V8 birden büyüktür), çağıranın sonucu beklemesi
gerekmez ve işin etkisinin gecikmesi taşınabilir. Üçüncüsü en sık atlanandır; tablodaki "geciken
olay" sütunu onun bedelidir.

#### Özet

- Eşzamanlı yazma yolunda kapasite anlık tepeye göre seçilir; K01'in tepe yazma hızı 97,22
  istek/s bu tasarımın gereksinimidir.
- Kuyruk kapasite kararını ikiye böler: kabul yolu tepeyi almak zorundadır, işleme kapasitesi
  seçilebilir; kazancın üst sınırı tepe çarpanı V8'dir, yani üç kat.
- Kazanç doğrusal, bedel değil: kapasite tepenin 0,67 / 0,50 / 0,40 katına indiğinde en yüksek
  birikme 350.000 / 525.000 / 630.000, en uzun bekleme 1,50 / 3,00 / 4,50 saat oluyor.
- Ortalamaya eşit kapasite bir bıçak sırtıdır: birikme tam 24,00 saatte boşalıyor ve olayların
  yüzde 98,6'sı beş dakikadan uzun bekliyor.
- K1 varsayımı duyarlıdır: tepe penceresi üç saatten altı saate çıktığında bütün birikmeler ve
  beklemeler iki katına çıkıyor, tepe hızı ise değişmiyor.
- Kuyruk kaybı gecikmeye çevirir: on beş dakikalık depo kesintisi eşzamanlı tasarımda 87.500
  olay düşürürken kuyruklu tasarımda sıfır olay düşürüyor, karşılığında bekleme 3,00 saatten
  3,25 saate çıkıyor.

#### Sonraki Adım

Bu dersin bütün olayları birbirinin eşiydi: her durum olayı aynı büyüklükte, aynı işi yapan,
milisaniyeler süren bir kayıttı. Birikme hesabı da buna dayanıyordu — kapasite bir sayıdır çünkü
işler tek tiptir. Aynı sistemde bu varsayımın tutmadığı bir iş var: satıcının istediği dönem
raporu, binlerce gönderi kaydını tarar ve saniyelerce sürer. Böyle bir iş isteğin içinde
çalıştırıldığında yalnız kendi süresini harcamaz, arkasındaki kısa istekleri de bekletir. Sonraki
ders bu iki işi ayırır: uzun işin istek yolunda kalmasının kısa isteklere ne yaptığını ölçer ve
görev kuyruğuna alındığında hangi ölçünün düzeldiğini sayar.

### Görev Kuyrukları ve Arka Plan İşleri

Source: https://academia.sh/tr/kurslar/uygulama-katmani/gorev-kuyruklari-ve-arka-plan-isleri

Uzun bir işin istek yolundan çıkarılmasının ölçülmesi: rapor işinin süresinin giriş kursunun sayılarından türetilmesi, aynı yuva havuzunu paylaşan kısa ve uzun işlerde kısa isteğin ne kadar kuyruklandığının sayılması, ayrı bir görev kuyruğuna alındığında hangi ölçünün düzeldiği ve ölçekleme biriminin kopyadan işçiye geçmesi.

Önceki dersin bütün olayları birbirinin eşiydi: her durum olayı aynı büyüklükte, aynı işi yapan,
milisaniyeler süren bir kayıttı. Birikme hesabı da buna dayanıyordu — kapasite tek bir sayıyla
ifade edilebilir, çünkü işler tek tiptir. Aynı sistemde bu varsayımın tutmadığı bir iş var:
satıcının istediği dönem raporu binlerce gönderi kaydını tarar ve saniyelerce sürer. Böyle bir iş
isteğin içinde çalıştırıldığında yalnız kendi süresini harcamaz, arkasındaki kısa istekleri de
bekletir.

Bu ders o işi istek yolundan çıkarır. **Görev kuyruğu** (task queue) mesaj kuyruğunun aynı
mekaniğidir; ayrıldığı yer içeriğidir — kuyruğa yazılan şey bir olay bildirimi değil, birinin
yapılmasını istediği bir **iştir**. İşçi süreçleri, çekme tabanlı dağıtım, gözetim, zarif kapanma
ve uzun işin parçalara bölünüp ilerlemesinin yazılması Önbellekleme, Kuyruklar ve Eşzamansız
İşleme kursunda kuruldu; hiçbiri yeniden anlatılmaz. Burada ölçülen tek şey ayırmanın **kapasite
karşılığıdır**: kısa isteğin beklemesi ne kadar düzelir ve tasarımın ölçekleme birimi ne olur.

#### İşin Boyu Nereden Geliyor

Uzun işin süresi uydurulmaz, K01'in sayılarından çıkar. Kabaca Büyüklük Hesabı dersi günde 4000
fatura satırı buluyordu ve her satır bir satıcının bir günüydü; satıcı sayısı 4000'dir. Günde
400.000 gönderi oluştuğuna göre satıcı başına günlük gönderi 100'dür. Ücretlendirme dönemi V11
gereği 30 gün olduğuna göre bir dönem raporu **3000 gönderi kaydı** tarar. Aynı ders tarama
hızını da veriyor: 833,33 kayıt/s, yani kayıt başına 1,20 ms. Rapor işi böylece **3,60 saniye**
sürer. Bu sayıların hepsi hesap sınıfındadır.

Karşılaştırılan kısa iş takip sorgusudur ve tek bir kayıt okur, yani bir tarama biriminde biter.
Aradaki oran doğrudan 3000'dir. Modelin tur birimi de buradan seçilmiştir: bir tur 1,20 ms'dir ve
model içindeki hiçbir süre ölçülmez, turdan çevrilir.

İki varsayım gerekir ve ikisi de bu kursun kendi varsayımıdır; K01'in tablosuna eklenmez.

**K2 — sabah penceresi: 30 dakika.** Gerekçe: K01'in V10 satırı "satıcı raporu sabah ister"
diyor; rapor talepleri iş gününün açılışına sıkışır.

**K3 — pencerede rapor isteyen satıcı oranı: 0,50.** Gerekçe: dönem raporu ayın belirli
günlerinde toplu olarak istenir ve o günlerde satıcıların yarısının uğradığı varsayılır.
Duyarlılığı aşağıdaki tabloda 0,25 satırlarıyla ölçülüyor.

Okuma yükü K01 ile K02'den gelir: tepe okuma 416,67 istek/s, kopya sayısı 4 — Trafik Katmanı
kursunun oturum yapışkanlığı dersi bu sayıyı 3'ten 4'e çıkarmıştı. Kopya başına düşen okuma
104,17 istek/s'dir.

#### Aynı Havuzda İki İş

Düzenek tek bir kopyayı süreç içi bir modelle kurar. Kopyanın belirli sayıda **yuvası** vardır;
her yuva bir anda tek bir isteği tutar, boşalınca kuyruğun başındakini alır. Varışlar sabit
tohumlu iki bağımsız üreteçten gelir, dolayısıyla bütün tasarımlar **aynı** istek dizisiyle
karşılaşır. Model olduğu için sonuçlar belirlenimlidir ve makineden bağımsızdır.

```js
// gorev/havuz.mjs — uzun isin istek yolunda kalmasi ile gorev kuyruguna alinmasinin
// karsilastirilmasi. MODEL: tur = 1,20 ms (K01: toplu is tarama 833,33 kayit/s), varislar
// sabit tohumlu bir uretecten gelir. Sonuclar belirlenimlidir; sure olculmez, turden cevrilir.
const TUR_MS = 1.2;                // K01: kayit basina 1,20 ms
const OKUMA_TEPE = 416.67;         // K01: tepe okuma istek/s
const KOPYA = 4;                   // K02 oturum yapiskanligi dersi: 4 kopya
const SATICI = 4000;               // K01: gunluk fatura satiri = satici sayisi
const RAPOR_KAYIT = 3000;          // K01: satici basina 100 gonderi/gun x V11 = 30 gun
const K2_PENCERE_DK = 30;          // bu kursun varsayimi K2: sabah penceresi
const K3_ORAN = 0.5;               // bu kursun varsayimi K3: pencerede rapor isteyen satici orani
const TOHUM = 20260730;

const TUR = Math.round((K2_PENCERE_DK * 60 * 1000) / TUR_MS);
const takipP = (OKUMA_TEPE / KOPYA) * (TUR_MS / 1000);
const raporSayisi = (oran) => (SATICI * oran) / KOPYA;

function uretec(tohum) {           // splitmix32: iki akis icin bagimsiz varis dizisi
  let a = tohum >>> 0;
  return () => {
    a = (a + 0x9e3779b9) >>> 0;
    let z = Math.imul(a ^ (a >>> 16), 0x21f0aaad) >>> 0;
    z = Math.imul(z ^ (z >>> 15), 0x735a2d97) >>> 0;
    return ((z ^ (z >>> 15)) >>> 0) / 4294967296;
  };
}

function kosum({ yuva, ayri, isci, oran = K3_ORAN }) {
  const raporP = raporSayisi(oran) / TUR;
  const rastTakip = uretec(TOHUM), rastRapor = uretec(TOHUM + 1);
  const meshul = new Array(yuva).fill(0), raporMeshul = new Array(isci).fill(0);
  const kuyruk = [], gorev = [], bekleme = [], raporSure = [];
  let gelenRapor = 0;
  for (let t = 0; t < TUR; t += 1) {
    if (rastTakip() < takipP) kuyruk.push({ t, sure: 1 });
    if (rastRapor() < raporP) { gelenRapor += 1; (ayri ? gorev : kuyruk).push({ t, sure: RAPOR_KAYIT, rapor: true }); }
    for (let i = 0; i < yuva; i += 1) {
      if (meshul[i] > 0) meshul[i] -= 1;
      if (meshul[i] === 0 && kuyruk.length > 0) {
        const is = kuyruk.shift();
        if (is.rapor) raporSure.push(t - is.t + is.sure); else bekleme.push(t - is.t);
        meshul[i] = is.sure;
      }
    }
    for (let i = 0; i < isci; i += 1) {
      if (raporMeshul[i] > 0) raporMeshul[i] -= 1;
      if (raporMeshul[i] === 0 && gorev.length > 0) {
        const is = gorev.shift();
        raporSure.push(t - is.t + is.sure);
        raporMeshul[i] = is.sure;
      }
    }
  }
  const d = [...bekleme].sort((a, b) => a - b);
  const y = (p) => d[Math.min(d.length - 1, Math.floor((p / 100) * d.length))];
  return { takip: bekleme.length, p50: y(50), p99: y(99), enUzun: d[d.length - 1],
    geciken: bekleme.filter((b) => b * TUR_MS > 100).length,
    gelenRapor, biten: raporSure.length,
    raporOrt: raporSure.reduce((a, b) => a + b, 0) / raporSure.length };
}

console.log(`tur = ${TUR_MS} ms, pencere ${K2_PENCERE_DK} dk = ${TUR} tur, ${KOPYA} kopya`);
console.log(`kopya basina takip ${(OKUMA_TEPE / KOPYA).toFixed(2)} istek/s, ` +
  `K3 = ${K3_ORAN} -> kopya basina beklenen rapor ${raporSayisi(K3_ORAN)}`);
console.log(`rapor isi = ${RAPOR_KAYIT} kayit = ${(RAPOR_KAYIT * TUR_MS / 1000).toFixed(2)} s\n`);

const satir = (ad, r) => console.log(`${ad.padEnd(24)} ${String(r.takip).padStart(7)} ` +
  `${(r.p50 * TUR_MS).toFixed(2).padStart(9)} ${(r.p99 * TUR_MS).toFixed(2).padStart(9)} ` +
  `${(r.enUzun * TUR_MS).toFixed(0).padStart(11)} ${String(r.geciken).padStart(9)} ` +
  `${(r.geciken / r.takip).toFixed(4).padStart(6)} ${`${r.biten}/${r.gelenRapor}`.padStart(9)} ` +
  `${(r.raporOrt * TUR_MS / 1000).toFixed(2).padStart(9)}`);

console.log("tasarim                    takip  p50 (ms)  p99 (ms)  en uzun(ms)  >100 ms     oran  biten/gelen  rapor (s)");
for (const yuva of [2, 4, 8]) satir(`paylasilan y=${yuva}`, kosum({ yuva, ayri: false, isci: 0 }));
for (const yuva of [2, 4]) satir(`paylasilan y=${yuva}, K3=0.25`, kosum({ yuva, ayri: false, isci: 0, oran: 0.25 }));
for (const yuva of [2, 4]) satir(`ayrilmis y=${yuva}, isci=2`, kosum({ yuva, ayri: true, isci: 2 }));

const enAz = (uret) => { for (let y = 1; y <= 12; y += 1) if (uret(y).geciken === 0) return y; return 0; };
const payYuva = enAz((yuva) => kosum({ yuva, ayri: false, isci: 0 }));
const ayriYuva = enAz((yuva) => kosum({ yuva, ayri: true, isci: 2 }));
const raporSaniye = raporSayisi(K3_ORAN) * KOPYA * (RAPOR_KAYIT * TUR_MS / 1000);
const isci = raporSaniye / (K2_PENCERE_DK * 60);
console.log(`\n100 ms ustu takip beklemesini sifirlayan en kucuk yuva: paylasilan ${payYuva}, ayrilmis ${ayriYuva}`);
console.log(`olcekleme birimi -> paylasilan ${payYuva * KOPYA} yuva (hepsi okuma yolunda), ` +
  `ayrilmis ${ayriYuva * KOPYA} yuva + gorev isciligi`);
console.log(`sistem geneli rapor isi = ${raporSaniye.toFixed(0)} isci-saniye / ` +
  `${K2_PENCERE_DK * 60} s = ${isci.toFixed(2)} isci (doluluk 1,00)`);
```

```
tur = 1.2 ms, pencere 30 dk = 1500000 tur, 4 kopya
kopya basina takip 104.17 istek/s, K3 = 0.5 -> kopya basina beklenen rapor 500
rapor isi = 3000 kayit = 3.60 s

tasarim                    takip  p50 (ms)  p99 (ms)  en uzun(ms)  >100 ms     oran  biten/gelen  rapor (s)
paylasilan y=2            187459      0.00   5887.20        9398     54392 0.2902   457/457      4.24
paylasilan y=4            187459      0.00    634.80        2808      3088 0.0165   457/457      3.61
paylasilan y=8            187459      0.00      0.00           0         0 0.0000   457/457      3.60
paylasilan y=2, K3=0.25   187459      0.00   2766.00        4984     15204 0.0811   223/223      3.77
paylasilan y=4, K3=0.25   187459      0.00      0.00        1240       293 0.0016   223/223      3.60
ayrilmis y=2, isci=2      187459      0.00      0.00           0         0 0.0000   457/457      4.14
ayrilmis y=4, isci=2      187459      0.00      0.00           0         0 0.0000   457/457      4.14

100 ms ustu takip beklemesini sifirlayan en kucuk yuva: paylasilan 7, ayrilmis 1
olcekleme birimi -> paylasilan 28 yuva (hepsi okuma yolunda), ayrilmis 4 yuva + gorev isciligi
sistem geneli rapor isi = 7200 isci-saniye / 1800 s = 4.00 isci (doluluk 1,00)
```

Otuz dakikada 187.459 takip isteği ve beklenen 500 rapora karşılık tohumun ürettiği 457 rapor
geldi; sayı Bernoulli varışının bir gerçekleşmesidir ve bütün satırlarda aynıdır.

#### Doluluk Düşük, Kuyruk Uzun

Paylaşılan havuzun sayıları ilk bakışta tutarsız görünür. İki yuvalı bir kopyada rapor işi
yuvaların yarısından azını kullanıyor: 457 rapor × 3,60 saniye = 1645 yuva-saniyesi, otuz
dakikalık pencerede iki yuvanın verdiği 3600 yuva-saniyesinin yüzde 46'sı. Takip akışı ise yüzde
12,5 istiyor. Toplam doluluk yüzde 60'ın altında, yani havuz kapasite olarak yeterlidir.

Buna rağmen takip isteklerinin **yüzde 29,02'si** 100 milisaniyeden uzun bekliyor, p99 beklemesi
5887 ms ve en uzun bekleme 9398 ms. Neden yeterli kapasitenin bu tabloyu üretebildiği ortadadır:
kuyruk sırayla boşalır ve önündeki iş 3000 tur sürüyorsa arkasındaki bir turluk iş de 3000 tur
bekler. İki rapor aynı anda çalıştığında iki yuvanın ikisi de kapanır ve o süre boyunca gelen
her takip isteği sıraya girer. **Ortalama doluluk düşükken kuyruğun uzayabilmesi, işlerin
süresinin dağılımından gelir; tek bir doluluk sayısı bu dağılımı saklar.**

Yuva sayısı bunu düzeltir ama pahalıya düzeltir. Dört yuvada oran yüzde 1,65'e, p99 634,80 ms'ye
iner; sekiz yuvada sıfırlanır. Aradaki eşik ölçülmüştür: 100 milisaniye üstü beklemeyi sıfırlayan
en küçük yuva sayısı **yedidir**. Kısa isteğin tek başına ihtiyacı bir yuvadır — ayrılmış
tasarımda ölçülen sayı budur. Yani altı yuva yalnız uzun işin arkasında sıra beklenmesin diye
tutuluyor.

K3'ün duyarlılığı aynı yönü gösteriyor. Rapor isteyen satıcı oranı yarıya indiğinde iki yuvada
etkilenen istek oranı yüzde 29,02'den yüzde 8,11'e, dört yuvada yüzde 1,65'ten yüzde 0,16'ya
düşüyor — ama **sıfırlanmıyor**. Talebin azalması sorunun sıklığını azaltır, yapısını değiştirmez;
tek bir uzun iş bile arkasındaki isteği kendi süresi kadar bekletir.

#### Ölçekleme Birimi

Ayrılmış tasarımda rapor işi kuyruğa yazılır ve çağıran bir iş kimliğiyle döner. Takip
isteklerinin ölçüleri her yuva sayısında aynı çıkıyor: p99 sıfır, en uzun bekleme sıfır,
100 milisaniye üstü bekleyen istek yok. Uzun iş yoldan çekildiğinde kısa isteğin kuyruğu da
kayboluyor.

Ödenen bedel rapor tarafındadır ve ölçülmüştür: iki işçiyle ortalama rapor süresi 3,60 saniye
yerine **4,14 saniye**. Rapor artık kendi kuyruğunda sıra bekliyor. Fark yüzde 15'tir ve
istenirse işçi eklenerek kapatılır — çünkü işçi sayısı artık takip akışından bağımsız bir
sayıdır.

Asıl sonuç son üç satırdadır. Paylaşılan tasarımda takip kuyruğunu sıfırlamak için kopya başına
yedi yuva gerekiyor; dört kopyada **28 yuva**. Bu yuvaların hepsi okuma yolundadır: her biri
takip isteği alabilecek biçimde yapılandırılmış, okuma trafiğinin dağıtıldığı kopyalarda durur.
Ayrılmış tasarımda takip için kopya başına bir yuva yetiyor — dört kopyada **4 yuva** — ve rapor
işi ayrı bir işçiliğe geçiyor. O işçiliğin büyüklüğü de aritmetiktir: sistem genelinde 2000 rapor
× 3,60 saniye = 7200 işçi-saniyesi, otuz dakikalık pencerede **4,00 işçi** eder ve bu sayı
doluluk 1,00 demektir, yani gerçek bir tasarım pay bırakır.

**Ayırmanın kazancı bir sayı değil, bir bağımsızlıktır.** Paylaşılan tasarımda rapor talebi
arttığında büyütülecek şey kopyadır; kopya büyütmek okuma yolunu da büyütür, oysa okuma yolunun
kapasiteye ihtiyacı yoktu. Ayrılmış tasarımda rapor talebi işçi sayısını, okuma talebi kopya
sayısını oynatır ve iki sayı birbirine bakmaz. Giriş kursunun tepe okuma hızı 416,67 istek/s
değişmedi; değişen, o hızı karşılayan kopyaların ne kadar büyük olmak zorunda olduğudur — kopya
başına yedi yuvadan bire.

#### Özet

- Uzun işin boyu K01'den türetilir: satıcı başına 100 gönderi/gün × 30 gün = 3000 kayıt, tarama
  hızı 833,33 kayıt/s ile 3,60 saniye; takip isteği aynı ölçüde tek kayıttır.
- Paylaşılan havuzda doluluk yüzde 60'ın altındayken bile takip isteklerinin yüzde 29,02'si 100
  ms'den uzun bekliyor, p99 5887 ms: tek bir doluluk sayısı iş sürelerinin dağılımını saklar.
- Kuyruğu sıfırlamanın bedeli yuvadır: 100 ms üstü beklemeyi sıfırlayan en küçük yuva sayısı
  paylaşılan tasarımda yedi, ayrılmış tasarımda birdir.
- K3 varsayımı yarıya indiğinde etkilenen istek oranı yüzde 29,02'den 8,11'e düşüyor ama
  sıfırlanmıyor; talep sorunun sıklığını değiştirir, yapısını değiştirmez.
- Ayrılmış tasarımda takip beklemesi her yuva sayısında sıfır; ödenen bedel iki işçiyle rapor
  süresinin 3,60 saniyeden 4,14 saniyeye çıkmasıdır.
- Ölçekleme birimi ayrışıyor: paylaşılan tasarım dört kopyada 28 yuva ister, ayrılmış tasarım
  4 yuva artı sistem genelinde 4,00 işçilik ister ve iki sayı birbirinden bağımsız oynar.

#### Sonraki Adım

Son satırdaki "4,00 işçi" bir kapasite değil bir alt sınırdır; doluluk bire eşit olduğunda kuyruk
boşalmaz. Sayının kendisi de bir soruyu açık bırakıyor: işçi eklemek gerçekten toplama mı
karşılık gelir. Önceki dersin birikme tablosunda tüketici kapasitesi tek bir sayıydı ve o sayının
nereden geldiği sorulmamıştı; oysa kapasite tüketici sayısıyla tüketici başına hızın çarpımıdır
ve çarpanlardan biri sonsuza kadar büyümez. Sonraki ders aynı kuyruktan çeken birden çok
tüketiciyi ele alır: eklenen her tüketicinin boşalma süresine katkısını, katkının nerede
durduğunu ve verimin karşılığında nelerin bozulduğunu ölçer.

### Rekabet Eden Tüketiciler

Source: https://academia.sh/tr/kurslar/uygulama-katmani/rekabet-eden-tuketiciler

Aynı kuyruktan çeken tüketici sayısının bir kapasite kararı olarak ölçülmesi: eklenen her tüketicinin boşalma süresine katkısının azalması, paylaşılan bir kaynağın koyduğu asimptotik tavanın hesaplanması, verim karşılığında bozulan gönderi sırasının ve yanlış kalan son durumun sayılması ve tavanın tek tüketiciden istenen hıza çevrilmesi.

Önceki dersin son satırındaki "4,00 işçi" bir kapasite değil bir alt sınırdı; doluluk bire eşit
olduğunda kuyruk boşalmaz. Sayının kendisi de bir soruyu açık bırakıyordu: işçi eklemek gerçekten
toplamaya karşılık gelir mi. İlk dersin birikme tablosunda tüketici kapasitesi tek bir sayıydı ve
o sayının nereden geldiği sorulmamıştı; oysa kapasite tüketici sayısıyla tüketici başına hızın
çarpımıdır ve çarpanlardan biri sonsuza kadar büyümez.

Bu ders o çarpımı ölçer. **Rekabet eden tüketiciler** (competing consumers), aynı kuyruktan çeken
ve her iletiyi yalnız birinin aldığı tüketici kümesidir. Önbellekleme, Kuyruklar ve Eşzamansız
İşleme kursunda aynı düzen kuruldu ve her iletinin tek tarafa gittiği
orada ölçüldü; kalıp aynı kalıptır, bu kursta kullanılan biçim katalog biçimidir. Orada kurulan
tüketici grupları ile buradaki fark bir cümleyle söylenebilir: grup, aynı iletinin kaç ayrı
tarafa **kopyalanacağını** belirler; rekabet eden tüketiciler tek bir tarafın işini kaç birime
**bölüneceğini** belirler. Bu dersin ölçüsü de bölmenin karşılığıdır: verim ile sıra arasındaki
ödünleşim.

#### Bölünen İş ve Bölünmeyen Kaynak

Düzenek 5000 gönderinin durum olaylarını bir kuyruğa koyar. Gönderi başına olay sayısı K01'in V4
varsayımından gelir: 7. Toplam 35.000 olay. Aynı gönderinin ardışık olayları kuyruğa yan yana
düşer, çünkü taşıyıcı bir aktarımda birden çok tarama gönderir; bu, sıranın bozulabildiği tek
durumdur ve modelin ilgi alanıdır.

Tüketicilerin bölemeyeceği bir şey vardır: olayın etkisini yazdığı depo. Modelde bu, **depo
yuvası** adıyla bir parametredir — bir turda en fazla S olay uygulanabilir. **S ölçülmüş bir sayı
değildir**, model parametresidir; buradaki işi, tüketici sayısının tek başına bir kapasite
olmadığını görünür kılmaktır. İki değerle koşulur: S = 2 ve S = 4.

Tur soyut bir adımdır. Bir olayın işlenmesi tohumdan gelen 1–3 tur sürer; gönderinin son olayı
("teslim edildi") 6 tur sürer, çünkü fatura satırını da hazırlar. Bütün koşumlar aynı olay
dizisiyle karşılaşır, dolayısıyla sonuçlar belirlenimlidir ve makineden bağımsızdır.

```js
// rakip/tuketici.mjs — ayni kuyruktan ceken N tuketicinin bosalma suresine ve gonderilerin
// son durumuna etkisi. MODEL: tur soyut bir adimdir; depo yuvasi S bir model parametresidir.
// Sonuclar belirlenimlidir (hesap sinifi) ve makineden bagimsizdir.
const GONDERI = 5000;              // is yukundeki gonderi sayisi
const ADIM = 7;                    // K01 varsayim V4: gonderi basina durum olayi
const ARA = 1;                     // ayni gonderinin ardisik olaylari arasindaki kuyruk mesafesi
const TOHUM = 20260731;

function uretec(tohum) {
  let a = tohum >>> 0;
  return () => {
    a = (a + 0x9e3779b9) >>> 0;
    let z = Math.imul(a ^ (a >>> 16), 0x21f0aaad) >>> 0;
    z = Math.imul(z ^ (z >>> 15), 0x735a2d97) >>> 0;
    return ((z ^ (z >>> 15)) >>> 0) / 4294967296;
  };
}

function isYuku() {                // kuyruga dusme sirasina gore olay dizisi
  const r = uretec(TOHUM);
  const o = [];
  for (let g = 0; g < GONDERI; g += 1) {
    const bas = r() * GONDERI * ARA;
    for (let k = 0; k < ADIM; k += 1) {
      o.push({ g, k, t: bas + k * ARA + r() * ARA, sure: k === ADIM - 1 ? 6 : 1 + Math.floor(r() * 3) });
    }
  }
  return o.sort((a, b) => a.t - b.t);
}

function kosum(olaylar, N, S) {
  const tuketici = Array.from({ length: N }, () => null);
  const sonUygulanan = new Array(GONDERI).fill(-1);
  const bozulan = new Set();
  let sira = 0, tur = 0, uygulanan = 0;
  while (uygulanan < olaylar.length) {
    for (const c of tuketici) if (c && c.kalan > 0) c.kalan -= 1;
    let yuva = S;                  // depo yazma yuvasi: her tur en fazla S olay uygulanir
    for (let i = 0; i < N; i += 1) {
      const c = tuketici[i];
      if (c && c.kalan === 0 && yuva > 0) {
        yuva -= 1;
        if (c.is.k < sonUygulanan[c.is.g]) bozulan.add(c.is.g);
        sonUygulanan[c.is.g] = c.is.k;
        uygulanan += 1;
        tuketici[i] = null;
      }
    }
    for (let i = 0; i < N; i += 1) {
      if (tuketici[i] === null && sira < olaylar.length) {
        const is = olaylar[sira]; sira += 1;
        tuketici[i] = { is, kalan: is.sure };
      }
    }
    tur += 1;
  }
  const yanlisSon = sonUygulanan.filter((k) => k !== ADIM - 1).length;
  return { tur, bozulan: bozulan.size, yanlisSon, verim: olaylar.length / tur };
}

const olaylar = isYuku();
console.log(`is yuku: ${GONDERI} gonderi x ${ADIM} olay (V4) = ${olaylar.length} olay`);
console.log(`depo yuvasi S: turda en fazla S olay uygulanir (model parametresi)\n`);
for (const S of [2, 4]) {
  console.log(`S = ${S}`);
  console.log("  N  bosalma turu  hizlanma  ek tuketicinin katkisi  verim(olay/tur)  sirasi bozulan  yanlis son durum");
  let onceki = null;
  const taban = kosum(olaylar, 1, S).tur;
  for (const N of [1, 2, 3, 4, 6, 8]) {
    const r = kosum(olaylar, N, S);
    const h = taban / r.tur;
    const katki = onceki === null ? "-" : ((h - onceki.h) / (N - onceki.N)).toFixed(3);
    console.log(`${String(N).padStart(3)}  ${String(r.tur).padStart(12)}  ${h.toFixed(2).padStart(8)}  ` +
      `${katki.padStart(22)}  ${r.verim.toFixed(3).padStart(15)}  ${String(r.bozulan).padStart(14)}  ` +
      `${String(r.yanlisSon).padStart(17)}`);
    onceki = { h, N };
  }
  console.log();
}

const TEPE = 97.22;                // K01: tepe yazma istek/s
const ISLEME = 48.61;              // 01. dersin sectigi isleme kapasitesi (tepenin yarisi)
const tekVerim = kosum(olaylar, 1, 2).verim;
console.log(`K01 donusu: tepe yazma ${TEPE} olay/s, 01. dersin isleme kapasitesi ${ISLEME} olay/s`);
for (const S of [2, 4]) {
  const tavan = S / tekVerim;
  console.log(`  S = ${S}: asimptotik tavan ${tavan.toFixed(2)} tuketici -> tek tuketici en az ` +
    `${(ISLEME / tavan).toFixed(2)} olay/s (kuyruklu) / ${(TEPE / tavan).toFixed(2)} olay/s (eszamanli)`);
}
const a = kosum(olaylar, 6, 2), b = kosum(olaylar, 8, 2);
console.log(`  S = 2, N 6 -> 8: bosalma ${((b.tur / a.tur - 1) * 100).toFixed(1)}%, ` +
  `sirasi bozulan +${((b.bozulan / a.bozulan - 1) * 100).toFixed(0)}%, ` +
  `yanlis son durum ${a.yanlisSon} -> ${b.yanlisSon}`);
```

```
is yuku: 5000 gonderi x 7 olay (V4) = 35000 olay
depo yuvasi S: turda en fazla S olay uygulanir (model parametresi)

S = 2
  N  bosalma turu  hizlanma  ek tuketicinin katkisi  verim(olay/tur)  sirasi bozulan  yanlis son durum
  1         89972      1.00                       -            0.389               0                  0
  2         44987      2.00                   1.000            0.778             102                  0
  3         30620      2.94                   0.938            1.143             248                  0
  4         23845      3.77                   0.835            1.468             511                  0
  6         18470      4.87                   0.549            1.895            1590                 30
  8         17551      5.13                   0.128            1.994            2279                432

S = 4
  N  bosalma turu  hizlanma  ek tuketicinin katkisi  verim(olay/tur)  sirasi bozulan  yanlis son durum
  1         89972      1.00                       -            0.389               0                  0
  2         44987      2.00                   1.000            0.778             102                  0
  3         29993      3.00                   1.000            1.167             236                  0
  4         22496      4.00                   1.000            1.556             429                  0
  6         15099      5.96                   0.980            2.318             915                  0
  8         11591      7.76                   0.902            3.020            1543                  0

K01 donusu: tepe yazma 97.22 olay/s, 01. dersin isleme kapasitesi 48.61 olay/s
  S = 2: asimptotik tavan 5.14 tuketici -> tek tuketici en az 9.45 olay/s (kuyruklu) / 18.91 olay/s (eszamanli)
  S = 4: asimptotik tavan 10.28 tuketici -> tek tuketici en az 4.73 olay/s (kuyruklu) / 9.45 olay/s (eszamanli)
  S = 2, N 6 -> 8: bosalma -5.0%, sirasi bozulan +43%, yanlis son durum 30 -> 432
```

#### Katkı Nerede Duruyor

"Ek tüketicinin katkısı" sütunu hızlanmanın tüketici başına artışını veriyor; birim katkı, eklenen
tüketicinin bir tüketicilik iş yaptığı anlamına gelir.

S = 2 sütununda katkı N = 2'de 1,000, N = 3'te 0,938, N = 4'te 0,835, N = 6'da 0,549 ve N = 8'de
0,128. Sekizinci tüketici birincinin sekizde biri kadarını katıyor. Boşalma süresi 18.470 turdan
17.551 tura, yani yalnız yüzde 5 kısalıyor. Verim sütunu nedeni söylüyor: 1,994 olay/tur, yani
S = 2 sınırının kendisi. **Tüketici eklemek kuyruk tarafında bir karardır, ama tavanı kuyruğun
dışındaki bir kaynak koyar.**

S = 4 sütununda aynı tüketici sayıları farklı davranıyor: katkı N = 6'ya kadar 0,98'in üstünde
kalıyor ve N = 8'de 0,902. Hızlanma 7,76, verim 3,020 olay/tur. Tavan bu kez uzaktadır ve tüketici
eklemek hâlâ karşılığını veriyor. İki sütun arasındaki fark tek bir parametredir ve **kuyruk
tarafında hiçbir şey değişmemiştir**.

Tavanın kendisi hesaplanabilir. Tek tüketicinin verimi 0,389 olay/tur olduğuna göre asimptotik
hızlanma tavanı $S / 0{,}389$ olur: S = 2 için 5,14, S = 4 için 10,28 tüketici. Ölçülen 5,13 ve
7,76 değerleri bu tavanla tutarlıdır. Tasarım sonucu doğrudandır: **bir kuyruğun arkasına
konabilecek yararlı tüketici sayısının üst sınırı, tüketicilerin paylaştığı en dar kaynağın
kapasitesine bölünmüş tek tüketici hızıdır.**

#### Verimin Karşılığında Bozulan

Sağdaki iki sütun ödenen bedeldir. "Sırası bozulan" bir gönderinin en az bir olayının kendisinden
sonraki bir olaydan **sonra** uygulanmasıdır; "yanlış son durum", gönderinin en son uygulanan
olayının yedinci adım olmamasıdır, yani gönderi kayıtta bitmiş görünmez.

Tek tüketicide ikisi de sıfırdır: kuyruk sırası uygulama sırasıdır. İkinci tüketiciyle 5000
gönderinin 102'sinin sırası bozuluyor. Dördüncüde 511, altıncıda 1590, sekizincide 2279 — yani
gönderilerin yüzde 45,6'sı. Bozulma tüketici sayısıyla birlikte hızlanarak artıyor, çünkü aynı
anda işlenen olay sayısı arttıkça aynı gönderinin iki olayının çakışma olasılığı artıyor.

İki sütunun ilişkisi dersin en önemli sonucudur. S = 4 ve N = 8'de 1543 gönderinin sırası
bozulduğu hâlde yanlış son durumda kalan gönderi **yoktur**. S = 2 ve N = 8'de ise 2279 bozulmanın
432'si yanlış son duruma dönüşüyor. Aradaki fark, son olayın altı tur sürmesi ve depo yuvası dar
olduğunda uygulanmasının gecikmesidir. Yani **sıra bozulmasının görünür bir hataya dönüşüp
dönüşmemesi, iş sürelerinin ve kaynak darlığının rastlantısına bağlıdır.** Bir tasarım bu
rastlantıya dayanamaz: yüklerin dağılımı değiştiğinde 0 olan sayı 432 olur.

Son satır bu ödünleşimi tek satıra indiriyor. S = 2 iken tüketici sayısını altıdan sekize
çıkarmak boşalma süresini yüzde 5 kısaltıyor, sırası bozulan gönderiyi yüzde 43 artırıyor ve
yanlış son durumda kalan gönderiyi 30'dan 432'ye çıkarıyor. **Kazanç yüzde beş, bedel on dört
kat.** Tavanın ötesindeki tüketiciler verim eklemez, yalnız bozulma ekler.

Sıranın korunması için kurulan çözüm — aynı anahtarın olaylarını aynı bölüme yönlendirmek —
Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda kuruldu ve orada bölüm sayısının koşutluğa
ve anahtar içi sıraya etkisi ölçüldü. Burada tekrarlanmaz; bu dersin katkısı, sıra bozulmasının
**hangi tüketici sayısında hangi maliyete karşılık geldiğini** kapasite kararının yanına koymaktır.

#### Hesaba Geri Dönüş

Tavan, K01'in sayılarına çevrildiğinde bir gereksinime dönüşür. İlk ders işleme kapasitesini
tepenin yarısında, 48,61 olay/s'de seçmişti; eşzamanlı tasarımda gereken 97,22 olay/s'ydi.

S = 2 iken grubun verebileceği en yüksek kapasite tek tüketicinin 5,14 katıdır. 48,61 olay/s'ye
ulaşmak için tek tüketicinin en az **9,45 olay/s** işlemesi gerekir; eşzamanlı tasarımın 97,22
olay/s'si için **18,91 olay/s**. Tek tüketici bunun altındaysa hiçbir tüketici sayısı yetmez ve
çözüm kuyruk tarafında değildir. S = 4 iken aynı eşikler 4,73 ve 9,45 olay/s'ye iner.

Buradan çıkan okuma kuralı şudur: bir kapasite hedefi "kaç tüketici" sorusuyla değil, **iki
sayının çarpımıyla** karşılanır ve çarpanlardan biri paylaşılan kaynağın kapasitesine bağlıdır.
Bir tasarım tartışmasında "tüketici ekleriz" cümlesi, tavan hesaplanmadan söylendiğinde bir çözüm
değil bir varsayımdır.

#### Özet

- Rekabet eden tüketiciler tek bir tarafın işini kaç birime böleceğini belirler; tüketici grupları
  ise aynı iletinin kaç tarafa kopyalanacağını belirler.
- Eklenen tüketicinin katkısı azalıyor: S = 2'de N = 2'de 1,000, N = 4'te 0,835, N = 8'de 0,128;
  boşalma altıdan sekiz tüketiciye geçişte yalnız yüzde 5 kısalıyor.
- Tavanı kuyruk değil paylaşılan kaynak koyuyor: asimptotik hızlanma tavanı S = 2 için 5,14,
  S = 4 için 10,28 tüketicidir ve ölçülen 5,13 ile 7,76 bununla tutarlıdır.
- Sıra bozulması tüketici sayısıyla hızlanarak artıyor: 5000 gönderinin 102'si iki tüketicide,
  511'i dörtte, 2279'u sekizde bozuluyor.
- Bozulmanın görünür hataya dönüşmesi rastlantısaldır: S = 4 ve N = 8'de 1543 bozulmanın hiçbiri
  yanlış son durum üretmezken S = 2'de 2279 bozulmanın 432'si üretiyor.
- K01'e dönüş: 48,61 olay/s'lik işleme kapasitesi S = 2 iken tek tüketiciden en az 9,45 olay/s,
  S = 4 iken 4,73 olay/s ister; tek tüketici bunun altındaysa tüketici sayısı çözüm değildir.

#### Sonraki Adım

Bu dersin iş yükü bir gün boyunca aynı hızda akan bir olay dizisiydi ve tüketici sayısı o hıza
göre seçildi. İlk dersin profili de aynı varsayımı taşıyordu: tepe biliniyor, ne zaman geleceği
biliniyor, süresi biliniyor. Gerçek bir akışta bu üçünün hiçbiri garanti değildir. Taşıyıcının
bağlantısı yirmi dakika koptuğunda hiçbir olay gelmez, sonra biriken her şey bir anda gelir ve o
anki hız günün tepesiyle ilgisiz olur. Sonraki ders bu ani yükü ele alır: kuyruğun yükü zamana
yaymasının önceden bilinmeyen bir tepede ne anlama geldiğini, tamponun ne kadar büyük olması
gerektiğini ve tampon yetmediğinde nelerin düştüğünü sayar.

### Kuyruk Tabanlı Yük Dengeleme

Source: https://academia.sh/tr/kurslar/uygulama-katmani/kuyruk-tabanli-yuk-dengeleme

Önceden bilinmeyen ani yükün kuyrukla tamponlanması: kesinti sonrası toplu geri gönderimin varış hızını on kata çıkarırken tampon gereksinimini hiç değiştirmemesi, ek bir yazma akışının aynı varış hızıyla tamponu yüzde altmış yedi büyütmesi, tampon sınırının hacimden hesaplanması ve tamponsuz tasarımın düşürdüğü olayların sayılması.

Önceki iki dersin iş yükü bir varsayımı sessizce taşıyordu: tepe biliniyor, ne zaman geleceği
biliniyor, süresi biliniyor. İlk ders bu bilgiyi K01'in V8 çarpanından ve kendi K1 varsayımından
kurmuştu. Gerçek bir akışta üçünün hiçbiri garanti değildir. Taşıyıcının bağlantısı yirmi dakika
koptuğunda hiçbir olay gelmez; bağlantı döndüğünde el terminallerinde biriken her şey bir anda
gelir ve o anki hızın günün tepesiyle ilgisi yoktur.

Bu ders o ani yükü ele alır. **Kuyruk tabanlı yük dengeleme** (queue-based load leveling), üretici
ile tüketici arasına bir tampon koyup tüketicinin gördüğü hızı üreticinin ürettiği hızdan ayırma
kararıdır. Adı Trafik Katmanı kursundaki **yük dengeleyici**yi çağrıştırır ve ikisi ayrı şeylerdir:
yük dengeleyici bir isteği birden çok kopyaya dağıtır, yani yükü **uzayda** böler; kuyruk tabanlı
yük dengeleme aynı tüketiciye giden yükü **zamana** yayar. İlk dersten farkı da bir cümleyle
söylenir: orada düzlenen şey **öngörülen** günlük profildi, burada düzlenen şey öngörülmeyen bir
sıçramadır.

#### İki Ayrı Ani Yük

Ani yük tek bir olgu değildir ve bu dersin savı ikisinin ayrılmasıdır. İki varsayım kurulur;
ikisi de bu kursun kendi varsayımıdır ve K01'in tablosuna eklenmez.

**K4 — taşıyıcı kesintisi: 20 dakika.** Gerekçe: taşıyıcının el terminalleri kapsama dışında
kaldığında taramalar cihazda birikir ve bağlantı döndüğünde toplu gönderilir. Kesinti tepe
penceresinin içine düşer; geri gönderilen olay sayısı 1200 × 97,22 = 116.667'dir. Bu bir **hız**
olayıdır: hacim değişmez, aynı olaylar daha kısa sürede gelir. Ne kadar kısa sürede geldiği
tekrar gönderim çarpanı R ile ölçülür ve iki, dört, on değerleriyle koşulur.

**K5 — ek yazma akışının süresi: 1 saat.** Gerekçe: yeni bir taşıyıcı bağlandığında ya da bir
kampanya dönemi açıldığında tepe penceresine bir saatlik ek akış girer. Ek akışın hızı tepeye
eşit alınır; eklenen olay sayısı 3600 × 97,22 = 350.000'dir. Bu bir **hacim** olayıdır: varış
hızı yalnız iki katına çıkar, ama sistemin o gün işleyeceği toplam iş büyür.

Duyarlılıkları doğrusaldır: kesinti iki katına çıkarsa geri gönderilen olay, ek akış iki katına
çıkarsa eklenen olay iki katına çıkar. Ayrımın neden önemli olduğu ölçümden çıkacak.

#### Model

Düzenek ilk dersin günlük profilini aynen kullanır: tepe 97,22 olay/s, K1 gereği üç saat, tepe
dışı 23,15 olay/s. Üstüne iki olaydan biri eklenir. Tüketici kapasitesi ilk dersin seçtiği
değerdir, tepenin yarısı. Kuyruk sınırı da bir parametredir; sıfır sınır **tamponsuz** tasarımı,
yani kapasiteyi aşan her olayın geri çevrilmesini ifade eder — bu davranış Önbellekleme, Kuyruklar
ve Eşzamansız İşleme kursunda **yük boşaltma** adıyla kurulmuştu ve burada yalnız karşılaştırma
tabanı olarak kullanılıyor. Model olduğu için sonuçlar belirlenimlidir ve makineden bağımsızdır.

```js
// duzleme/ani-yuk.mjs — ani yukun iki bicimi: kesintiden sonra toplu geri gonderim (hiz artisi,
// hacim sabit) ve ek bir yazma akisi (hacim artisi). MODEL: tur bir saniyedir, gunluk profil
// 01. dersle aynidir. Sonuclar belirlenimlidir (hesap sinifi) ve makineden bagimsizdir.
const GUN = 86_400;
const GUNLUK_OLAY = 2_800_000;           // K01: gunluk durum olayi
const TEPE = (GUNLUK_OLAY / GUN) * 3;    // K01 hesabi + V8: tepe yazma olay/s
const TEPE_BAS = 9 * 3600, TEPE_SN = 3 * 3600;   // K1: 3 saatlik tepe penceresi
const DISI = (GUNLUK_OLAY - TEPE * TEPE_SN) / (GUN - TEPE_SN);
const K4 = 20 * 60;                      // bu kursun varsayimi K4: tasiyici kesintisi (s)
const K5 = 3600;                         // bu kursun varsayimi K5: ek akisin suresi (s)
const BAS = TEPE_BAS + 2400;             // her iki olay da tepe penceresinin icinde

const olagan = (t) => (t >= TEPE_BAS && t < TEPE_BAS + TEPE_SN ? TEPE : DISI);

function kosum({ kapasite, sinir, R = 1, kip = "yok" }) {
  let kalan = 0, birikme = 0, dusen = 0, enYuksek = 0, bosalma = -1, gelenToplam = 0;
  const OLC = 2 * GUN;
  for (let t = 0; t < 4 * GUN; t += 1) {
    const g = t % GUN;
    let gelen = olagan(g);
    if (kip === "tekrar") {
      if (g >= BAS && g < BAS + K4) { kalan += gelen; gelen = 0; }
      else { const ek = Math.min(kalan, (R - 1) * TEPE); kalan -= ek; gelen += ek; }
    } else if (kip === "hacim" && g >= BAS && g < BAS + K5) gelen += TEPE;
    const yer = sinir === 0 ? kapasite : sinir - birikme;
    const alinan = Math.min(gelen, Math.max(0, yer));
    if (t >= OLC && t < OLC + GUN) { dusen += gelen - alinan; gelenToplam += gelen; }
    birikme += sinir === 0 ? Math.min(alinan, kapasite) : alinan;
    birikme -= Math.min(birikme, kapasite);
    if (t >= OLC && t < OLC + GUN && birikme > enYuksek) enYuksek = birikme;
    if (bosalma < 0 && t > OLC + TEPE_BAS && birikme < 1 && kalan < 1) bosalma = t - OLC - TEPE_BAS;
  }
  return { dusen, gelenToplam, enYuksek, bosalma, bekleme: enYuksek / kapasite };
}

const varisi = (o) => (o.kip === "tekrar" ? o.R * TEPE : o.kip === "hacim" ? 2 * TEPE : TEPE);
const satir = (ad, o) => {
  const r = kosum(o), v = varisi(o);
  console.log(`${ad.padEnd(24)} ${v.toFixed(0).padStart(7)} ${o.kapasite.toFixed(2).padStart(8)} ` +
    `${r.dusen.toFixed(0).padStart(7)} ${r.enYuksek.toFixed(0).padStart(9)} ` +
    `${(r.bekleme / 3600).toFixed(2).padStart(11)} ` +
    `${(r.bosalma < 0 ? NaN : r.bosalma / 3600).toFixed(2).padStart(11)} ` +
    `${(v / o.kapasite).toFixed(2).padStart(7)}`);
};

console.log(`K01 tepe yazma ${TEPE.toFixed(2)} olay/s; K4 kesinti ${K4} s -> ` +
  `${(TEPE * K4).toFixed(0)} olay geri gonderilir`);
console.log(`K5 ek akis ${K5} s x ${TEPE.toFixed(2)} olay/s -> ${(TEPE * K5).toFixed(0)} olay eklenir\n`);
console.log("tasarim                  varis/s  kapasite   dusen   birikme  bekleme(sa)  bosalma(sa)  duzleme");
satir("tamponsuz, olay yok", { kapasite: TEPE, sinir: 0 });
satir("tamponsuz tekrar R=4", { kapasite: TEPE, sinir: 0, R: 4, kip: "tekrar" });
satir("tamponsuz hacim", { kapasite: TEPE, sinir: 0, kip: "hacim" });
satir("kuyruk 0.5x, olay yok", { kapasite: TEPE / 2, sinir: Infinity });
for (const R of [2, 4, 10]) satir(`kuyruk 0.5x tekrar R=${R}`, { kapasite: TEPE / 2, sinir: Infinity, R, kip: "tekrar" });
satir("kuyruk 0.5x hacim", { kapasite: TEPE / 2, sinir: Infinity, kip: "hacim" });

const enAzSinir = (o) => {
  let alt = 0, ust = 2_000_000;
  while (ust - alt > 500) { const orta = (alt + ust) / 2; if (kosum({ ...o, sinir: orta }).dusen < 1) ust = orta; else alt = orta; }
  return ust;
};
console.log("\ngereken en kucuk kuyruk siniri (kapasite 0.5x tepe):");
for (const [ad, o] of [["olay yok", {}], ["tekrar R=4", { R: 4, kip: "tekrar" }],
  ["tekrar R=10", { R: 10, kip: "tekrar" }], ["hacim", { kip: "hacim" }]]) {
  console.log(`  ${ad.padEnd(12)} ${enAzSinir({ kapasite: TEPE / 2, ...o }).toFixed(0).padStart(9)}`);
}
console.log("\nsinir 525000 (olaysiz gune gore secilmis) ile dusen olay:");
for (const [ad, o] of [["tekrar R=4", { R: 4, kip: "tekrar" }], ["tekrar R=10", { R: 10, kip: "tekrar" }],
  ["hacim", { kip: "hacim" }]]) {
  console.log(`  ${ad.padEnd(12)} ${kosum({ kapasite: TEPE / 2, sinir: 525_000, ...o }).dusen.toFixed(0).padStart(9)}`);
}
```

```
K01 tepe yazma 97.22 olay/s; K4 kesinti 1200 s -> 116667 olay geri gonderilir
K5 ek akis 3600 s x 97.22 olay/s -> 350000 olay eklenir

tasarim                  varis/s  kapasite   dusen   birikme  bekleme(sa)  bosalma(sa)  duzleme
tamponsuz, olay yok           97    97.22       0         0        0.00        0.00    1.00
tamponsuz tekrar R=4         389    97.22  116667         0        0.00        0.00    4.00
tamponsuz hacim              194    97.22  350000         0        0.00        0.00    2.00
kuyruk 0.5x, olay yok         97    48.61       0    525000        3.00        8.73    2.00
kuyruk 0.5x tekrar R=2       194    48.61       0    525000        3.00        8.73    4.00
kuyruk 0.5x tekrar R=4       389    48.61       0    525000        3.00        8.73    8.00
kuyruk 0.5x tekrar R=10      972    48.61       0    525000        3.00        8.73   20.00
kuyruk 0.5x hacim            194    48.61       0    875000        5.00       12.55    4.00

gereken en kucuk kuyruk siniri (kapasite 0.5x tepe):
  olay yok        525391
  tekrar R=4      525391
  tekrar R=10     525391
  hacim           875488

sinir 525000 (olaysiz gune gore secilmis) ile dusen olay:
  tekrar R=4          49
  tekrar R=10         49
  hacim           350049
```

#### Hız Sıçraması Tampona Yazılmıyor

Tablonun ortasındaki dört satır dersin en aykırı sonucudur. Tekrar gönderim çarpanı ikiden dörde,
dörtten ona çıkarken varış hızı 194'ten 389'a, oradan 972 olay/s'ye çıkıyor — yani K01'in tepe
yazma hızının on katına. Aynı satırlarda en yüksek birikme **525.000'de sabit kalıyor**, en uzun
bekleme 3,00 saatte, boşalma 8,73 saatte. Üç sayı da olaysız günün sayılarıyla birebir aynıdır.

Nedeni hacim korunumudur. Kesinti sırasında hiçbir olay gelmez ve tüketici çalışmaya devam eder,
yani kuyruk o yirmi dakika boyunca **boşalır**; geri gönderim aynı olayları geri koyar. Kesintinin
açtığı çukur ile sıçramanın yaptığı tepe birbirini götürür, çünkü ikisinin toplamı değişmemiştir.
Tüketicinin gördüğü tek şey aynı gün içinde aynı sayıda iştir.

Sağdaki **düzleme** sütunu kuyruğun yaptığı işi tek sayıyla veriyor: varış hızının tüketici
kapasitesine oranı. Olaysız günde 2,00, tekrar gönderimde 4,00, 8,00 ve **20,00**. Yirmi katlık bir
varış sıçraması tüketici tarafında hiçbir iz bırakmıyor. Bu, kuyruğun asıl işidir ve tek satırla
söylenebilir: **kuyruk hızı düzler, hacmi düzlemez.**

Tamponsuz tasarımda aynı olay 116.667 olay düşürüyor ve düşen sayı R'den bağımsızdır — hangi hızla
geldiği fark etmez, kapasitenin üstünde kalan her şey gider. Bu tasarımda tek çare kapasiteyi
varış tepesine göre seçmektir; R = 10 için gereken kapasite 972 olay/s, yani K01'in tepe yazma
hızının on katı ve günün geri kalanında tamamen boş duran bir kapasite.

Üreticiyi yavaşlatmak da bir seçenek gibi görünür; Önbellekleme, Kuyruklar ve Eşzamansız İşleme
kursunda **geri basınç** adıyla kurulan yol budur. Bu akışta uygulanamaz, çünkü üretici taşıyıcının
el terminalidir: yavaşlatıldığında olayları yine kendi belleğinde biriktirir ve sıçrama yalnız
ertelenir. Geri basınç, üreticinin gerçekten duraklatılabildiği zincirlerde çalışır.

#### Hacim Sıçraması Tampona Yazılıyor

Son satır aynı kuyruğun aynı kapasitesiyle çok farklı davranıyor. Ek yazma akışı varış hızını
yalnız iki katına çıkarıyor — 194 olay/s, tekrar gönderimin en yumuşak hâliyle aynı — ama en
yüksek birikme 525.000'den **875.000'e**, yani 1,67 katına çıkıyor. En uzun bekleme 3,00 saatten
5,00 saate, boşalma 8,73 saatten 12,55 saate uzuyor.

İki olayın varış hızları karşılaştırıldığında sonuç kesindir: 972 olay/s'lik bir sıçrama tampona
hiçbir şey eklemezken 194 olay/s'lik bir akış 350.000 olay ekliyor. **Tamponu dolduran şey varış
hızı değil, işlenmemiş hacimdir.** Bir tasarım tartışmasında "saniyede kaç istek geldi" sorusu
tampon boyutunu belirlemez; belirleyen soru, gelen işin ne kadarının o gün içinde işlenmeden
kaldığıdır.

#### Sınır Nereye Konur

Sınırsız kuyruk bir tasarım değildir; her tampon sonludur ve dolduğunda ya üreticiyi durdurur ya
olay düşürür. Sınırın nereye konacağı ölçülebilir bir sorudur ve iki grup çıktı yanıtını veriyor.

Gereken en küçük sınır olaysız günde 525.391, tekrar gönderim olayında R'den bağımsız olarak yine
**525.391**, hacim olayında ise **875.488**. Yani öngörülen günlük profile göre boyutlanmış bir
tampon, on kat varış sıçramasını tek olay düşürmeden karşılar; aynı tampon hacim artışında
yetersiz kalır.

Son grup bunu doğrudan sayıyor. Sınır olaysız güne göre 525.000 seçildiğinde tekrar gönderim
olayında düşen olay sayısı 49'dur — sınırın gerçek gereksinimden 391 olay eksik yuvarlanmasının
sonucu, yani pratikte sıfır. Aynı sınırla hacim olayında **350.049** olay düşer ve bu sayı, eklenen
hacmin kendisidir.

Kararın kuralı buradan çıkar: **tampon sınırı bir hız sayısından değil, bir hacim sayısından
hesaplanır** — en kötü günün üretilen ile işlenen iş farkından. Sınırı seçtikten sonra düşme
davranışının ne olacağı ayrı bir karardır ve seçenekleri (üreticiyi engellemek, yeniyi atmak,
eskiyi atmak) Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda ölçüldü.

#### Hesaba Geri Dönüş

K01'in tablosunda bu dersin ihtiyaç duyduğu sayı yoktur ve eksikliğin kendisi sonuçtur. V8 tepe
çarpanı 3'tür ve tepe yazma hızını 97,22 olay/s yapar; o sayı **öngörülen** tepedir. Kabul yolunun
karşılamak zorunda olduğu varış hızı ise ölçümde 194, 389 ve 972 olay/s çıktı — V8'in ürettiği
sayının iki, dört ve on katı. Bir tasarım kabul ucunu V8'e göre boyutlarsa, tek bir yirmi dakikalık
kesintinin ardından kabul ucu doyar.

İşleme kapasitesi ise hiç değişmedi: ilk dersin seçtiği 48,61 olay/s, on kat varış sıçramasında da
yeterlidir. K01'in "tepe yazma 97,22 istek/s" satırı böylece iki ayrı sayıya bölünüyor — kabul
yolunun görmesi gereken **anlık varış tepesi** ve işleme yolunun karşılaması gereken **hacim
hızı** — ve ikisi arasındaki oran, kuyruğun düzleme çarpanıdır.

#### Özet

- Kuyruk tabanlı yük dengeleme yükü zamana yayar; yük dengeleyici ise yükü kopyalara, yani uzaya
  böler. İlk dersin düzlediği profil öngörülüydü, buradaki sıçrama öngörülmemiştir.
- Yirmi dakikalık kesintinin ardından gelen 116.667 olay varış hızını 972 olay/s'ye çıkardığı
  hâlde en yüksek birikme 525.000'de, bekleme 3,00 saatte, boşalma 8,73 saatte sabit kaldı.
- Bir saatlik ek akış varış hızını yalnız 194 olay/s'ye çıkardı ama birikmeyi 875.000'e (1,67
  kat), beklemeyi 5,00 saate, boşalmayı 12,55 saate taşıdı.
- Tamponu dolduran şey varış hızı değil hacimdir: gereken en küçük kuyruk sınırı tekrar gönderimde
  R'den bağımsız 525.391, hacim artışında 875.488 çıktı.
- Öngörülen profile göre 525.000 seçilmiş bir sınır, on kat varış sıçramasında 49 olay, hacim
  artışında 350.049 olay düşürüyor.
- K01'in tepe yazma hızı iki sayıya ayrılıyor: kabul yolunun gördüğü anlık varış tepesi V8'in
  ürettiği 97,22'nin on katına kadar çıkabilir, işleme yolunun karşıladığı hacim hızı 48,61
  olay/s'de kalır.

#### Sonraki Adım

Buraya kadarki bütün kuyruklarda tek bir iş türü vardı ve tüketiciler sıradaki işi ayırt etmeden
aldı. Aynı sistemde kuyruğa giren işler birbirinin eşi değildir: taşıyıcının durum olayı alıcının
takip sayfasında görünür ve gecikmesi doğrudan hissedilir, gün sonu ücretlendirme kaydının ise
sabaha kadar zamanı vardır, satıcının rapor işi ise ikisinden de bekleyebilir. Bu dersin 525.000
işlik birikmesi tek bir sıra olarak ele alındığında üçü de aynı üç saati bekler. Sonraki ders
sıranın kendisini bir karara çevirir: işleri hizmet düzeyine göre ayırmanın hangi sınıfa ne
kazandırdığını, hangisine ne kaybettirdiğini ve önceliğin sınırsız verildiğinde neyi tamamen
durdurduğunu ölçer.

### Öncelikli Kuyruk

Source: https://academia.sh/tr/kurslar/uygulama-katmani/oncelikli-kuyruk

Aynı tüketici grubunu paylaşan üç iş sınıfının hizmet düzeyine göre ayrılması: tek sırada üç sınıfın aynı beklemeyi paylaşması, katı önceliğin en düşük sınıfı tamamen durdurması, pay ayırmanın açığı yeniden dağıtması, payın aynı anda bir taban ve bir tavan olması ve toplam açığın politikadan bağımsız kalması.

Buraya kadarki bütün kuyruklarda tek bir iş türü vardı ve tüketiciler sıradaki işi ayırt etmeden
aldı. Aynı sistemde kuyruğa giren işler birbirinin eşi değildir: taşıyıcının durum olayı alıcının
takip sayfasında görünür ve gecikmesi doğrudan hissedilir; gün sonu ücretlendirme kaydının sabaha
kadar zamanı vardır; satıcının rapor işi ikisinden de bekleyebilir. Önceki dersin 525.000 işlik
birikmesi tek bir sıra olarak ele alındığında üçü de aynı üç saati bekler.

Bu ders sıranın kendisini bir karara çevirir. **Öncelikli kuyruk** (priority queue) burada bir
hizmet düzeyi kararıdır: hangi işin hangisinin önüne geçeceğini iş sınıfına bakarak seçmek.
Veri Yapıları müfredatında aynı İngilizce terimin karşılığı olan **öncelik kuyruğu** bir veri
yapısıdır — anahtarına göre en küçüğü veren yığın tabanlı bir yapı; ikisi ayrı şeylerdir ve bu
derste veri yapısı anlatılmaz, kararın kapasite karşılığı ölçülür.

#### Üç Sınıf ve Bir Kapasite

Ölçüm penceresi K01'in V10 varsayımıdır: dört saat. Pencereye üç sınıf girer ve üçünün de hızı
K01'den gelir.

**A — durum olayı.** 97,22 iş/s (K01 tepe yazma hızı, V8 çarpanıyla). Hizmet düzeyi: alıcının
takip sayfasında görünür, beklemesi kısa olmalı.

**B — ücretlendirme kaydı.** 833,33 iş/s (K01 toplu iş tarama hızı). Hizmet düzeyi: tek tek
beklemesi önemsizdir, pencerenin sonunda bitmesi gerekir.

**C — rapor işi.** Yüz rapor, her biri 3000 kayıt — önceki derste K01'den türetilen boy. Hizmet
düzeyi: gün içinde bitsin.

İş birimi bir kaydın işlenmesidir, dolayısıyla üç sınıf aynı birimle sayılır. Üçünün toplam
gereksinimi 951,39 iş/s'dir. Kapasite bir varsayımdır:

**K6 — ortak tüketici grubunun kapasitesi: 900 iş/s.** Bu kursun kendi varsayımıdır ve K01'in
tablosuna eklenmez. Gerekçe: grup gün sonu işinin gereksinimine (833,33 kayıt/s) küçük bir payla
boyutlandırılmış, olay akışı ile rapor işleri sonradan aynı gruba verilmiştir. Duyarlılığı
çıktının sonunda ölçülüyor.

Üç sınıfın tam çakışması en kötü durumdur; pencereleri ayırmak bu dersin ele aldığı sorunu ortadan
kaldıran bir tasarım kararıdır ve ölçüm o kararın değerini de gösterir.

Karşılaştırılan üç politika şudur: **tek sıra**, işleri geliş sırasına göre alır; **katı öncelik**,
kapasiteyi önce A'ya, kalanı B'ye, kalanı C'ye verir; **pay ayırma**, her sınıfa kapasitenin bir
oranını ayırır (A 0,15, B 0,75, C 0,10) ve kullanılmayan payı sırayla ötekilere dağıtır. Dördüncü
ve beşinci koşumlarda A'ya önceki dersin sıçraması eklenir: yirmi dakika boyunca dört kat.

```js
// oncelik/sinif.mjs — uc is sinifinin ayni tuketici grubunu paylasmasi: tek sira, kati oncelik
// ve pay ayirma. MODEL: tur bir saniyedir, is birimi bir kayit islemedir.
// Sonuclar belirlenimlidir (hesap sinifi) ve makineden bagimsizdir.
const PENCERE = 4 * 3600;          // K01 varsayim V10: gun sonu isinin penceresi
const A_HIZ = (2_800_000 / 86_400) * 3;   // K01 + V8: tepe yazma olay/s
const B_HIZ = 833.3333;            // K01: toplu is tarama kayit/s
const C_ADET = 100, C_IS = 3000;   // rapor isleri: her biri 3000 kayit (02. ders)
const K6 = 900;                    // bu kursun varsayimi K6: grup kapasitesi (is/s)

class Sira {
  constructor() { this.b = []; this.bas = 0; this.uzunluk = 0; this.gelen = 0; }
  ekle(t, n) { if (n > 0) { this.b.push({ t, n }); this.uzunluk += n; this.gelen += n; } }
  bekleyenBas() { return this.bas < this.b.length ? this.b[this.bas].t : Infinity; }
  al(t, s) {
    let kalan = s;
    while (kalan > 1e-9 && this.bas < this.b.length) {
      const g = this.b[this.bas];
      const k = Math.min(kalan, g.n);
      g.n -= k; kalan -= k; this.uzunluk -= k;
      this.toplamBekleme = (this.toplamBekleme ?? 0) + k * (t - g.t);
      this.bitenIs = (this.bitenIs ?? 0) + k;
      if (t - g.t > (this.enUzun ?? 0)) this.enUzun = t - g.t;
      if (g.n <= 1e-9) this.bas += 1;
    }
    return s - kalan;
  }
}

function kosum(politika, kapasite, aCarpan) {
  const s = [new Sira(), new Sira(), new Sira()];
  const PAY = [0.15, 0.75, 0.10];
  for (let t = 0; t < PENCERE; t += 1) {
    const patlama = aCarpan > 1 && t >= 3600 && t < 3600 + 1200;
    s[0].ekle(t, A_HIZ * (patlama ? aCarpan : 1));
    s[1].ekle(t, B_HIZ);
    if (t % Math.floor(PENCERE / C_ADET) === 0) s[2].ekle(t, C_IS);
    let kalan = kapasite;
    if (politika === "tek-sira") {
      while (kalan > 1e-9) {
        const i = [0, 1, 2].reduce((a, b) => (s[b].bekleyenBas() < s[a].bekleyenBas() ? b : a), 0);
        if (s[i].bekleyenBas() === Infinity) break;
        kalan -= s[i].al(t, Math.min(kalan, s[i].b[s[i].bas].n));
      }
    } else if (politika === "kati-oncelik") {
      for (const i of [0, 1, 2]) kalan -= s[i].al(t, kalan);
    } else {
      for (const i of [0, 1, 2]) kalan -= s[i].al(t, Math.min(kalan, kapasite * PAY[i]));
      for (const i of [0, 1, 2]) kalan -= s[i].al(t, kalan);
    }
  }
  return s;
}

const AD = ["A durum olayi", "B ucretlendirme", "C rapor"];
console.log(`pencere ${PENCERE} s (V10), kapasite K6 = ${K6} is/s`);
console.log(`gereken hiz = ${((A_HIZ * PENCERE + B_HIZ * PENCERE + C_ADET * C_IS) / PENCERE).toFixed(2)} is/s\n`);
for (const [ad, pol, kap, carpan] of [["tek sira", "tek-sira", K6, 1], ["kati oncelik", "kati-oncelik", K6, 1],
  ["pay ayirma", "pay", K6, 1], ["kati oncelik + A patlamasi", "kati-oncelik", K6, 4],
  ["pay ayirma + A patlamasi", "pay", K6, 4]]) {
  console.log(`${ad} (kapasite ${kap})`);
  console.log("  sinif             gelen     biten   biten/gelen  ort. bekleme(dk)  en uzun(dk)  kalan birikme");
  const siralar = kosum(pol, kap, carpan);
  for (const [i, q] of siralar.entries()) {
    const biten = q.bitenIs ?? 0;
    console.log(`  ${AD[i].padEnd(16)} ${q.gelen.toFixed(0).padStart(8)} ${biten.toFixed(0).padStart(9)} ` +
      `${(biten / q.gelen).toFixed(3).padStart(13)} ${((q.toplamBekleme ?? 0) / Math.max(biten, 1) / 60).toFixed(1).padStart(17)} ` +
      `${((q.enUzun ?? 0) / 60).toFixed(1).padStart(12)} ${Math.max(0, q.uzunluk).toFixed(0).padStart(14)}`);
  }
  const toplam = siralar.reduce((a, q) => a + Math.max(0, q.uzunluk), 0);
  console.log(`  toplam kalan birikme: ${toplam.toFixed(0)}\n`);
}

const gereken = (A_HIZ * PENCERE + B_HIZ * PENCERE + C_ADET * C_IS) / PENCERE;
console.log(`acik = (${gereken.toFixed(2)} - ${K6}) x ${PENCERE} = ` +
  `${((gereken - K6) * PENCERE).toFixed(0)} is; her politikada ayni`);
console.log(`acigi kapatmak: kapasite ${K6} -> ${gereken.toFixed(2)} is/s (x${(gereken / K6).toFixed(3)}) ` +
  `ya da pencere 4.00 -> ${(4 * gereken / K6).toFixed(2)} saat (V10)`);
console.log("K6 duyarliligi (pay ayirma, toplam kalan birikme):");
for (const kap of [850, 900, 950]) {
  const toplam = kosum("pay", kap, 1).reduce((a, q) => a + Math.max(0, q.uzunluk), 0);
  console.log(`  kapasite ${kap} -> ${toplam.toFixed(0)}`);
}
```

```
pencere 14400 s (V10), kapasite K6 = 900 is/s
gereken hiz = 951.39 is/s

tek sira (kapasite 900)
  sinif             gelen     biten   biten/gelen  ort. bekleme(dk)  en uzun(dk)  kalan birikme
  A durum olayi     1400000   1324264         0.946               6.5         13.0          75736
  B ucretlendirme  12000000  11350736         0.946               6.5         13.0         649263
  C rapor            300000    285000         0.950               6.5         12.9          15000
  toplam kalan birikme: 740000

kati oncelik (kapasite 900)
  sinif             gelen     biten   biten/gelen  ort. bekleme(dk)  en uzun(dk)  kalan birikme
  A durum olayi     1400000   1400000         1.000               0.0          0.0              0
  B ucretlendirme  12000000  11560000         0.963               4.4          8.8         440000
  C rapor            300000         0         0.000               0.0          0.0         300000
  toplam kalan birikme: 740000

pay ayirma (kapasite 900)
  sinif             gelen     biten   biten/gelen  ort. bekleme(dk)  en uzun(dk)  kalan birikme
  A durum olayi     1400000   1400000         1.000               0.0          0.0              0
  B ucretlendirme  12000000  11260000         0.938               7.4         14.8         740000
  C rapor            300000    300000         1.000               0.3          0.6              0
  toplam kalan birikme: 740000

kati oncelik + A patlamasi (kapasite 900)
  sinif             gelen     biten   biten/gelen  ort. bekleme(dk)  en uzun(dk)  kalan birikme
  A durum olayi     1750000   1750000         1.000               0.0          0.0              0
  B ucretlendirme  12000000  11210000         0.934               9.5         15.8         790000
  C rapor            300000         0         0.000               0.0          0.0         300000
  toplam kalan birikme: 1090000

pay ayirma + A patlamasi (kapasite 900)
  sinif             gelen     biten   biten/gelen  ort. bekleme(dk)  en uzun(dk)  kalan birikme
  A durum olayi     1750000   1750000         1.000               3.5         18.1              0
  B ucretlendirme  12000000  10910000         0.909              12.0         21.8        1090000
  C rapor            300000    300000         1.000               0.3          0.6              0
  toplam kalan birikme: 1090000

acik = (951.39 - 900) x 14400 = 740000 is; her politikada ayni
acigi kapatmak: kapasite 900 -> 951.39 is/s (x1.057) ya da pencere 4.00 -> 4.23 saat (V10)
K6 duyarliligi (pay ayirma, toplam kalan birikme):
  kapasite 850 -> 1460000
  kapasite 900 -> 740000
  kapasite 950 -> 20000
```

#### Tek Sıra Adildir ve Yanlıştır

İlk blokta üç sınıfın sayıları neredeyse aynı: tamamlanma oranı 0,946 / 0,946 / 0,950, ortalama
bekleme üçünde de 6,5 dakika, en uzun bekleme 13,0 dakika. Tek sıra eşit davranıyor.

Eşitliğin bedeli, sınıfların hizmet düzeylerinin eşit olmamasıdır. Alıcının takip sayfasında
görünen durum olayı 6,5 dakika bekliyor; aynı 6,5 dakikayı, sabaha kadar zamanı olan
ücretlendirme kaydı da bekliyor. Tek sıra hiçbir sınıfa zarar vermez ve hiçbirine yardım etmez;
**hizmet düzeyi kavramının bulunmadığı bir politikadır.**

#### Katı Öncelik Açlık Üretir

İkinci blokta A'ya mutlak öncelik verildi. Sonuç A için kusursuz: 1.400.000 işin tamamı bitiyor,
ortalama ve en uzun bekleme 0,0 dakika. B de iyileşiyor — bekleme 6,5'ten 4,4 dakikaya, tamamlanma
0,946'dan 0,963'e — çünkü kapasitenin A'dan artanı tamamen ona gidiyor.

C için sonuç başkadır: **tamamlanan iş sıfır.** Yüz raporun hiçbiri bitmedi; 300.000 işin tamamı
pencerenin sonunda kuyrukta duruyor. Bu **açlıktır** (starvation): üst sınıfların toplam talebi
kapasiteyi doldurduğu sürece alt sınıfa hiçbir şey kalmaz ve bekleme süresi bir sayıyla ifade
edilemez, çünkü iş hiç başlamaz. Katı öncelik bir sıralama değil, koşullu bir **iptaldir**.

#### Pay Ayırma Açığı Yeniden Dağıtır

Üçüncü blokta her sınıfa bir pay ayrıldı. A yine kusursuz (0,0 dakika bekleme), çünkü ayrılan pay
135 iş/s ve ihtiyacı 97,22. C'nin tamamı bitiyor: 300.000 işin hepsi, ortalama bekleme 0,3 dakika.

Bedeli B ödüyor: tamamlanma 0,963'ten 0,938'e, kalan birikme 440.000'den 740.000'e çıkıyor. Fark
tam olarak **300.000**, yani C'nin iş hacminin kendisi. Politika değişimi hiçbir iş yaratmadı;
C'nin işini B'nin sırtından aldı ve muhasebe kuruşuna kadar tutuyor.

#### Pay Bir Taban ve Bir Tavandır

Son iki blok aynı politikaları A'nın sıçraması altında gösteriyor. Katı öncelikte A yine
etkilenmiyor: 1.750.000 işin tamamı, 0,0 dakika bekleme. Pay ayırmada A **bekliyor**: ortalama
3,5, en uzun 18,1 dakika. Nedeni doğrudandır — sıçrama sırasında A'nın hızı 388,9 iş/s'ye
çıkıyor, ayrılan pay ise 135 iş/s. Kullanılmayan paylar A'ya aktarılıyor ama yetmiyor.

**Bir pay, aynı anda bir taban ve bir tavandır.** C'yi açlıktan kurtaran şey, A'yı sıçramada
sınırlayan şeyle aynı mekanizmadır. Politika seçimi bu yüzden "hangisi daha iyi" sorusuna değil,
"hangi sınıfın kötü gününde ne olmasını istiyoruz" sorusuna bakar. Katı öncelik en üst sınıfın
kötü gününü öteki sınıflara ödetir; pay ayırma her sınıfın kötü gününü kendi payıyla sınırlar.

#### Hesaba Geri Dönüş

Son üç satır dersin sınırını çiziyor. Üç sınıfın toplam gereksinimi 951,39 iş/s, kapasite 900;
açık dört saatlik pencerede 740.000 iştir. **Bu sayı beş koşumun beşinde de aynıdır.** Tek sırada
75.736 + 649.263 + 15.000, katı öncelikte 0 + 440.000 + 300.000, pay ayırmada 740.000 + 0 + 0.
Politika açığın kime yazılacağını seçiyor, açığı küçültmüyor. Sıçramalı koşumlarda toplam
1.090.000'e çıkıyor ve fark eklenen 350.000 işin ta kendisidir.

Açığı kapatmanın iki yolu vardır ve ikisi de kuyruğun dışındadır: kapasiteyi 900'den 951,39 iş/s'ye
çıkarmak (1,057 kat) ya da V10 penceresini 4,00 saatten 4,23 saate uzatmak. K6'nın duyarlılığı bunu
doğruluyor: kapasite 850'de toplam kalan 1.460.000, 900'de 740.000, 950'de 20.000. Yüzde altılık
bir kapasite artışı açığın yüzde 97'sini siliyor.

Buradan K01'e dönen sonuç şudur: V10'un dört saatlik penceresi, gün sonu işi **tek başına**
koşarken geçerlidir. Aynı gruba başka bir akış girdiğinde pencere artık bir kısıt değil, üç
sınıfın paylaştığı bir kaynaktır ve V10'un sağlanıp sağlanmadığı yalnız B'nin hızına bakarak
söylenemez.

#### Özet

- Öncelikli kuyruk bir hizmet düzeyi kararıdır; Veri Yapıları müfredatındaki öncelik kuyruğu ise
  bir veri yapısıdır ve bu derste anlatılmaz.
- Tek sırada üç sınıf aynı sayıları alıyor — tamamlanma 0,946 / 0,946 / 0,950, bekleme 6,5 dakika
  — yani hizmet düzeyi kavramı yoktur.
- Katı öncelik en üst sınıfı kusursuz karşılıyor (0,0 dakika bekleme) ve en alt sınıfta açlık
  üretiyor: yüz raporun sıfırı bitti, 300.000 iş kuyrukta kaldı.
- Pay ayırma C'nin tamamını bitiriyor ve bedelini kuruşuna kadar B ödüyor: B'nin kalan birikmesi
  440.000'den 740.000'e, tam 300.000 artıyor.
- Pay bir taban ve bir tavandır: A'nın sıçramasında katı öncelik A'yı hiç bekletmezken pay ayırma
  ortalama 3,5, en uzun 18,1 dakika bekletiyor.
- Toplam açık politikadan bağımsızdır: beş koşumun üçünde de 740.000 iş; kapatmanın yolu
  kapasiteyi 1,057 katına çıkarmak ya da V10 penceresini 4,23 saate uzatmaktır.

#### Sonraki Adım

Bu ders sırayı bir **tercih** olarak ele aldı: öncelikli kuyruk, işleri hangi sırayla alacağımızı
seçmenin bir yoluydu ve hızlı olanı yavaş olanın önüne geçiriyordu. Bir işin beklemesi burada bir
hizmet düzeyi kararıydı, bir doğruluk sorunu değil. Bazı işlerde ise sıra seçilebilir bir şey
değildir. Aynı gönderinin durum olayları birbirinin üstüne yazar; "aktarma merkezinden ayrıldı"
ile "teslim edildi" ters sırayla işlenirse gönderi teslim edildikten sonra yeniden yolda görünür
ve sonuç bozulur — üçüncü derste sekiz tüketicide 2279 gönderinin sırası bu yüzden bozulmuştu.
Böyle işlerde sıra bir tercih değil bir **kısıttır**. Sonraki ders sıranın korunması gereken
işleri ele alır ve kısıtın koşutlukla nasıl bir arada tutulacağını inceler.

### Sıralı Konvoy

Source: https://academia.sh/tr/kurslar/uygulama-katmani/sirali-konvoy

Sıranın bir tercih değil kısıt olduğu işlerin yönetimi: aynı gönderinin durum olaylarını anahtar başına tek şeride sokan sıralı konvoyun, serbest tüketimin ve sürüm damgalı işleyicinin aynı olay kümesinde ölçülmesi; geriye yazma, yanlış son durum ve geçmişte eksik kalan adım sayılarının ayrılması; şerit inceliğinin verim tavanını belirlemesi ve maliyetin K01'in tepe yazma hızına uygulanması.

Önceki ders sırayı bir **tercih** olarak ele aldı: öncelikli kuyruk, işleri hangi sırayla
alacağımızı seçmenin bir yoluydu ve hızlı olanı yavaş olanın önüne geçiriyordu. Bir işin
beklemesi orada bir hizmet düzeyi kararıydı, bir doğruluk sorunu değil. Bazı işlerde ise sıra
seçilebilir bir şey değildir. Aynı gönderinin durum olayları — kabul, aktarma, dağıtıma çıkış,
teslim — birbirinin üstüne yazar; ters sırayla işlenirse gönderi teslim edildikten sonra
yeniden aktarmada görünür.

**Sıralı konvoy** (sequential convoy), sıra kısıtı taşıyan işleri bir anahtara göre tek şeride
sokan, şeritler arasında ise koşutluğu koruyan yerleşimdir. Kuyruk mekaniği, teslim garantileri
ve sıra ihlalinin ters çiftle sayılması Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda
kurulmuştu; burada teslim garantisi hiç tartışılmaz. Bu dersin sorusu bir kapasite sorusudur:
sırayı korumak koşutluktan ne kadarını geri veriyor ve bu kısıttan kaçmanın bir yolu var mı.

#### Sıra Kısıtı Taşıyan Olay

Bir gönderinin bütün olayları sıra istemez. "Bölgeye vardı" bilgisi bir durum geçişidir;
"tarayıcıdan geçti" kaydı ise durumun üstüne yazmaz, yalnız geçmişe eklenir. Ayrımın ölçütü
tek cümleyle söylenebilir: **olay bir alanın önceki değerini siliyorsa sıra kısıtı taşır.**

Model bu ayrımı bir durum makinesiyle kurar. Dört adım sıralıdır ve her olay bir adım numarası
taşır. Üç düzen aynı olay kümesinde koşturulur: olayları hiçbir kısıt olmadan dağıtan
**serbest** düzen, anahtar başına şerit açan **konvoy** düzeni ve olayları serbest bırakıp
işleyicide adım numarasına bakan **sürüm damgası** düzeni. Dördüncü sütun konvoyun anahtarını
gönderiden hatta kaydırır.

```js
// konvoy/olay.mjs — belirlenimli durum olayi kumesi ve uc duzenin surec ici modeli.
// Tur soyut bir adimdir; sure model parametresidir, olculmus bir sure degildir.
export const ADIM = { kabul: 1, aktarma: 2, dagitim: 3, teslim: 4 };
export const OLAY = [            // [takip no, hat, durum, sure]
  ["TR-4821", "H1", "kabul", 1], ["TR-9007", "H1", "kabul", 3],
  ["TR-4821", "H1", "aktarma", 4], ["TR-5533", "H2", "kabul", 1],
  ["TR-4821", "H1", "dagitim", 1], ["TR-9007", "H1", "aktarma", 1],
  ["TR-2214", "H2", "kabul", 2], ["TR-4821", "H1", "teslim", 1],
  ["TR-5533", "H2", "aktarma", 3], ["TR-9007", "H1", "dagitim", 1],
  ["TR-7788", "H3", "kabul", 1], ["TR-5533", "H2", "dagitim", 1],
  ["TR-2214", "H2", "aktarma", 4], ["TR-9007", "H1", "teslim", 1],
  ["TR-5533", "H2", "teslim", 1], ["TR-7788", "H3", "aktarma", 2],
  ["TR-2214", "H2", "dagitim", 1], ["TR-7788", "H3", "dagitim", 1],
  ["TR-2214", "H2", "teslim", 1], ["TR-7788", "H3", "teslim", 1],
];

// Olaylar tuketicilere dagitilir; bir tuketici bosaldiginda siradaki isi alir.
// serit: konvoy anahtarina gore serit adi, null ise sira kisiti yoktur.
function kosum(isler, tuketici, serit) {
  const bos = new Array(tuketici).fill(0);   // her tuketicinin bosalacagi tur
  const seritBos = new Map();                // her seridin bosalacagi tur
  return isler.map((o) => {
    const t = bos.indexOf(Math.min(...bos));
    const s = serit ? serit(o) : null;
    const basla = Math.max(bos[t], s === null ? 0 : (seritBos.get(s) ?? 0));
    const bitis = basla + o.sure;
    bos[t] = bitis;
    if (s !== null) seritBos.set(s, bitis);
    return { ...o, tuketici: t, bitis, serit: s };
  });
}

// Bitis sirasina gore uygulanan olaylar: son yazan kazanir ya da surum damgasi.
function uygula(bitmis, damga) {
  const durum = new Map(), s = { geriyeYazma: 0, atlanan: 0 };
  for (const o of [...bitmis].sort((a, b) => a.bitis - b.bitis || a.sira - b.sira)) {
    const simdi = durum.get(o.takipNo) ?? 0;
    if (ADIM[o.durum] < simdi) {
      s.geriyeYazma += 1;
      if (damga) { s.atlanan += 1; continue; }   // damga geriye yazmayi reddeder
    }
    durum.set(o.takipNo, ADIM[o.durum]);
  }
  const yanlis = [...durum.entries()].filter(([, a]) => a !== ADIM.teslim).map(([n]) => n);
  return { ...s, yanlisSonDurum: yanlis.length, yanlis, tur: Math.max(...bitmis.map((o) => o.bitis)) };
}

export function duzen(ad, tuketici) {
  const isler = OLAY.map(([takipNo, hat, durum, sure], sira) => ({ takipNo, hat, durum, sure, sira }));
  const SERIT = { serbest: null, konvoy: (o) => o.takipNo, "konvoy-hat": (o) => o.hat,
    "surum-damgasi": null };
  const bitmis = kosum(isler, tuketici, SERIT[ad]);
  const r = uygula(bitmis, ad === "surum-damgasi");
  const yuk = new Map();
  for (const o of bitmis) if (o.serit !== null) yuk.set(o.serit, (yuk.get(o.serit) ?? 0) + o.sure);
  return { ...r, serit: yuk.size, enYuklu: yuk.size ? Math.max(...yuk.values()) : 0,
    isYuku: OLAY.reduce((t, o) => t + o[3], 0) };
}
```

```js
// konvoy/olc.mjs — dort duzen ayni olay kumesinde: sira bozulmasi, tur ve serit yuku
import { duzen, OLAY } from "./olay.mjs";

const TUKETICI = 4;
const ALAN = [["geriyeYazma", "geriye yazma"], ["yanlisSonDurum", "yanlis son durum"],
  ["atlanan", "atlanan olay"], ["tur", "tur"], ["serit", "serit"], ["enYuklu", "en yuklu serit"]];
const D = ["serbest", "konvoy", "konvoy-hat", "surum-damgasi"].map((a) => [a, duzen(a, TUKETICI)]);

console.log(`${OLAY.length} durum olayi, ${new Set(OLAY.map((o) => o[0])).size} gonderi, ` +
  `${TUKETICI} tuketici, toplam is yuku ${D[0][1].isYuku} tur`);
console.log();
console.log(`${"olcu".padEnd(18)}${D.map(([a]) => a.padStart(15)).join("")}`);
for (const [k, ad] of ALAN)
  console.log(`${ad.padEnd(18)}${D.map(([, r]) => String(r[k]).padStart(15)).join("")}`);

const [serbest, konvoy, hat, damga] = D.map(([, r]) => r);
console.log(`\nserbest duzende yanlis son duruma dusen gonderiler = ${serbest.yanlis.join(", ")}`);
console.log(`konvoy / serbest tur orani = ${(konvoy.tur / serbest.tur).toFixed(3)}, ` +
  `hat konvoyu / serbest = ${(hat.tur / serbest.tur).toFixed(3)}`);
console.log(`etkin kosutluk = is yuku / tur: serbest ${(serbest.isYuku / serbest.tur).toFixed(2)}, ` +
  `konvoy ${(konvoy.isYuku / konvoy.tur).toFixed(2)}, hat konvoyu ${(hat.isYuku / hat.tur).toFixed(2)}`);
console.log(`damga duzeninde durum gecmisinde eksik kalan adim = ${damga.atlanan}`);

console.log(`\n${"tuketici".padEnd(10)}${["serbest", "konvoy", "konvoy-hat"].map((a) => a.padStart(12)).join("")}`);
for (const t of [1, 2, 4, 8]) {
  const satir = ["serbest", "konvoy", "konvoy-hat"].map((a) => String(duzen(a, t).tur).padStart(12));
  console.log(`${String(t).padEnd(10)}${satir.join("")}`);
}
```

```
20 durum olayi, 5 gonderi, 4 tuketici, toplam is yuku 32 tur

olcu                      serbest         konvoy     konvoy-hat  surum-damgasi
geriye yazma                    5              0              0              5
yanlis son durum                1              0              0              0
atlanan olay                    0              0              0              5
tur                             8             13             19              8
serit                           0              5              3              0
en yuklu serit                  0              8             14              0

serbest duzende yanlis son duruma dusen gonderiler = TR-4821
konvoy / serbest tur orani = 1.625, hat konvoyu / serbest = 2.375
etkin kosutluk = is yuku / tur: serbest 4.00, konvoy 2.46, hat konvoyu 1.68
damga duzeninde durum gecmisinde eksik kalan adim = 5

tuketici       serbest      konvoy  konvoy-hat
1                   32          32          32
2                   16          17          23
4                    8          13          19
8                    5           9          14
```

#### Sayıların Okunması

İlk iki satır birbirine karıştırılmaması gereken iki sayıyı ayırıyor. Serbest düzende 20
olayın 5'i kendinden ileri bir adımın üstüne yazdı, ama yalnız **bir** gönderi yanlış son
duruma düştü. Aradaki fark, geriye yazmaların çoğunun kendiliğinden kapanmasıdır: sonra gelen
doğru bir olay yanlış değeri siler. Bozuk kalan yalnız en son uygulanan geriye yazmadır.
Ölçüm bunu adıyla söylüyor: bozulan gönderi `TR-4821`. Sıra bozulmasını yalnız ihlal sayısıyla
raporlayan bir düzenek, hasarı beş kat büyük gösterir; yalnız son durumu denetleyen bir düzenek
ise sorunun beş kez yaşandığını görmez.

Konvoy sütunu iki sayıyı da sıfıra indiriyor ve bedeli tek bir yerde topluyor: tur sayısı 8'den
13'e, etkin koşutluk 4,00'ten 2,46'ya iniyor. Oran 1,625 — sıra kısıtı, koşutluğun yaklaşık
beşte ikisini geri alıyor. Bu, kalıbın ödediği doğrudan bedeldir ve kaçınılmaz değil,
**anahtara bağlıdır**.

Dördüncü sütun kısıttan kaçmanın yolunu gösteriyor. Sürüm damgalı işleyici olayları serbest
bırakır ve tur sayısı 8'de, koşutluk 4,00'te kalır; geriye yazmalar yine 5'tir ama işleyici
onları reddettiği için yanlış son durum 0'dır. Bedel yeni bir satırda görünüyor: 5 atlanan
olay. Bu gönderilerin durum geçmişinde beş adım eksik kalır — son durum doğrudur, yol
eksiktir. İki düzen aynı doğruluğu ayrı bir para birimiyle satın alıyor: konvoy verimle,
sürüm damgası geçmişle.

#### Şeridin İnceliği Tavanı Belirler

Üçüncü ve dördüncü sütunun farkı yalnız anahtardır. Konvoy anahtarı takip numarası olduğunda 5
şerit açılıyor ve en yüklü şerit 8 tur iş taşıyor; anahtar hat olduğunda şerit 3'e iniyor ve en
yüklü şerit 14 tur taşıyor. Tur sayısı 13'ten 19'a, koşutluk 2,46'dan 1,68'e düşüyor. Kaba
anahtar, ilgisiz gönderileri aynı şeride sokar: `TR-2214` numaralı gönderinin dağıtım olayı,
`TR-5533` numaralı gönderinin dört turluk aktarma olayının arkasında bekler. Aralarında hiçbir
sıra ilişkisi yoktur.

Alttaki tablo bunun neden bir kapasite sorusu olduğunu gösteriyor. Tüketici sayısı 1'den 8'e
çıktığında serbest düzen 32 turdan 5 tura iniyor. Konvoy 9 turda, hat konvoyu 14 turda
duruyor — ikisi de en yüklü şeridin yükünde. **Şerit yükü verimin tavanıdır ve tüketici
eklemek o tavanı aşmaz.** Sıralı konvoy tasarlanırken sorulacak asıl soru tüketici sayısı
değil, en yüklü şeridin ne taşıdığıdır.

#### Hesaba Dönüş

Modelin 20 olayı bir hacim sayısı değildir. Ölçülen şey orandır ve oran K01'in tepe yazma
hızına uygulanır: saniyede 97,22 durum olayı, Kabaca Büyüklük Hesabı dersinden gelen bir
**hesap**tır. Bu derse özgü tek varsayım şudur.

**KK1 — sıra kısıtı taşıyan durum olayı oranı 4/7.** Gerekçesi K01'in V4 satırında yazılıdır:
gönderi başına yedi olay "kabul, aktarma, dağıtıma çıkış, teslim ve ara durumlar" diye
gerekçelendirilmişti; dördü adlandırılmış durum geçişidir, üçü ara kayıttır. Bu sayı K01'in
varsayım tablosuna eklenmez; duyarlılığı aşağıda 5/7 ile birlikte hesaplanır.

```js
// konvoy/hesap.mjs — modelin kosutluk maliyetini K01'in tepe yazma hizina uygular
import { duzen } from "./olay.mjs";

const TEPE_YAZMA = 97.22;          // K01 Kabaca Buyukluk Hesabi, hesap sinifi
const T = 4;
const kosutluk = (a) => duzen(a, T).isYuku / duzen(a, T).tur;
const maliyet = { konvoy: kosutluk("serbest") / kosutluk("konvoy"),
  "konvoy-hat": kosutluk("serbest") / kosutluk("konvoy-hat") };

console.log(`${"KK1".padStart(5)}${"kisitli olay/s".padStart(16)}${"kisitsiz olay/s".padStart(17)}` +
  `${"konvoy x".padStart(11)}${"hat konvoyu x".padStart(15)}`);
for (const KK1 of [4 / 7, 5 / 7]) {          // KK1: sira kisiti tasiyan durum olayi orani
  const kisitli = TEPE_YAZMA * KK1, kisitsiz = TEPE_YAZMA - kisitli;
  const secici = (ad) => KK1 * maliyet[ad] + (1 - KK1);
  console.log(`${KK1.toFixed(3).padStart(5)}${kisitli.toFixed(2).padStart(16)}` +
    `${kisitsiz.toFixed(2).padStart(17)}${secici("konvoy").toFixed(3).padStart(11)}` +
    `${secici("konvoy-hat").toFixed(3).padStart(15)}`);
}
console.log(`\ntum akis konvoya sokulursa: konvoy x${maliyet.konvoy.toFixed(3)}, ` +
  `hat konvoyu x${maliyet["konvoy-hat"].toFixed(3)}`);
console.log(`serit yuku tavani (8 tuketici): konvoy ${duzen("konvoy", 8).tur} tur, ` +
  `hat konvoyu ${duzen("konvoy-hat", 8).tur} tur, serbest ${duzen("serbest", 8).tur} tur`);
```

```
  KK1  kisitli olay/s  kisitsiz olay/s   konvoy x  hat konvoyu x
0.571           55.55            41.67      1.357          1.786
0.714           69.44            27.78      1.446          1.982

tum akis konvoya sokulursa: konvoy x1.625, hat konvoyu x2.375
serit yuku tavani (8 tuketici): konvoy 9 tur, hat konvoyu 14 tur, serbest 5 tur
```

Tepe yazma hızının 55,55 olay/s'lik bölümü sıra kısıtı taşıyor, 41,67 olay/s'lik bölümü
taşımıyor. Bütün yazma akışı konvoya sokulursa aynı tamamlanma hızı için gereken tüketici
sayısı 1,625 katına çıkar. Kısıtı yalnız taşıyan olaylara uygulamak — **seçici konvoy** —
çarpanı 1,357'ye indirir; hat anahtarında 2,375 yerine 1,786 olur. KK1 5/7'ye çıkarsa çarpanlar
1,446 ve 1,982'ye yükselir: seçici konvoyun kazancı, sıra kısıtı taşımayan olayların oranı
kadardır ve o oran daralırsa kazanç da daralır.

Karar üç seçenek arasındadır ve üçü de aynı olay kümesinde ölçüldü. Sürüm damgalı işleyici
verimden hiçbir şey vermez ama durum geçmişinde boşluk bırakır; taşıyıcı geçmişi sözleşme
uyuşmazlığında kullanıldığı için bu boşluk ücretsiz değildir. Gönderi anahtarlı seçici konvoy
geçmişi eksiksiz tutar ve 1,357 çarpan öder. Hat anahtarlı konvoy hiçbir ek doğruluk vermez,
yalnız şerit sayısını azaltır ve çarpanı 1,786'ya çıkarır — kaba anahtar, karşılığı olmayan
bir maliyettir.

#### Özet

- Sıra bir tercih değil kısıttır: olay bir alanın önceki değerini siliyorsa o olayın sırası
  korunmak zorundadır; gönderi başına yedi olayın dördü bu sınıftadır.
- Geriye yazma ile yanlış son durum ayrı sayılardır: serbest düzende 5 geriye yazma oluştu ama
  yalnız 1 gönderi yanlış son duruma düştü, çünkü sonraki doğru olaylar çoğunu kapattı.
- Sıralı konvoy iki sayıyı da sıfırlar ve bedeli verimden alır: tur 8'den 13'e, etkin koşutluk
  4,00'ten 2,46'ya iner (oran 1,625).
- Sürüm damgalı işleyici koşutluğu 4,00'te tutar ve yanlış son durumu sıfırlar, ama 5 olayı
  atlar; durum geçmişinde beş adım eksik kalır.
- Şerit yükü verimin tavanıdır: sekiz tüketiciyle serbest düzen 5 tura inerken konvoy 9,
  hat konvoyu 14 turda durur — ikisi de en yüklü şeridin yükünde.
- K01'e dönüş: tepe yazmanın 55,55 olay/s'i kısıtlıdır; seçici konvoy tüketici gereksinimini
  1,357 katına çıkarır, tüm akış konvoya sokulursa 1,625, kaba anahtarla 1,786 olur.

#### Sonraki Adım

Bu dersin bütün olayları tek adımlıydı: bir durum yazılır ve iş biter. Gün sonu ücretlendirmesi
böyle değildir. Bir satıcının bir günü için tarife hesaplanır, indirim uygulanır, fatura satırı
üretilir ve bildirim gönderilir; dört adım birbirini bekler ve her adım ayrı bir servistedir.
Böyle bir işin sırası burada olduğu gibi bir anahtarla korunamaz, çünkü sorun aynı anahtara iki
yazma değil, **tek bir işin dört ayrı yerde yürümesi**dir. Sonraki ders bu işi iki ayrı biçimde
kurar — adımların birbirini olaylarla tetiklediği düzen ile tek bir yürütücünün adımları
sırayla çağırdığı düzen — ve ikisini adım sayısı, ileti sayısı, telafi edilen adım ve yarım
kalan iş cinsinden ölçer.

### Koreografi ve Orkestrasyon

Source: https://academia.sh/tr/kurslar/uygulama-katmani/koreografi-ve-orkestrasyon

Dört adımı dört servise yayılan gün sonu ücretlendirme işinin iki modelle kurulması: adımların birbirini olayla tetiklediği koreografi ile tek yürütücünün adımları çağırdığı orkestrasyon; ortak işlem sınırının kaybolduğunun gösterilmesi, telafi adımının kurulması ve iki kipin ileti, telafi edilen adım, telafisiz kalan adım ile yarım kalan iş cinsinden ölçülmesi.

Önceki dersin bütün olayları tek adımlıydı: bir durum yazılır ve iş biter. Gün sonu
ücretlendirmesi böyle değildir. Bir satıcının bir günü için tarife hesaplanır, indirim
uygulanır, fatura satırı üretilir ve bildirim gönderilir. Dört adım birbirini bekler ve her
adım ayrı bir serviste yürür — tarife ile fatura ücretlendirme bağlamında, indirim sözleşme
bağlamında, bildirim ise teslimat operasyonu tarafında.

İşi kurmanın iki modeli vardır. **Koreografi** (choreography) düzeninde ortada yürütücü yoktur:
her adım bittiğinde bir olay yayımlar, sonraki adımın servisi o olaya abonedir. **Orkestrasyon**
(orchestration) düzeninde tek bir yürütücü adımları sırayla çağırır ve işin nerede olduğunu
kendi tutar. Koreografi, Mimari Biçemler kursunda anlatılan olay güdümlü mimarinin iş akışı
hâlidir; biçemin kendisi ve yayımla–abone ol mekaniği orada ve Önbellekleme, Kuyruklar ve
Eşzamansız İşleme kursunda kuruldu, burada yeniden anlatılmaz. Ölçülen şey iki kipin aynı işi
aynı hatalarla koştururken bıraktığı sayılardır.

#### Ortak İşlem Sınırı Yok

Veri Erişim Katmanı ve İş Mantığı kursu iş birimini ve işlem sınırını kurmuştu: bir sınırın
içindeki yazmalar ya birlikte kesinleşir ya hiçbiri kesinleşmez. Dört adım dört ayrı serviste
yürüdüğünde bu dayanak kalkar. Her servis kendi sınırını kendi açar ve kapatır; üçüncü adım
başarısız olduğunda birinci ve ikinci adımın yazmaları **çoktan kesinleşmiştir** ve onları
birlikte geri alacak bir çağrı yoktur.

Yerine konan şey bir **telafi adımı**dır: her adımın etkisini iş anlamıyla geri alan ikinci bir
adım tanımlanır ve hata çıktığında tamamlananlar tersten telafi edilir. Telafi geri alma
değildir; yazma bir süre görünür kalır, sonra ters bir yazmayla kapatılır. Dördüncü adım —
bildirim — telafi edilemez, çünkü gönderilmiş bir bildirim geri alınamaz; bu yüzden sıranın
sonuna konur. Sıralamadaki bu kural iki kip için de aynıdır ve modelde `telafi` alanının `null`
olmasıyla yazılıdır.

```js
// akis/is.mjs — gun sonu ucretlendirme is akisinin surec ici modeli. Dort adim dort ayri
// serviste yurur; her servis kendi islem sinirini kendi kapatir, ortak bir sinir yoktur.
export const ADIM = [
  { ad: "tarife", servis: "ucretlendirme", telafi: "tarife-iptal" },
  { ad: "indirim", servis: "sozlesme", telafi: "indirim-iptal" },
  { ad: "fatura", servis: "faturalama", telafi: "fatura-iptal" },
  { ad: "bildirim", servis: "bildirim", telafi: null },   // geri alinamaz, bu yuzden sonda
];
export const ISLER = ["S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"];
export const HATA = { S3: "fatura", S7: "indirim" };      // belirlenimli hata noktalari

// kip: "koreografi" adimlari olayla tetikler, "orkestrasyon" tek yuruticuyle cagirir.
// eksikAbone: koreografide iptal olayina abone olmamis servis (null ise zincir tam).
export function kosum({ kip, eksikAbone = null }) {
  const s = { tamamlanan: 0, adim: 0, ileti: 0, telafiEdilen: 0, telafisizAdim: 0, yarimKalanIs: 0 };
  const kalici = new Map(ADIM.map((a) => [a.ad, new Set()]));   // kesinlesmis yazmalar
  const iz = [];

  for (const is of ISLER) {
    if (kip === "koreografi") s.ileti += 1;                     // is akisini baslatan olay
    const yapilan = [];
    let hata = null;
    for (const a of ADIM) {
      if (kip === "orkestrasyon") s.ileti += 1;                 // yurutucu -> servis komutu
      if (HATA[is] === a.ad) { hata = a.ad; s.ileti += 1; break; }   // hata yaniti ya da olayi
      s.adim += 1;
      kalici.get(a.ad).add(is);                                 // adim kendi sinirinda kesinlesti
      yapilan.push(a);
      s.ileti += 1;                    // koreografi: adimin yayimladigi olay; orkestrasyon: yanit
    }
    if (hata === null) { s.tamamlanan += 1; continue; }
    let acik = 0;
    for (const a of [...yapilan].reverse()) {
      if (a.telafi === null) continue;
      const abone = kip === "orkestrasyon" || a.servis !== eksikAbone;
      s.ileti += kip === "orkestrasyon" ? 2 : (abone ? 1 : 0);  // telafi komutu+yanit / iptal olayi
      if (abone) { kalici.get(a.ad).delete(is); s.telafiEdilen += 1; }
      else { s.telafisizAdim += 1; acik += 1; }                 // kimse dinlemedi, yazma kaldi
    }
    if (acik > 0) s.yarimKalanIs += 1;
    iz.push(`${is}: ${hata} adiminda durdu, kesinlesmis adim ${yapilan.length}, acik kalan ${acik}`);
  }
  return { ...s, iz, kalanYazma: ADIM.map((a) => `${a.ad}=${kalici.get(a.ad).size}`).join(" ") };
}

// Yapisal olculer: kip secildiginde bellidir, kosuma bagli degildir.
export function yapi(kip) {
  const n = ADIM.length, telafili = ADIM.filter((a) => a.telafi !== null).length;
  const o = kip === "orkestrasyon";
  return {
    "is akisi tanimini tasiyan birim": o ? 1 : n,
    "is akisi durumunu tutan birim": o ? 1 : 0,
    "durumu okumak icin sorulan birim": o ? 1 : n,
    "telafi sorumlulugu tasiyan birim": o ? 1 : telafili,
    "sona adim eklenince degisen birim": o ? 2 : 1,
    "islem siniri": n,
    "atomik geri alinabilen adim": 0,
  };
}
```

Üçüncü koşum bir soruyu sınar: koreografide telafi de bir olayla tetiklenir, yani her servisin
iptal olayına abone olmuş olması gerekir. `eksikAbone` bir servisin bu aboneliği kurmadığı
durumu üretir.

```js
// akis/olc.mjs — iki kip ayni is kumesinde: adim, ileti, telafi, yarim kalan is ve yapi
import { kosum, yapi, ISLER, ADIM, HATA } from "./is.mjs";

const K = [["koreografi", kosum({ kip: "koreografi" })],
  ["kor+eksik abone", kosum({ kip: "koreografi", eksikAbone: "ucretlendirme" })],
  ["orkestrasyon", kosum({ kip: "orkestrasyon" })]];
const OLCU = [["tamamlanan", "tamamlanan is"], ["adim", "yurutulen adim"], ["ileti", "ileti"],
  ["telafiEdilen", "telafi edilen adim"], ["telafisizAdim", "telafisiz kalan adim"],
  ["yarimKalanIs", "yarim kalan is"]];

console.log(`${ISLER.length} satici-gun, ${ADIM.length} adim, ` +
  `${Object.keys(HATA).length} is hata veriyor: ${Object.entries(HATA).map(([i, a]) => `${i}@${a}`).join(", ")}`);
console.log();
console.log(`${"olcu".padEnd(22)}${K.map(([a]) => a.padStart(17)).join("")}`);
for (const [k, ad] of OLCU)
  console.log(`${ad.padEnd(22)}${K.map(([, r]) => String(r[k]).padStart(17)).join("")}`);

console.log(`\nhata izi (uc kosumda da ayni):`);
for (const satir of K[2][1].iz) console.log(`  ${satir}`);
console.log(`kesinlesmis kalan yazma:`);
for (const [ad, r] of K) console.log(`  ${ad.padEnd(16)}${r.kalanYazma}`);

const YAPI = Object.keys(yapi("orkestrasyon"));
console.log(`\n${"yapisal olcu".padEnd(36)}${"koreografi".padStart(13)}${"orkestrasyon".padStart(14)}`);
for (const a of YAPI)
  console.log(`${a.padEnd(36)}${String(yapi("koreografi")[a]).padStart(13)}` +
    `${String(yapi("orkestrasyon")[a]).padStart(14)}`);

const [kor, , ork] = K.map(([, r]) => r);
console.log(`\nileti orani orkestrasyon/koreografi = ${(ork.ileti / kor.ileti).toFixed(3)}`);
console.log(`is basina ileti: koreografi ${(kor.ileti / ISLER.length).toFixed(2)}, ` +
  `orkestrasyon ${(ork.ileti / ISLER.length).toFixed(2)}`);
```

```
8 satici-gun, 4 adim, 2 is hata veriyor: S3@fatura, S7@indirim

olcu                         koreografi  kor+eksik abone     orkestrasyon
tamamlanan is                         6                6                6
yurutulen adim                       27               27               27
ileti                                40               38               64
telafi edilen adim                    3                1                3
telafisiz kalan adim                  0                2                0
yarim kalan is                        0                2                0

hata izi (uc kosumda da ayni):
  S3: fatura adiminda durdu, kesinlesmis adim 2, acik kalan 0
  S7: indirim adiminda durdu, kesinlesmis adim 1, acik kalan 0
kesinlesmis kalan yazma:
  koreografi      tarife=6 indirim=6 fatura=6 bildirim=6
  kor+eksik abone tarife=8 indirim=6 fatura=6 bildirim=6
  orkestrasyon    tarife=6 indirim=6 fatura=6 bildirim=6

yapisal olcu                           koreografi  orkestrasyon
is akisi tanimini tasiyan birim                 4             1
is akisi durumunu tutan birim                   0             1
durumu okumak icin sorulan birim                4             1
telafi sorumlulugu tasiyan birim                3             1
sona adim eklenince degisen birim               1             2
islem siniri                                    4             4
atomik geri alinabilen adim                     0             0

ileti orani orkestrasyon/koreografi = 1.600
is basina ileti: koreografi 5.00, orkestrasyon 8.00
```

#### Sayıların Okunması

İki kip aynı işi yapıyor: 8 satıcı-gününün 6'sı tamamlandı, 27 adım yürütüldü, telafi edilen
adım ikisinde de 3. İş sonucu açısından fark yok. Fark iki başka yerde toplanıyor.

Birincisi ileti sayısıdır: koreografi 40, orkestrasyon 64 — oran 1,600. İş başına 5'e karşı 8.
Sebep yapısaldır: koreografide bir adım tamamlandığında tek bir olay yayımlanır ve sonraki adım
onu kendisi alır; orkestrasyonda her adım bir komut ve bir yanıt gerektirir, telafi de öyle.
Yürütücünün işin nerede olduğunu bilmesi bedavaya gelmez; bilgiyi taşıyan iletileri kendisi
ödetir.

İkincisi üçüncü sütundadır ve asıl karar oradadır. Koreografide telafi de bir olayla tetiklenir;
ücretlendirme servisi iptal olayına abone olmadığında telafi edilen adım 3'ten 1'e iner,
telafisiz kalan adım 0'dan 2'ye çıkar ve 2 iş yarım kalır. `tarife` satırındaki kesinleşmiş
yazma 6'dan 8'e çıkıyor: iki satıcı için tarife hesabı sistemde durmaya devam ediyor, oysa
faturaları hiç üretilmedi. Bu iki yazma bir hata mesajı üretmez; yalnız ay sonunda mutabakat
tutmaz.

Kaçırılan aboneliğin orkestrasyonda karşılığı yoktur, çünkü telafi listesi yürütücünün
kodundadır. Yapısal tablo bunu adlandırıyor: telafi sorumluluğu koreografide 3 birime dağılmış,
orkestrasyonda 1 birimde toplanmış. Aynı satır düzeni iş akışı tanımı ve durumu için de
geçerli. Koreografide iş akışının tanımı hiçbir yerde bütün olarak yazılı değildir; onu okumak
için dört servisin abonelik listesine bakmak gerekir ve "bu iş nerede kaldı" sorusunu tek bir
birime soramazsınız.

Tablonun tek satırı ters yönde: sona adım eklendiğinde koreografide 1 birim değişir (yeni servis
son olaya abone olur), orkestrasyonda 2 (yeni servis ve yürütücü). Koreografinin kazancı burada
somuttur ve iletideki 1,600 çarpanıyla birlikte okunmalıdır.

#### Hesaba Dönüş

Modelin 8 işi ve 2 hatası bir hacim değildir; ölçülen şey iş başına orandır. Hacim K01'den
gelir: günde 4000 fatura satırı bir **hesap**tır ve her satır bir satıcının bir günü olduğuna
göre günde 4000 iş akışı koşar; gün sonu penceresi 4 saat K01'in V10 **varsayımı**dır.

**KK2 — gün sonu iş akışının hata oranı 0,02.** Gerekçesi eksik sözleşme kaydı, pencere
kapandıktan sonra gelen tarife değişikliği ve henüz kapanmamış gönderilerin sürekli bir taban
üretmesidir. Modelin 2/8 oranı gerçekçi değildir ve öyle sunulmaz; K01'in tablosuna da
eklenmez. Duyarlılığı 0,04 ile birlikte hesaplanır.

```js
// akis/hesap.mjs — modelin oranlarini K01'in gun sonu is hacmine uygular
import { kosum, yapi, ISLER, ADIM } from "./is.mjs";

const FATURA_SATIRI = 4000;      // K01: gunluk fatura satiri (hesap) — satici basina bir is akisi
const PENCERE = 4 * 3600;        // K01 V10: gun sonu penceresi 4 saat (varsayim)
const kor = kosum({ kip: "koreografi" }), ork = kosum({ kip: "orkestrasyon" });
const eksik = kosum({ kip: "koreografi", eksikAbone: "ucretlendirme" });
const iletiPerIs = { koreografi: kor.ileti / ISLER.length, orkestrasyon: ork.ileti / ISLER.length };
const hataliIs = 2;                                        // modeldeki hata veren is sayisi
const telafiPerHata = kor.telafiEdilen / hataliIs;
const telafisizPerHata = eksik.telafisizAdim / hataliIs;

console.log(`is akisi ${FATURA_SATIRI}/gun, pencere ${PENCERE} s -> ` +
  `${(FATURA_SATIRI / PENCERE).toFixed(4)} is/s, ${(FATURA_SATIRI * ADIM.length / PENCERE).toFixed(4)} adim/s`);
for (const [ad, n] of Object.entries(iletiPerIs))
  console.log(`  ${ad.padEnd(13)} ${n.toFixed(2)} ileti/is -> ${(FATURA_SATIRI * n / PENCERE).toFixed(4)} ileti/s`);

console.log(`\n${"KK2".padStart(5)}${"hatali is/gun".padStart(15)}${"telafi adim/gun".padStart(17)}` +
  `${"telafisiz adim/gun".padStart(20)}${"yarim kalan is/gun".padStart(20)}`);
for (const KK2 of [0.02, 0.04]) {                          // KK2: gun sonu is akisinin hata orani
  const hatali = FATURA_SATIRI * KK2;
  console.log(`${KK2.toFixed(2).padStart(5)}${hatali.toFixed(0).padStart(15)}` +
    `${(hatali * telafiPerHata).toFixed(0).padStart(17)}` +
    `${(hatali * telafisizPerHata).toFixed(0).padStart(20)}${hatali.toFixed(0).padStart(20)}`);
}
console.log(`\nmodelden gelen oranlar: hata basina telafi edilen adim = ${telafiPerHata.toFixed(2)}, ` +
  `eksik abonede telafisiz kalan = ${telafisizPerHata.toFixed(2)}`);
console.log(`K01 tepe yazma 97.22 istek/s ile karsilastirma: orkestrasyon ileti akisi ` +
  `${(100 * (FATURA_SATIRI * iletiPerIs.orkestrasyon / PENCERE) / 97.22).toFixed(2)}%`);
console.log(`is akisi tanimini okumak icin acilacak birim: koreografi ` +
  `${yapi("koreografi")["is akisi tanimini tasiyan birim"]}, orkestrasyon ` +
  `${yapi("orkestrasyon")["is akisi tanimini tasiyan birim"]}`);
```

```
is akisi 4000/gun, pencere 14400 s -> 0.2778 is/s, 1.1111 adim/s
  koreografi    5.00 ileti/is -> 1.3889 ileti/s
  orkestrasyon  8.00 ileti/is -> 2.2222 ileti/s

  KK2  hatali is/gun  telafi adim/gun  telafisiz adim/gun  yarim kalan is/gun
 0.02             80              120                  80                  80
 0.04            160              240                 160                 160

modelden gelen oranlar: hata basina telafi edilen adim = 1.50, eksik abonede telafisiz kalan = 1.00
K01 tepe yazma 97.22 istek/s ile karsilastirma: orkestrasyon ileti akisi 2.29%
is akisi tanimini okumak icin acilacak birim: koreografi 4, orkestrasyon 1
```

İleti farkı büyütüldüğünde küçülüyor. Dört saatlik pencerede iş akışı hızı saniyede 0,2778,
adım hızı 1,1111; koreografi saniyede 1,3889 ileti, orkestrasyon 2,2222 ileti üretiyor.
Orkestrasyonun fazlası saniyede 0,83 iletidir ve K01'in tepe yazma akışının yüzde 2,29'u
kadardır. İleti sayısındaki 1,600 çarpan, sistemin bütününde bir kapasite sorunu değildir.

Öteki sütun aynı büyütmede tersine davranıyor. KK2 = 0,02 ile günde 80 iş akışı hata veriyor;
model oranıyla günde 120 telafi adımı yürütülür. Telafi zincirinde tek bir eksik abonelik,
günde 80 telafisiz adım ve 80 yarım kalan iş demektir. KK2 iki katına çıkarsa 160 olur. Bu
sayılar bir kesinti üretmez, bir hata sayacı artırmaz; yalnız ay sonu mutabakatında ortaya
çıkar. Ölçülen iki maliyet arasındaki mertebe farkı kararı veriyor: koreografinin ileti kazancı
uçtaki akışın yüzde ikisi kadar, telafi zincirindeki bir boşluğun bedeli ise günde 80 yanlış
kayıttır. Adım sayısı üçü aşan ve telafi gerektiren iş akışları için orkestrasyon, telafi
sorumluluğunu tek bir birime topladığı için savunulabilir olandır; adımların birbirinden habersiz
büyümesi beklenen, telafisi olmayan bildirim türü akışlar için koreografi yerindedir.

#### Özet

- Dört adım dört serviste yürüdüğünde ortak işlem sınırı yoktur: işlem sınırı sayısı 4, atomik
  olarak geri alınabilen adım 0; hata çıktığında önceki adımlar kesinleşmiş kalır.
- Yerine telafi adımı konur; telafi edilemeyen adım (bildirim) sıralamanın sonuna alınır.
- İki kip aynı sonucu üretiyor — 6 tamamlanan iş, 27 adım, 3 telafi — ama koreografi 40,
  orkestrasyon 64 ileti harcıyor (oran 1,600; iş başına 5'e karşı 8).
- Koreografide telafi sorumluluğu 3 birime dağılır: tek bir eksik abonelik telafi edileni 3'ten
  1'e indirdi, 2 adımı telafisiz bıraktı ve `tarife` yazmalarını 6'dan 8'e çıkardı.
- Yapısal fark iş akışının tanımında toplanıyor: koreografide tanım 4 birime dağılmış ve durumu
  tutan birim 0; orkestrasyonda ikisi de 1. Ters yöndeki tek satır uzatmadır — sona adım
  eklenince koreografide 1, orkestrasyonda 2 birim değişir.
- K01'e dönüş: 4000 iş akışı ve 4 saatlik pencerede 0,2778 iş/s, 1,1111 adım/s; ileti farkı
  saniyede 0,83 (tepe yazmanın yüzde 2,29'u), buna karşılık KK2 = 0,02 ile eksik abonelik günde
  80 telafisiz adım ve 80 yarım kalan iş bırakıyor.

#### Sonraki Adım

Bu dersin bütün hataları açıkça bildirildi: bir adım hata döndürdü, telafi zinciri işledi ya da
işlemedi. Gerçek bir iş akışının çoğu arızası böyle konuşmaz. Yürütücü komutu gönderdikten
sonra çöker, adım yanıt vermeden asılı kalır, telafi çağrısı bir kez denenir ve düşer. Böyle
bir işin durumunu kimse sormazsa iş sonsuza kadar "yürüyor" görünür ve pencere kapanır. Sonraki
ders bu boşluğu kapatan yerleşimi kurar: işi kalıcı olarak kaydeden ve adımları başlatan bir
zamanlayıcı, adımı uzaktan yürüten bir aracı ve süresi dolan adımları bulup yeniden süren ya da
telafi eden bir denetçi — ve tarama sıklığının kurtarma gecikmesi ile denetçinin okuduğu kayıt
sayısı arasındaki ödünleşimi ölçer.

### Zamanlayıcı–Aracı–Denetçi

Source: https://academia.sh/tr/kurslar/uygulama-katmani/zamanlayici-araci-denetci

Yarım kalan işi kimsenin sormadığı bir iş akışının dayanıklı hâle getirilmesi: işi kalıcı kaydeden zamanlayıcı, adımı uzaktan yürüten aracı ve süresi dolan adımı bulup yeniden süren ya da telafi eden denetçi; denetçisiz ve denetçili koşumda yarım kalan iş, yeniden sürülen adım ve boşa yürütülen adım sayılarının ölçülmesi ve tarama aralığının kurtarma gecikmesiyle ödünleşmesi.

Önceki dersin bütün hataları açıkça bildirildi: bir adım hata döndürdü, telafi zinciri işledi ya
da işlemedi. Gerçek bir iş akışının çoğu arızası böyle konuşmaz. Yürütücü komutu gönderdikten
sonra çöker, adım yanıt vermeden asılı kalır, telafi çağrısı bir kez denenir ve düşer. Böyle bir
iş sonsuza kadar "yürüyor" görünür; kimse sormadığı için de kimse fark etmez.

**Zamanlayıcı–aracı–denetçi** (scheduler–agent–supervisor) bu boşluğu üç rolle kapatır.
**Zamanlayıcı**, iş akışının durumunu kalıcı bir kayda yazar ve sıradaki adımı başlatır; işin
nerede olduğu bellekte değil, süreç çöktükten sonra da okunabilen bir kayıttadır. **Aracı**,
adımı uzaktaki serviste yürütür ve sonucu kayda düşer. **Denetçi**, kaydı düzenli aralıklarla
tarar; başlatıldığı hâlde tanınan süre içinde sonucu düşmemiş adımları bulur, yeniden sürer ve
deneme eşiği aşıldığında telafiyi başlatır. Her başlatılan adıma tanınan bu süreye **kira
süresi** (lease) denir.

#### Üç Rolün Ayrılmasının Nedeni

Ayrımın gerekçesi tek cümlede yazılabilir: **hiçbir rol kendi arızasını fark edemez.** Aracı
çöktüğünde "çöktüm" diyecek bir taraf yoktur; zamanlayıcı çöktüğünde başlattığı adımların
sonucunu bekleyecek kimse kalmaz. Fark etme işi, işi yapmayan bir üçüncü tarafa verilir ve o
taraf yalnız kalıcı kayda bakar.

Model iki arıza türünü ayırır. **Asılı kalma**: aracı işi yürütmüştür ama sonucu kayda
düşememiştir — çağrı yanıtsız kalmış ya da süreç kayıt öncesinde durmuştur. **Kalıcı hata**:
adım hiçbir denemede geçmez, çünkü sözleşme kaydı eksiktir ya da tarife tanımlanmamıştır.
İkisi aynı görünür — kayıt düşmemiştir — ve denetçi ikisini ancak deneyerek ayırır.

```js
// denetci/akis.mjs — zamanlayici, araci ve denetci; tur tabanli surec ici model. Tur soyut bir
// adimdir. Asili kalma ve kalici hata parametreyle tetiklenir, olculmus sureler degildir.
export const ADIM = ["tarife", "indirim", "fatura", "bildirim"];
export const ISLER = Array.from({ length: 12 }, (_, i) => `S${i + 1}`);
export const ASILI = { S2: "indirim", S5: "tarife", S9: "fatura" };  // araci isi yapti, kayit dusmedi
export const KALICI = { S7: "indirim" };                             // adim hicbir denemede gecmez

export function kosum({ denetci = true, tarama = 2, kira = 2, denemeSiniri = 3, tur = 80 }) {
  const K = ISLER.map((is) => ({ is, adim: 0, hal: "bekliyor", basladi: 0, deneme: 0 }));
  const s = { tamamlanan: 0, adim: 0, yenidenSurulen: 0, telafiEdilen: 0, bosaYurutulen: 0,
    okunanKayit: 0, kurtarma: [], bittigiTur: 0 };

  for (let t = 1; t <= tur; t++) {
    for (const k of K) {                                   // araci: bir onceki turda baslayan adim
      if (k.hal !== "yuruyor" || t !== k.basladi + 1) continue;
      const ad = ADIM[k.adim];
      if (KALICI[k.is] === ad) continue;                   // kayit hicbir zaman dusmez
      if (ASILI[k.is] === ad && k.deneme === 0) continue;  // is yapildi, kayit dusmedi
      s.adim += 1; k.adim += 1; k.deneme = 0; k.hal = "bekliyor";
    }
    if (denetci && t % tarama === 0) {                     // denetci: suresi dolan adimlari bulur
      s.okunanKayit += K.length;
      for (const k of K) {
        if (k.hal !== "yuruyor" || t - k.basladi < kira) continue;
        s.yenidenSurulen += 1;
        s.kurtarma.push(t - (k.basladi + 1));              // beklenen kayittan bu yana gecen tur
        if (ASILI[k.is] === ADIM[k.adim]) s.bosaYurutulen += 1;   // adim ikinci kez yurutulecek
        k.deneme += 1; k.hal = "bekliyor";
      }
    }
    for (const k of K) {                                   // zamanlayici: siradaki adimi baslatir
      if (k.hal !== "bekliyor") continue;
      if (k.adim >= ADIM.length) { k.hal = "bitti"; s.tamamlanan += 1; s.bittigiTur = t; continue; }
      if (k.deneme >= denemeSiniri) {                      // esik asildi: tamamlanmislar telafi edilir
        s.telafiEdilen += k.adim; k.hal = "telafi"; s.bittigiTur = t; continue;
      }
      k.hal = "yuruyor"; k.basladi = t;
    }
    if (K.every((k) => k.hal === "bitti" || k.hal === "telafi")) break;   // butun isler durdu
  }
  const ort = s.kurtarma.length ? s.kurtarma.reduce((a, b) => a + b, 0) / s.kurtarma.length : 0;
  return { ...s, yarimKalanIs: K.filter((k) => k.hal === "yuruyor").length,
    telafiEdilenIs: K.filter((k) => k.hal === "telafi").length,
    ortKurtarma: ort, enKotuKurtarma: s.kurtarma.length ? Math.max(...s.kurtarma) : 0 };
}
```

```js
// denetci/olc.mjs — denetcisiz ve denetcili kosum, sonra tarama araliginin taranmasi
import { kosum, ISLER, ADIM, ASILI, KALICI } from "./akis.mjs";

const D = [["denetci yok", kosum({ denetci: false })], ["denetci var", kosum({ denetci: true })]];
const OLCU = [["tamamlanan", "tamamlanan is"], ["adim", "yurutulen adim"],
  ["yenidenSurulen", "yeniden surulen adim"], ["bosaYurutulen", "bosa yurutulen adim"],
  ["telafiEdilenIs", "telafi edilen is"], ["telafiEdilen", "telafi edilen adim"],
  ["yarimKalanIs", "yarim kalan is"], ["bittigiTur", "son isin bittigi tur"]];

console.log(`${ISLER.length} is, ${ADIM.length} adim; ${Object.keys(ASILI).length} adim asili kaliyor ` +
  `(${Object.entries(ASILI).map(([i, a]) => `${i}@${a}`).join(", ")}), ` +
  `${Object.keys(KALICI).length} adim kalici hatali (${Object.entries(KALICI).map(([i, a]) => `${i}@${a}`).join(", ")})`);
console.log();
console.log(`${"olcu".padEnd(24)}${D.map(([a]) => a.padStart(14)).join("")}`);
for (const [k, ad] of OLCU)
  console.log(`${ad.padEnd(24)}${D.map(([, r]) => String(r[k]).padStart(14)).join("")}`);

console.log(`\n${"tarama".padStart(7)}${"kurtarilan is".padStart(15)}${"ort kurtarma".padStart(14)}` +
  `${"en kotu".padStart(9)}${"son tur".padStart(9)}${"okunan kayit".padStart(14)}`);
for (const tarama of [1, 2, 4, 8]) {
  const r = kosum({ denetci: true, tarama });
  console.log(`${String(tarama).padStart(7)}${String(r.tamamlanan).padStart(15)}` +
    `${r.ortKurtarma.toFixed(2).padStart(14)}${String(r.enKotuKurtarma).padStart(9)}` +
    `${String(r.bittigiTur).padStart(9)}${String(r.okunanKayit).padStart(14)}`);
}
```

```
12 is, 4 adim; 3 adim asili kaliyor (S2@indirim, S5@tarife, S9@fatura), 1 adim kalici hatali (S7@indirim)

olcu                       denetci yok   denetci var
tamamlanan is                        8            11
yurutulen adim                      36            45
yeniden surulen adim                 0             6
bosa yurutulen adim                  0             3
telafi edilen is                     0             1
telafi edilen adim                   0             1
yarim kalan is                       4             0
son isin bittigi tur                 5             8

 tarama  kurtarilan is  ort kurtarma  en kotu  son tur  okunan kayit
      1             11          1.00        1        8            96
      2             11          1.33        2        8            48
      4             11          2.33        4       12            36
      8             11          5.67        7       24            36
```

#### Sayıların Okunması

Denetçisiz sütun sessiz arızanın ne demek olduğunu gösteriyor. On iki işten 8'i tamamlandı,
36 adım yürütüldü ve son iş 5. turda bitti. Görünürde her şey yolundadır: hata sayacı sıfır,
telafi sıfır, kimse bir istisna görmedi. Alt satır asıl sonucu taşıyor: **4 iş yarım kaldı** ve
sonsuza kadar "yürüyor" durumunda duruyor. Zamanlayıcı adımı başlattı, aracı yanıt vermedi ve
soruyu soracak kimse yok.

Denetçili sütunda 4 işin 3'ü kurtarıldı, biri telafi edildi ve yarım kalan iş 0'a indi.
Tamamlanan iş 8'den 11'e çıktı — 12'ye değil, çünkü `S7` işi kalıcı hatalıdır ve denetçi onu üç
kez sürdükten sonra eşiği aşmış saydı; o işin tamamlanmış tek adımı telafi edildi. Denetçi
başarısız işi başarılı yapmaz; **belirsiz durumu belirli bir duruma çevirir.** Bir iş ya biter
ya telafi edilir; "yürüyor" durumunda kalan iş sayısı sıfırdır.

İki satır bedeli gösteriyor. Yürütülen adım 36'dan 45'e çıkıyor ve bunun 3'ü **boşa
yürütülen** adımdır: aracı işi zaten yapmıştı, yalnız kaydı düşememişti. Denetçi bunu bilemez;
asılı kalmayı kalıcı hatadan ayırmanın tek yolu yeniden denemektir. Bu yüzden zamanlayıcı–aracı–
denetçi düzeninin karşılanması zorunlu bir koşulu vardır: **adımlar etkisiz (idempotent)
olmalıdır.** Aynı adımın ikinci kez yürütülmesi ek etki üretmemelidir; adım doğal olarak bu
özelliği taşımıyorsa tekillik anahtarıyla korunur. Etkisizlik ve tekillik anahtarı Web API
Tasarımı ile Veri Erişim Katmanı ve İş Mantığı kurslarında kurulmuştu; burada denetçinin
çalışabilmesinin ön koşuludur. Etkisiz olmayan bir `fatura` adımı, üç boşa yürütmede üç fazladan
fatura satırı üretir.

Alttaki tablo tarama aralığının ne satın aldığını gösteriyor. Kurtarılan iş sayısı dört
aralıkta da 11: denetçi er ya da geç bulur. Değişen, ne kadar geç bulduğudur. Ortalama kurtarma
gecikmesi 1,00 turdan 5,67 tura, en kötüsü 1'den 7'ye çıkıyor; toplu işin son işi 8. turdan 24.
tura kayıyor. Karşılığında denetçinin okuduğu kayıt 96'dan 36'ya iniyor. Aralık iki katına
çıktığında gecikme kabaca iki katına çıkar ve okuma yarıya iner; sık tarama kurtarmayı
hızlandırır, seyrek tarama kalıcı kaydın üzerindeki okuma yükünü azaltır. Bu ödünleşim
belirlenimlidir ve modelin içinden okunur; ortama bağlı bir süre ölçümü değildir.

#### Hesaba Dönüş

Modelin 12 işi bir hacim değildir. Hacim K01'den gelir: günde 4000 iş akışı ve iş başına dört
adım, yani günde 16.000 adım. Önceki dersin KK2 varsayımı (gün sonu iş akışının kalıcı hata
oranı 0,02) sürer. Bu ders bir varsayım daha ekler.

**KK3 — bir adımın asılı kalma oranı 0,005.** Gerekçesi aracı süreçlerinin yeniden başlatılması,
kayıt öncesinde kesilen çağrılar ve zaman aşımına uğrayıp yanıtı kaybolan adımların sürekli bir
taban üretmesidir. K01'in tablosuna eklenmez; duyarlılığı 0,01 ile hesaplanır.

```js
// denetci/hesap.mjs — modelin oranlarini K01'in gun sonu is hacmine uygular
import { kosum, ADIM } from "./akis.mjs";

const FATURA_SATIRI = 4000;                 // K01: gunluk fatura satiri (hesap) = gunluk is akisi
const ADIM_GUN = FATURA_SATIRI * ADIM.length;
const KK2 = 0.02;                           // onceki ders: gun sonu is akisinin kalici hata orani
const DENEME = 3;                           // denetcinin esigi (model parametresi)
const r = kosum({ denetci: true, tarama: 2 });
const bosaOran = r.bosaYurutulen / r.yenidenSurulen;   // yeniden surmelerin bosa giden payi

console.log(`gunluk is akisi ${FATURA_SATIRI}, adim/gun ${ADIM_GUN}, ` +
  `modelde bosa giden yeniden surme orani ${bosaOran.toFixed(3)}`);
console.log();
console.log(`${"KK3".padStart(6)}${"asili adim/gun".padStart(16)}${"yeniden surme/gun".padStart(19)}` +
  `${"bosa yurutulen/gun".padStart(20)}${"denetcisiz yarim is".padStart(21)}${"is orani".padStart(10)}`);
for (const KK3 of [0.005, 0.01]) {          // KK3: bir adimin asili kalma orani
  const asili = ADIM_GUN * KK3;
  const kalici = FATURA_SATIRI * KK2;
  const yarim = asili + kalici;
  console.log(`${KK3.toFixed(3).padStart(6)}${asili.toFixed(0).padStart(16)}` +
    `${(asili + kalici * DENEME).toFixed(0).padStart(19)}${asili.toFixed(0).padStart(20)}` +
    `${yarim.toFixed(0).padStart(21)}${`%${(100 * yarim / FATURA_SATIRI).toFixed(2)}`.padStart(10)}`);
}

console.log(`\ntarama araliginin bedeli (model olcumu, belirlenimli):`);
for (const tarama of [1, 8]) {
  const k = kosum({ denetci: true, tarama });
  console.log(`  tarama ${tarama}: ort kurtarma ${k.ortKurtarma.toFixed(2)} tur, ` +
    `en kotu ${k.enKotuKurtarma}, son tur ${k.bittigiTur}, okunan kayit ${k.okunanKayit}`);
}
```

```
gunluk is akisi 4000, adim/gun 16000, modelde bosa giden yeniden surme orani 0.500

   KK3  asili adim/gun  yeniden surme/gun  bosa yurutulen/gun  denetcisiz yarim is  is orani
 0.005              80                320                  80                  160     %4.00
 0.010             160                400                 160                  240     %6.00

tarama araliginin bedeli (model olcumu, belirlenimli):
  tarama 1: ort kurtarma 1.00 tur, en kotu 1, son tur 8, okunan kayit 96
  tarama 8: ort kurtarma 5.67 tur, en kotu 7, son tur 24, okunan kayit 36
```

Sayı okunduğunda karar kendini veriyor. Binde beşlik bir asılı kalma oranı günde 80 asılı adım
demektir; kalıcı hatalarla birlikte denetçisiz bir düzende günde **160 iş akışı yarım kalır** ve
bu, günlük 4000 fatura satırının yüzde 4'üdür. Oran 0,01'e çıkarsa 240 ve yüzde 6 olur. Bu
işlerin hiçbiri bir hata sayacı artırmaz; ilk belirtileri ay sonu mutabakatının tutmamasıdır.

Denetçinin ödettiği sayı aynı tabloda: günde 320 yeniden sürme ve bunların 80'i boşa yürütülen
adım. Boşa yürütmelerin oranı modelde 0,500'dür, çünkü yeniden sürmelerin yarısı kalıcı hatalı
işi tekrar denemekten gelir. Sekseni etkisizlikle karşılanır; karşılanmazsa günde 80 fazladan
fatura satırı çıkar — yani denetçi, düzeltmeyi amaçladığı hatanın tam yarısı kadar yeni hata
üretir. Zamanlayıcı–aracı–denetçi düzeni bu yüzden tek başına bir kalıp değildir; etkisiz
adımlarla birlikte tek bir karardır.

#### Özet

- Üç rol ayrılır çünkü hiçbir rol kendi arızasını fark edemez: zamanlayıcı işi kalıcı kayda
  yazıp adımı başlatır, aracı adımı yürütür, denetçi yalnız kayda bakar.
- Denetçisiz koşumda 12 işin 4'ü hiçbir hata üretmeden "yürüyor" durumunda kaldı; hata sayacı
  sıfırdı ve tamamlanan iş 8'de durdu.
- Denetçi belirsiz durumu belirliye çevirir: 3 iş kurtarıldı, 1 iş telafi edildi, yarım kalan
  iş 4'ten 0'a indi, tamamlanan iş 8'den 11'e çıktı.
- Bedel yürütülen adımdadır (36 → 45) ve 3'ü boşa yürütmedir; bu yüzden adımların etkisiz
  olması düzenin ön koşuludur.
- Tarama aralığı kurtarma gecikmesiyle okuma yükünü takas eder: aralık 1'den 8'e çıktığında
  ortalama kurtarma 1,00'den 5,67 tura, son iş 8. turdan 24. tura kayarken okunan kayıt 96'dan
  36'ya iniyor.
- K01'e dönüş: 16.000 adım/gün ve KK3 = 0,005 ile denetçisiz düzen günde 160 yarım kalan iş
  bırakır (fatura satırlarının yüzde 4'ü); denetçi bunu sıfırlar ve karşılığında günde 320
  yeniden sürme ile 80 boşa yürütme getirir.

#### Sonraki Adım

Bu dersin denetçisi tek bir taraf sayıldı. Gerçekte denetçi de, gün sonu işini başlatan
zamanlayıcı da birden çok düğümde koşar — çünkü tek düğümde koşan bir denetçi, düştüğünde
kimsenin fark etmediği bir iş akışının kendisi olur. Ama zamanlayıcının işi tekildir: gün sonu
ücretlendirmesi bir kez başlatılmalıdır. İki düğüm aynı anda başlatırsa aynı satıcı-gün için iki
fatura satırı üretilir ve bu, telafiyle değil ancak elle düzeltilir. Sonraki ders bu tekil
sorumluluğu bir düğüme veren düzeneği kurar: düğümlerden birinin kira süresi boyunca sorumluluğu
üstlenmesi, o düğüm düştüğünde devralmanın kaç turda tamamlandığı ve kira süresinin yanlış
ayarlanmasının iki düğümü aynı anda çalıştırdığı turların sayılması.

### Lider Seçimi

Source: https://academia.sh/tr/kurslar/uygulama-katmani/lider-secimi

Tekil sorumluluk gerektiren bir görevin tek düğüme verilmesi: kira süresine dayalı lider seçiminin süreç içi modelle kurulması, devralma turunun ve iki liderin aynı anda çalıştığı tur sayısının ölçülmesi, kira süresi uzadıkça lidersiz turun büyümesi, çitlemenin çift başlatmayı kaldırması, kalıcı tetiğin kaçırılan partiyi kurtarması ve sonuçların K01'in günlük fatura satırına çevrilmesi.

Önceki dersin denetçisi tek bir taraf sayıldı. Gerçekte denetçi de, gün sonu işini başlatan
zamanlayıcı da birden çok düğümde koşar — çünkü tek düğümde koşan bir denetçi, düştüğünde
kimsenin fark etmediği bir iş akışının kendisi olur. Ama zamanlayıcının işi tekildir: gün sonu
ücretlendirmesi bir kez başlatılmalıdır. İki düğüm aynı anda başlatırsa aynı satıcı-gün için iki
fatura satırı üretilir ve bu, telafiyle değil ancak elle düzeltilir.

**Lider seçimi** (leader election), bir düğüm kümesinden yalnız birine belirli bir süre için
sorumluluk veren düzenektir. Sistem Tasarımına Giriş kursunda ölçülen devralmayla akrabadır ama
sorusu farklıdır: orada devralma **hizmeti sürdürmek** için yapılıyordu — yazma kabul eden bir
kopya kalmalıydı; burada seçim **tekil sorumluluk** için yapılır — işi yapan yalnız bir taraf
olmalıdır. Birincisinin başarısızlığı kesinti, ikincisininki çift iştir.

#### Kira Süresi ve Çitleme Belirteci

Sorumluluk süresizce verilmez, çünkü sorumluluğu alan düğümün düştüğünü kimse kesin olarak
bilemez. Bunun yerine bir **kira süresi** (lease) tanınır: lider, paylaşılan kalıcı kayda
düzenli aralıklarla imzasını atar. Kayıt kira süresi kadar tazelenmemişse başka bir düğüm
sorumluluğu üstlenir ve kayıttaki **çitleme belirteci** (fencing token) adlı sayacı bir artırır.
Belirteç, bir liderliğin kaçıncı sırada verildiğini söyler; ücretlendirmenin otuz günlük
dönemiyle ilgisi yoktur.

Kira süresi düşen bir düğümü ayakta olan bir düğümden ayırt etmez; yalnız **yazamayan** bir
düğümü ayırt eder. Duraklamış, ağdan kopmuş ya da yavaşlamış bir lider hâlâ ayaktadır ve
kendini lider sanmaya devam eder. Bu yüzden asıl ölçülecek sayı devralma turu değil, **iki
düğümün aynı anda kendini lider saydığı tur sayısıdır.** Modelde 5 düğüm vardır, ilk lider
altıncı turdan itibaren beş tur boyunca ulaşılamaz kalır ve gün sonu partisi her altı turda bir
başlatılmalıdır.

```js
// lider/secim.mjs — kira suresine dayali lider secimi; tur tabanli surec ici model. Tur soyut
// bir adimdir; duraklama, kira ve yenileme aralik parametreleridir, olculmus sureler degildir.
export const TETIK = [6, 12, 18, 24];             // gun sonu partisinin baslatilmasi gereken turlar
export const BAS = 6;                             // liderin ulasilamaz kaldigi ilk tur

export function kosum({ dugum = 5, kira = 3, yenileme = 3, duraklama = 5,
  citleme = false, kaliciTetik = false, tur = 24 }) {
  const bit = BAS + duraklama - 1;
  const kayit = { lider: 0, sonYenileme: 0, belirtec: 1 };     // paylasilan kalici kayit
  const inanan = new Array(dugum).fill(false); inanan[0] = true;
  const belirtec = new Array(dugum).fill(0); belirtec[0] = 1;
  const s = { baslatilan: 0, kacirilan: 0, ciftBaslatma: 0, reddedilen: 0, gecikmeliTur: 0,
    ikiLiderliTur: 0, lidersizTur: 0, devralmaTuru: 0, devralma: 0 };
  let bekleyen = 0, bekleyenTur = 0;
  const ulasilir = (d, t) => d !== 0 || t < BAS || t > bit;

  for (let t = 1; t <= tur; t++) {
    if (ulasilir(kayit.lider, t)) kayit.sonYenileme = t;       // lider kirasini yeniliyor
    if (t - kayit.sonYenileme >= kira) {                       // kira doldu: bir dugum ustleniyor
      const yeni = [...Array(dugum).keys()].find((d) => d !== kayit.lider && ulasilir(d, t));
      s.devralmaTuru += t - kayit.sonYenileme; s.devralma += 1;
      kayit.lider = yeni; kayit.sonYenileme = t; kayit.belirtec += 1;
      inanan[yeni] = true; belirtec[yeni] = kayit.belirtec;
    }
    const calisan = [...Array(dugum).keys()].filter((d) => inanan[d] && ulasilir(d, t));
    if (calisan.length > 1) s.ikiLiderliTur += 1;
    if (calisan.length === 0) s.lidersizTur += 1;
    let kabul = 0;
    for (const d of calisan) {
      if (citleme && belirtec[d] < kayit.belirtec) { s.reddedilen += 1; continue; } // eski belirtec
      kabul += 1;
    }
    if (TETIK.includes(t)) {                                   // gun sonu partisi baslatilmali
      if (kabul === 0) { if (kaliciTetik) { bekleyen += 1; bekleyenTur = t; } else s.kacirilan += 1; }
      else { s.baslatilan += 1; s.ciftBaslatma += kabul - 1; }
    } else if (bekleyen > 0 && kabul > 0) {                    // kalici tetik: yeni lider devraliyor
      s.baslatilan += 1; s.gecikmeliTur += t - bekleyenTur; bekleyen = 0;
    }
    if (t % yenileme === 0) for (const d of calisan)            // dugum kaydi okur, gerekirse birakir
      if (kayit.lider !== d) inanan[d] = false;
  }
  return s;
}
```

Modelin iki eklenti seçeneği var. **Çitleme**, her işin bu belirteçle birlikte yapılmasıdır:
kayıt, belirteci geride kalmış bir düğümün işini reddeder. Çitleme İlişkisel Veritabanı Yönetimi
kursunda kurulmuştu; burada tekil sorumluluğa uygulanır. **Kalıcı tetik**, lidersiz bir turda
gelen başlatma isteğinin bir kayda düşürülmesi ve sonraki liderin onu üstlenmesidir.

```js
// lider/olc.mjs — citlemesiz ve citlemeli kosum, sonra kira suresinin taranmasi
import { kosum, TETIK, BAS } from "./secim.mjs";

const D = [["yalin", kosum({})], ["citleme", kosum({ citleme: true })],
  ["citleme+kalici tetik", kosum({ citleme: true, kaliciTetik: true })]];
const OLCU = [["baslatilan", "baslatilan parti"], ["kacirilan", "kacirilan parti"],
  ["gecikmeliTur", "gecikmeli baslama turu"], ["ciftBaslatma", "cift baslatilan parti"],
  ["reddedilen", "citlemeyle reddedilen"], ["ikiLiderliTur", "iki liderli tur"],
  ["lidersizTur", "lidersiz tur"], ["devralmaTuru", "devralma turu"]];

console.log(`5 dugum, 24 tur, kira 3, yenileme 3; lider dugum ${BAS}-${BAS + 4}. turlarda ` +
  `ulasilamaz. Gun sonu partisi tetigi: ${TETIK.join(", ")}. turlar`);
console.log();
console.log(`${"olcu".padEnd(24)}${D.map(([a]) => a.padStart(22)).join("")}`);
for (const [k, ad] of OLCU)
  console.log(`${ad.padEnd(24)}${D.map(([, r]) => String(r[k]).padStart(22)).join("")}`);

const S = ["devralmaTuru", "lidersizTur", "kacirilan", "ikiLiderliTur", "ciftBaslatma"];
const bas = ["devr.turu", "lidersiz", "kacirilan", "iki lider", "cift"];
console.log(`\n${"".padStart(5)}${"uzun duraklama (5 tur)".padStart(50)}${"kisa duraklama (2 tur)".padStart(50)}`);
console.log(`${"kira".padStart(5)}${bas.map((a) => a.padStart(10)).join("").repeat(2)}`);
for (const kira of [2, 3, 5, 8]) {
  const satir = [5, 2].flatMap((duraklama) => {
    const r = kosum({ kira, duraklama, citleme: false });
    return S.map((k) => String(r[k]).padStart(10));
  });
  console.log(`${String(kira).padStart(5)}${satir.join("")}`);
}
```

```
5 dugum, 24 tur, kira 3, yenileme 3; lider dugum 6-10. turlarda ulasilamaz. Gun sonu partisi tetigi: 6, 12, 18, 24. turlar

olcu                                     yalin               citleme  citleme+kalici tetik
baslatilan parti                             3                     3                     4
kacirilan parti                              1                     1                     0
gecikmeli baslama turu                       0                     0                     2
cift baslatilan parti                        1                     0                     0
citlemeyle reddedilen                        0                     2                     2
iki liderli tur                              2                     2                     2
lidersiz tur                                 2                     2                     2
devralma turu                                3                     3                     3

                                 uzun duraklama (5 tur)                            kisa duraklama (2 tur)
 kira devr.turu  lidersiz kacirilan iki lider      cift devr.turu  lidersiz kacirilan iki lider      cift
    2         2         1         1         2         1         2         1         1         2         0
    3         3         2         1         2         1         0         2         1         0         0
    5         5         4         1         2         1         0         2         1         0         0
    8         0         5         1         0         0         0         2         1         0         0
```

#### Sayıların Okunması

Yalın koşum dört tetikten yalnız üçünü başlatabildi. Altıncı turda lider ulaşılamaz duruma
geldi ve kira henüz dolmadığı için kimse yerine geçmedi: **parti hiç başlamadı.** Devralma
üçüncü turda tamamlandı ve iki tur lidersiz geçti. On birinci turda eski lider geri döndü ve
hâlâ kendini lider sanıyordu; on ikinci turdaki tetiği o da, yeni lider de gördü ve parti iki
kez başlatıldı. Yalın düzenin bilançosu şudur: bir parti kaçtı, bir parti iki kez koştu, biri
de doğru koştu.

Üç satır birbirinden ayrı tutulmalı. `iki liderli tur` iki düğümün **kendini lider saydığı**
tur sayısıdır ve üç sütunda da 2'dir; çitleme bu sayıyı değiştirmez, çünkü çitleme bir düğümün
inancını düzeltmez. Değişen `cift baslatilan parti` satırıdır: 1'den 0'a iniyor ve karşılığında
`citlemeyle reddedilen` 0'dan 2'ye çıkıyor. Kayıt, belirteci geride kalmış düğümün işini reddetti.
İnanç ile etki arasındaki bu ayrım kalıbın çekirdeğidir: **lider seçimi kimin lider olduğunu
belirler, çitleme kimin yazabileceğini belirler.** İkincisi olmadan birincisi yeterli değildir.

Üçüncü sütun ikinci hatayı kapatıyor. Kalıcı tetikle kaçırılan parti 0'a iniyor, başlatılan
parti 3'ten 4'e çıkıyor ve bedel iki turluk bir gecikme oluyor. Tetiğin anlık olması onu bir
zamana bağlar; kayda düşülmesi bir işe çevirir. Bu, önceki dersin zamanlayıcısının aynı
kuralıdır: bellekte duran bir niyet arızayı geçemez.

Alttaki tablo kira süresinin ne satın aldığını gösteriyor. Kira 2'den 5'e çıktıkça devralma
turu 2'den 5'e, lidersiz tur 1'den 4'e çıkıyor; kira 8 olduğunda devralma hiç olmuyor ve
duraklama boyunca 5 tur lidersiz geçiyor. Buna karşılık `iki lider` sütunu kira 2, 3 ve 5'te
aynı — 2. **Uzun kira iki liderli pencereyi daraltmıyor**, çünkü o pencere kira süresinden
değil, geri dönen düğümün kaydı ne zaman yeniden okuduğundan doğuyor. Kira 8'de sütunun 0
olması bir kazanç değil: orada hiç devralma olmadığı için ikinci lider hiç ortaya çıkmadı ve
sistem beş tur boyunca sorumsuz kaldı.

Sağdaki blok kısa duraklamayı gösteriyor: iki turluk bir yavaşlamada kira 3 ve üstü hiç devralma
üretmiyor, kira 2 ise gereksiz bir devralma ve iki liderli iki tur üretiyor. Kısa kira geçici
yavaşlamayı arıza sanar; uzun kira gerçek arızayı geç fark eder. Ayarın doğru yeri bu ikisinin
arasındadır ve kararın ölçüsü lidersiz turla iki liderli turun toplamıdır.

#### Hesaba Dönüş

Modelin turları K01'in sayılarına iki adımda bağlanır. Modelin verdiği şey **pencere payıdır**:
bir devralmanın olduğu koşumda turların 2/24'ü lidersiz, 2/24'ü iki liderli geçti. Tetiğin bu
pencerelere düşme oranı, tetiğin turlar arasında düzgün dağıldığı okumasıyla aynı paydır.

**KK4 — yılda 6 devralma.** Bu varsayım yeni değildir: Sistem Tasarımına Giriş kursunun
Erişilebilirlik Kalıpları dersinde devralma penceresi isteğe çevrilirken aynı sayı kullanılmıştı
ve burada aynı gerekçeyle sürdürülür. K01'in varsayım tablosuna eklenmez; duyarlılığı 12 ile
hesaplanır. Bir gün sonu partisi K01'in hesabına göre 4000 fatura satırı üretir.

```js
// lider/hesap.mjs — modelin pencere oranlarini K01'in gun sonu partisine cevirir
import { kosum } from "./secim.mjs";

const FATURA_SATIRI = 4000;          // K01: gunluk fatura satiri (hesap) = bir gun sonu partisi
const TUR = 24;
const y = kosum({}), c = kosum({ citleme: true, kaliciTetik: true });
const lidersizPay = y.lidersizTur / TUR, ikiLiderliPay = y.ikiLiderliTur / TUR;

console.log(`model penceresi: lidersiz ${y.lidersizTur}/${TUR} = ${lidersizPay.toFixed(4)}, ` +
  `iki liderli ${y.ikiLiderliTur}/${TUR} = ${ikiLiderliPay.toFixed(4)}`);
console.log(`bir parti = ${FATURA_SATIRI} fatura satiri (K01, hesap)`);
console.log();
console.log(`${"KK4".padStart(5)}${"kacirilan parti/yil".padStart(21)}${"uretilmeyen satir".padStart(19)}` +
  `${"cift parti/yil".padStart(16)}${"cift satir".padStart(12)}`);
for (const KK4 of [6, 12]) {         // KK4: yilda kac devralma (M19/K01 Erisilebilirlik Kaliplari)
  const kacirilan = KK4 * lidersizPay, cift = KK4 * ikiLiderliPay;
  console.log(`${String(KK4).padStart(5)}${kacirilan.toFixed(2).padStart(21)}` +
    `${(kacirilan * FATURA_SATIRI).toFixed(0).padStart(19)}${cift.toFixed(2).padStart(16)}` +
    `${(cift * FATURA_SATIRI).toFixed(0).padStart(12)}`);
}
console.log(`\ncitleme + kalici tetik ile: kacirilan parti ${c.kacirilan}, ` +
  `cift baslatilan parti ${c.ciftBaslatma}, gecikmeli baslama ${c.gecikmeliTur} tur`);
console.log(`kira 8 (uzun) ile: devralma turu ${kosum({ kira: 8 }).devralmaTuru}, ` +
  `lidersiz tur ${kosum({ kira: 8 }).lidersizTur}, iki liderli tur ${kosum({ kira: 8 }).ikiLiderliTur}`);
```

```
model penceresi: lidersiz 2/24 = 0.0833, iki liderli 2/24 = 0.0833
bir parti = 4000 fatura satiri (K01, hesap)

  KK4  kacirilan parti/yil  uretilmeyen satir  cift parti/yil  cift satir
    6                 0.50               2000            0.50        2000
   12                 1.00               4000            1.00        4000

citleme + kalici tetik ile: kacirilan parti 0, cift baslatilan parti 0, gecikmeli baslama 2 tur
kira 8 (uzun) ile: devralma turu 0, lidersiz tur 5, iki liderli tur 0
```

Yılda altı devralmada beklenen kaçırılan parti 0,50 ve beklenen çift parti 0,50'dir. Sayıyı
fatura satırına çevirdiğinizde iki yılda bir 4000 satırlık bir gün hiç üretilmez ve iki yılda
bir 4000 satır iki kez üretilir; yıllık beklenti her ikisinde de 2000 satırdır. Devralma sayısı
ikiye katlanırsa yılda birer olay ve 4000'er satır olur.

Bu sayıların büyüklüğü kararın nereye bağlanacağını söylüyor. Hizmet erişilebilirliği
cinsinden bakıldığında iki tur lidersizlik hiçbir şeydir; K01'in kesinti bütçesinde görünmez.
Fatura satırı cinsinden bakıldığında aynı iki tur, yılda 2000 çift satır demektir ve her biri
bir satıcıya yanlış tutar yansıtır. Tekil sorumluluk gerektiren işlerde ölçünün birimi süre
değil, **iş**tir. Çitleme çift satırı sıfıra indirir, kalıcı tetik üretilmeyen satırı sıfıra
indirir; ikisi birlikte iki turluk bir gecikmeye mal olur. Lider seçimi tek başına bu iki
hatanın hiçbirini kaldırmaz.

#### Özet

- Lider seçimi ile hizmeti sürdüren devralma ayrı sorulardır: birinde amaç yazma kabul eden bir
  kopyanın kalması, ötekinde işin yalnız bir kez yapılmasıdır.
- Kira süresi düşen düğümü değil yazamayan düğümü ayırt eder; duraklamış lider geri döndüğünde
  kendini hâlâ lider sanar.
- Yalın koşumda 4 tetikten 3'ü başlatıldı: 1 parti kaçtı (lidersiz turda geldi), 1 parti iki kez
  başlatıldı (iki liderli turda geldi), devralma 3 turda tamamlandı.
- Çitleme inancı değil etkiyi düzeltir: iki liderli tur 2'de kalırken çift başlatılan parti
  1'den 0'a indi, reddedilen iş 0'dan 2'ye çıktı. Kalıcı tetik kaçırılan partiyi 0'a indirdi ve
  iki turluk gecikme getirdi.
- Kira uzadıkça devralma 2'den 5 tura, lidersiz tur 1'den 4'e çıkıyor; iki liderli pencere ise
  değişmiyor, çünkü kaynağı kira değil geri dönen düğümün kaydı yeniden okuma anıdır.
- K01'e dönüş: KK4 = yılda 6 devralma ile beklenen kaçırılan parti 0,50 ve çift parti 0,50, yani
  yılda 2000 üretilmeyen ve 2000 çift fatura satırı; devralma 12'ye çıkarsa 4000'er olur.

#### Sonraki Adım

Bu kursun bütün derslerinde iş, isteği yapan taraftan koparılmış olarak yürüdü: durum olayı
kuyruğa yazıldı, iş akışı arkada koştu, gün sonu partisi bir lider tarafından başlatıldı. Bir
soru hep dışarıda kaldı — **isteyen tarafın sonucu nasıl öğrendiği.** Bir satıcı otuz günlük
ücretlendirme raporunu istediğinde, iş kuyruğa alınır ve saniyeler sonra biter; ama satıcının
tarayıcısı bir yanıt bekliyordur ve bağlantı o kadar açık tutulamaz. Sonraki ders bu boşluğu
kapatan yerleşimi kurar: isteğin hemen bir iş kimliğiyle karşılanması, sonucun ayrı bir adresten
alınması ve istemcinin sonucu görene kadar kaç kez sorduğunun ölçülmesi.

### Eşzamansız İstek–Yanıt

Source: https://academia.sh/tr/kurslar/uygulama-katmani/eszamansiz-istek-yanit

Uzun süren bir işlemde yanıtın nasıl döndürüleceği: 3,60 saniyelik dönem raporunun 200 milisaniyelik eşiğe sığmaması, isteğin bir iş kimliğiyle hemen karşılanması, durum ve sonuç adreslerinin ayrılması, yoklama aralığının yoklama sayısı ile sonucu görme süresi arasındaki ödünleşiminin ölçülmesi ve üstel geri çekilmeli yoklamanın on beş yoklamayı dörde indirmesi.

Bu kursun bütün derslerinde iş, isteği yapan taraftan koparılmış olarak yürüdü: durum olayı
kuyruğa yazıldı, iş akışı arkada koştu, gün sonu partisi bir lider tarafından başlatıldı. Bir
soru hep dışarıda kaldı — isteyen tarafın sonucu nasıl öğrendiği. Bir satıcı otuz günlük
ücretlendirme raporunu istediğinde iş kuyruğa alınır ve saniyeler sonra biter; ama satıcının
istemcisi bir yanıt beklemektedir.

**Eşzamansız istek–yanıt** (asynchronous request–reply), isteği hemen bir iş kimliğiyle
karşılayan ve sonucu ayrı bir adresten sunan yerleşimdir. Bu ders o yerleşimin bedelini ölçer:
istemci sonucu görene kadar kaç kez sorar, ne kadar geç görür ve bu sorular uçtaki tepe istek
hızına ne ekler.

#### Eşzamanlı Yol Neden Yetmiyor

Servis Tasarımı konusunun son dersi uçtan uca eşiği bir bütçe olarak ele almıştı: takip
okumasının sıkı eşiği 200 milisaniyedir ve zincirdeki her adım bu bütçenin bir payını alır.
Dönem raporunun süresi de bu kursun ikinci dersinde K01'den türetilmişti: satıcı başına 3000
kayıt, tarama hızı 833,33 kayıt/s, yani **3,60 saniye**.

İki sayı yan yana konduğunda karar aritmetiktir: rapor işi eşiğin 18 katıdır. Bütçeyi
bölüştürmek de işe yaramaz, çünkü bölüştürme her adımı **daraltır**; adımın kendisi bütçenin
tamamından büyükse bölünecek bir şey yoktur. Zaman aşımı ile deneme sayısının çarpımı burada
üçüncü bir seçenek üretmez. Üçüncü seçenek çağıranın beklememesidir.

#### Üç Adres

Yerleşim, tek bir çağrının yaptığı işi üç adrese böler ve her adres tek bir soruya yanıt verir.
**İstek adresi** raporu ister ve hemen döner; yanıt sonucu değil, işin kabul edildiğini ve bir
iş kimliğini taşır. **Durum adresi** o kimliğin işinin bitip bitmediğini söyler. **Sonuç
adresi** biten işin çıktısını verir.

Bölmenin üç sonucu vardır. Birincisi, istek adresi artık kısa bir yazma olduğu için 200
milisaniyelik eşiğe girer; durum adresi de tek kayıt okumasıdır ve aynı eşiğe girer. İkincisi,
iş kimliği yazıldıktan sonra istemci bağlantıyı kapatabilir ve sonucu daha sonra alabilir;
istemcinin çökmesi işi düşürmez. Üçüncüsü, aynı isteğin ikinci kez gönderilmesi ikinci bir iş
başlatmamalıdır — istek adresi etkisiz olmalı ve aynı tekillik anahtarı aynı iş kimliğini
döndürmelidir. Bu üç sonuç yerleşimin kendisidir; ölçülecek olan, istemcinin durum adresini
kaç kez sorduğudur.

```js
// eszamansiz/yoklama.mjs — uzun suren rapor isteginde yoklama sayisi ve gorulme suresi.
// Sureler K01'den turetilmis hesaplardir; kosum aritmetiktir, ortama bagli bir olcum degildir.
export const IS_SURESI = 3.6;      // K01: 3000 kayit / 833,33 kayit/s (Gorev Kuyruklari dersi)
export const ESIK = 0.2;           // K01: takip okumasinin siki esigi 200 ms

// Istemci istegi birakir, sonra durum adresini yoklar. Ustel geri cekilmede aralik tavana kadar
// ikiye katlanir; algoritma M14/K06'da kurulu, burada yalnizca yoklama sayisina uygulaniyor.
export function yokla({ aralik, ustel = false, tavan = 2, sure = IS_SURESI }) {
  let t = 0, p = aralik, yoklama = 0, bosa = 0;
  for (;;) {
    t += p; yoklama += 1;
    if (t >= sure) break;
    bosa += 1;
    if (ustel) p = Math.min(p * 2, tavan);
  }
  return { yoklama, bosa, gorme: t, fazla: t - sure,
    istek: 1 + yoklama, bosaOran: bosa / yoklama };
}
```

```js
// eszamansiz/olc.mjs — yoklama araligi taranir, sonra K01'in hacmine ve esigine baglanir
import { yokla, IS_SURESI, ESIK } from "./yoklama.mjs";

const S = [...[0.25, 0.5, 1, 2].map((a) => [`sabit ${a} s`, yokla({ aralik: a })]),
  ["ustel 0.25 s (tavan 2 s)", yokla({ aralik: 0.25, ustel: true })]];

console.log(`is suresi ${IS_SURESI.toFixed(2)} s (K01 hesabi), uctan uca esik ${ESIK * 1000} ms (K01)`);
console.log(`eszamanli tutulsaydi: 1 istek, baglanti ${IS_SURESI.toFixed(2)} s acik, ` +
  `esigin ${(IS_SURESI / ESIK).toFixed(0)} kati`);
console.log();
console.log(`${"strateji".padEnd(26)}${"yoklama".padStart(9)}${"bosa".padStart(7)}` +
  `${"bosa orani".padStart(12)}${"gorme s".padStart(10)}${"fazla s".padStart(10)}${"istek".padStart(7)}`);
for (const [ad, r] of S)
  console.log(`${ad.padEnd(26)}${String(r.yoklama).padStart(9)}${String(r.bosa).padStart(7)}` +
    `${r.bosaOran.toFixed(3).padStart(12)}${r.gorme.toFixed(2).padStart(10)}` +
    `${r.fazla.toFixed(2).padStart(10)}${String(r.istek).padStart(7)}`);

// K01: gunluk fatura satiri 4000 = satici sayisi (hesap); tepe carpani V8 = 3 (varsayim);
// uctaki tepe 513,89 istek/s (hesap).
const SATICI = 4000, V8 = 3, UC_TEPE = 513.89, GUN = 86_400;
console.log(`\n${"KK5".padStart(5)}${"rapor/gun".padStart(11)}${"strateji".padStart(27)}` +
  `${"istek/gun".padStart(11)}${"tepe istek/s".padStart(14)}${"uctaki payi".padStart(13)}`);
for (const KK5 of [0.25, 0.5]) {          // KK5: satici basina gunluk donem raporu istegi
  const rapor = SATICI * KK5;
  for (const [ad, r] of [S[0], S[4]]) {
    const tepe = (rapor * r.istek / GUN) * V8;
    console.log(`${KK5.toFixed(2).padStart(5)}${rapor.toFixed(0).padStart(11)}${ad.padStart(27)}` +
      `${(rapor * r.istek).toFixed(0).padStart(11)}${tepe.toFixed(4).padStart(14)}` +
      `${`%${(100 * tepe / UC_TEPE).toFixed(3)}`.padStart(13)}`);
  }
}
const esz = (rapor) => ((rapor / GUN) * V8 * IS_SURESI);
console.log(`\neszamanli tutulsaydi tepede acik baglanti: KK5 = 0,25 -> ` +
  `${esz(SATICI * 0.25).toFixed(3)}, KK5 = 0,50 -> ${esz(SATICI * 0.5).toFixed(3)}`);
```

```
is suresi 3.60 s (K01 hesabi), uctan uca esik 200 ms (K01)
eszamanli tutulsaydi: 1 istek, baglanti 3.60 s acik, esigin 18 kati

strateji                    yoklama   bosa  bosa orani   gorme s   fazla s  istek
sabit 0.25 s                     15     14       0.933      3.75      0.15     16
sabit 0.5 s                       8      7       0.875      4.00      0.40      9
sabit 1 s                         4      3       0.750      4.00      0.40      5
sabit 2 s                         2      1       0.500      4.00      0.40      3
ustel 0.25 s (tavan 2 s)          4      3       0.750      3.75      0.15      5

  KK5  rapor/gun                   strateji  istek/gun  tepe istek/s  uctaki payi
 0.25       1000               sabit 0.25 s      16000        0.5556       %0.108
 0.25       1000   ustel 0.25 s (tavan 2 s)       5000        0.1736       %0.034
 0.50       2000               sabit 0.25 s      32000        1.1111       %0.216
 0.50       2000   ustel 0.25 s (tavan 2 s)      10000        0.3472       %0.068

eszamanli tutulsaydi tepede acik baglanti: KK5 = 0,25 -> 0.125, KK5 = 0,50 -> 0.250
```

#### Sayıların Okunması

Sabit aralıklı dört satır bir ödünleşimi gösteriyor. Aralık dört kat büyüdüğünde (0,25 → 1
saniye) yoklama sayısı 15'ten 4'e iniyor; karşılığında sonucu görme süresi 3,75'ten 4,00
saniyeye çıkıyor. Fazla bekleme 0,15 saniyeden 0,40 saniyeye, yani 2,67 katına çıkıyor.
Ödünleşim asimetriktir: dört kat daha az soru, yalnız 0,25 saniye daha geç sonuç.

Boşa yoklama oranı asıl israfı adlandırıyor. Çeyrek saniyelik aralıkta 15 yoklamanın 14'ü
"hâlâ yürüyor" yanıtı alıyor: oran 0,933. İki saniyelik aralıkta oran 0,500'e iniyor. Sabit
aralık, işin ne kadar süreceğini bilmediği için işin başında da sonunda da aynı sıklıkla sorar;
oysa bir işin biteceğine dair bilgi zamanla artar.

Son satır bunu kullanıyor. Üstel geri çekilmeli yoklama çeyrek saniyeyle başlar, aralığı iki
saniyelik tavana kadar iki katına çıkarır ve **4 yoklamayla 3,75 saniyede** sonucu görür — yani
sabit iki saniyelik aralığın yoklama sayısına yakın, sabit çeyrek saniyelik aralığın görme
süresiyle aynı. Fazla bekleme 0,15 saniyede kalıyor. Sabit aralıklı satırların hiçbiri bu iki
sayıyı birlikte veremiyor. Geri çekilme algoritması Dayanıklılık ve Güvenilirlik kursunda
kurulmuştu; burada yeniden denemeye değil, yoklamaya uygulanıyor ve aynı biçimde davranıyor.

#### Hesaba Dönüş

Yoklama isteklerinin uçtaki tepe hıza ne eklediğini görmek için rapor hacmi gerekir. K01
günde 4000 fatura satırı hesaplamıştı ve her satır bir satıcının bir günü olduğuna göre satıcı
sayısı 4000'dir. Rapor isteği sıklığı K01'de yok.

**KK5 — satıcı başına günlük dönem raporu isteği 0,25.** Gerekçesi satıcıların dönem raporunu
her gün değil, haftada bir–iki kez istemesidir. K01'in tablosuna eklenmez; duyarlılığı 0,50
ile hesaplanır. Tepe çarpanı K01'in V8 varsayımıdır (3).

Sonuç iki yönlü okunmalı. Çeyrek saniyelik sabit yoklamada günde 16.000 istek çıkıyor ve tepede
0,5556 istek/s ediyor — K01'in uçtaki 513,89 istek/s'sinin yüzde 0,108'i. Üstel geri çekilmede
5000 istek, 0,1736 istek/s ve yüzde 0,034. KK5 iki katına çıkarsa oranlar yüzde 0,216 ve 0,068
olur. Yani **yoklamanın uçtaki maliyeti her durumda binde birkaçtır.**

Eşzamanlı alternatifin maliyeti de büyük değil: tepede aynı anda açık kalan bağlantı 0,125
(KK5 = 0,50'de 0,250). İki sayı birlikte okunduğunda kalıbın gerekçesi netleşir: eşzamansız
istek–yanıt bir **kaynak** kararı değil, bir **bütçe** kararıdır. Kaynak açısından iki yol da
ucuzdur; ayrım, 3,60 saniyelik bir işin 200 milisaniyelik eşiğin altına hiçbir zaman
inememesidir. Eşiği olan bir sistemde eşiğe sığmayan işin tek çıkışı, isteği yanıtla
tamamlamamaktır.

#### Özet

- 3,60 saniyelik dönem raporu K01'in 200 milisaniyelik uçtan uca eşiğinin 18 katıdır; bütçeyi
  bölüştürmek adımı daraltır, büyütmez.
- Yerleşim üç adres kurar — istek, durum, sonuç; istek adresi kısa bir yazmadır ve eşiğe girer,
  aynı tekillik anahtarıyla gelen ikinci istek yeni iş başlatmaz.
- Sabit aralıkta yoklama sayısı ile görme süresi asimetrik takas edilir: aralık 0,25'ten 1
  saniyeye çıkınca yoklama 15'ten 4'e, fazla bekleme 0,15'ten 0,40 saniyeye gidiyor.
- Boşa yoklama oranı sabit çeyrek saniyede 0,933, iki saniyede 0,500; sabit aralık işin bitme
  olasılığının zamanla arttığını kullanmaz.
- Üstel geri çekilmeli yoklama iki sayıyı birlikte veriyor: 4 yoklama ve 3,75 saniyede görme,
  fazla bekleme 0,15 saniye.
- K01'e dönüş: KK5 = 0,25 ile yoklama tepede 0,5556 istek/s (uçtaki hızın yüzde 0,108'i), üstel
  geri çekilmede 0,1736 istek/s (yüzde 0,034); eşzamanlı tutulsaydı tepede 0,125 açık bağlantı.
  Kalıp bir kaynak kararı değil, bir bütçe kararıdır.

#### Kurs Kapanışı

Kurs, isteğin uygulamaya ulaştıktan sonrasını iki konuda tasarladı ve her derste aynı üç soruyu
sordu: bu karar nedir, giriş kurslarının hesabındaki hangi sayıyı oynatıyor, karşılığında hangi
sayı büyüyor. Derslerin bıraktığı ölçüler aşağıdaki tabloda toplanıyor.

| Ders | Kararı | Oynadığı sayı | Karşılığında büyüyen |
|---|---|---|---|
| Durumsuz Servisler | durumun servis dışına taşınması | gereken kopya 4 → 3; kopya başına 200,93 → 171,30 istek/s | durum deposuna 1027,78 işlem/s, K01'in depo yükünün 7,40 katı |
| Servis Keşfi | adresin çalışma zamanında bulunması | arıza başına düşen istek 102.778 → 510,46 | liste penceresi 2980 ms; defterin kendi yükü 3,00 atış/s |
| İletişim Biçimleri | iç çağrının biçimi | iç bayt 352 / 181 / 206; iç istek 833,34 istek/s | çağıranın bildiği ad 6 → 8; iç sınır 1,60 Mbit/s'in 0,38–0,73 katı |
| Servisler Arası Sözleşme | şemanın evrilme sırası | kırılan tüketici 4/5 → 0 | kayıt 228 → 406 bayt; iç sınır 0,760 → 1,353 Mbit/s |
| Zaman Aşımı ve Deneme Bütçesi | bütçenin zincire bölüşümü | en kötü süre bütçenin 6,1 katından 200 ms'ye | iç istek 1666,68 → 1895,85 istek/s; yaygın yavaşlamada 3333,36 |
| Mesaj Kuyrukları | yükün zamana yayılması | işleme kapasitesi 97,22 → 48,61 olay/s | birikme tepesi 525.000, en uzun bekleme 3,00 saat |
| Görev Kuyrukları ve Arka Plan İşleri | uzun işin istek yolundan çıkarılması | 100 ms üstü bekleyen takip isteği yüzde 29,02 → 0 | rapor süresi 3,60 → 4,14 s; 4 yuva ve 4,00 işçi |
| Rekabet Eden Tüketiciler | işin kaç tüketiciye bölüneceği | 48,61 olay/s için tüketici başına en az 9,45 olay/s; N = 8'de katkı 0,128 | sıra bozulması 102 → 2279 gönderi |
| Kuyruk Tabanlı Yük Dengeleme | öngörülmeyen sıçramanın tamponlanması | varış 972 olay/s'ye çıkarken birikme 525.000'de, bekleme 3,00 saatte sabit | kuyruk sınırı 525.391; hacim artışında 350.049 düşen olay |
| Öncelikli Kuyruk | işlerin hizmet düzeyine ayrılması | en üst sınıfın beklemesi 6,5 dakikadan 0,0'a iniyor | en alt sınıfta 300.000 iş kuyrukta kalıyor; pay ayırmada B'nin birikmesi 440.000 → 740.000 |
| Sıralı Konvoy | sıra kısıtının şeride sokulması | yanlış son durum 1 → 0; kısıtlı tepe yazma 55,55 olay/s | tüketici gereksinimi ×1,357, kaba anahtarla ×1,786 |
| Koreografi ve Orkestrasyon | iş akışının kurulma modeli | telafi sorumluluğu 3 → 1 birim; telafisiz adım 80 → 0/gün | ileti 1,3889 → 2,2222 ileti/s, oran 1,600 |
| Zamanlayıcı–Aracı–Denetçi | yarım kalan işin bulunması | yarım kalan iş 160 → 0/gün, fatura satırlarının yüzde 4'ü | 320 yeniden sürme ve 80 boşa yürütme/gün |
| Lider Seçimi | tekil sorumluluğun tek düğüme verilmesi | çift ve üretilmeyen fatura satırı 2000 + 2000 → 0/yıl | iki turluk gecikme; iki liderli tur 2'de kalıyor |
| Eşzamansız İstek–Yanıt | çağıranın beklememesi | 3,60 s'lik iş 200 ms eşiğin dışından çıkıyor | yoklama tepede 0,1736 istek/s, uçtaki hızın yüzde 0,034'ü |

Tablonun kuralı adıyla anılmalı. Bu kursta ölçü **iki tanedir ve seçim çağıranın bekleyip
beklememesine göre yapılır.** Çağıran bekliyorsa ölçü **zincir gecikmesidir**: adım sayısı,
zaman aşımı ile deneme sayısının çarpımı, uçtan uca en kötü süre ve bütçe aşıldığında düşen
istek. Çağıran beklemiyorsa ölçü **birikme ve boşalmadır**: bekleyen iş tepesi, boşalma süresi,
tüketici sayısının katkısı ve tamponun sınırında düşen iş. İki ölçü birbirinin yerine geçmez;
bir kuyruk kararı gecikme bütçesiyle, bir zincir kararı birikme tepesiyle savunulamaz. Kursun
kendi varsayımları — durum deposunun yükü, kayıt defterinin yaşam süresi, geçit payı, tepe
penceresi, uzun iş talebi, taşıyıcı kesintisi, ek akışın süresi, tüketici grubunun kapasitesi,
sıra kısıtı oranı, iş akışı hata oranı, asılı kalma oranı, devralma sıklığı ve rapor isteği
sıklığı — K01'in tablosuna karışmadı; her biri kendi adı, gerekçesi ve duyarlılığıyla yazıldı.

Kursun bıraktığı soru bir katman daha içeridedir. Servisler durumsuzlaştı, adreslerini buldu,
sözleşmelerini kırmadan evirdi ve bütçelerini böldü; iş kuyruğa alındı, öncelik verildi, sırası
korundu, akışı koordine edildi ve yarım kalanı bulundu. Ama bütün bu tasarım **tek bir veri
deposu** varsayımı üzerinde duruyor. Durum, servisin dışına çıkarıldığında bir yere kondu ve o
yerin nasıl ölçekleneceği hiç sorulmadı: deponun çoğaltılması, verinin parçalanması,
bağlamların kendi depolarına federe edilmesi ve önbelleğin hangi katmanda duracağı bu kursta
tasarlanmadı. K01'in hesabındaki `saklanan veri GB` satırı bu kurs boyunca hiç oynamadı;
`depoya ulasan istek/s` ise Mesaj Kuyrukları dersinde kuyruk kapasitesi kararıyla oynatıldı,
ama deponun kendisi hiç ölçeklenmedi. Sonraki kurs **Veri Katmanı Ölçekleme** o satırları ele
alır.

