---
title: 'Gözlemlenebilirlik ve Güvenilirlik'
source: 'https://academia.sh/tr/kurslar/gozlemlenebilirlik'
language: tr
updated: '2026-08-17T04:38:52+00:00'
license: 'CC BY-SA 4.0'
lessons: 19
outcomes:
    - 'Günlük, ölçüt ve iz üçlüsünü tutarlı bir telemetri modeline oturtur'
    - 'Servis düzeyi hedeflerini ölçülebilir göstergelerle tanımlar'
    - 'Dayanıklılık kalıplarıyla kısmi arızayı yönetir'
---

# Gözlemlenebilirlik ve Güvenilirlik

Birden çok süreçte koşan bir sistemin görünür kılınması ve kısmi arızaya dayanması: günlük, ölçüt ve iz üçlüsünün tutarlı bir telemetri modeline oturtulması, yapılandırılmış günlükleme ile bağıntı kimliği, sayaç, ölçer ve histogram ayrımı, yayılım bağlamı ve iz örnekleme, satıcıdan bağımsız enstrümantasyon, üç sinyalin tek olayda birleştirilmesi, gösterge ile hedefin ayrımı, altın sinyaller, hata bütçesi, belirti tabanlı uyarı, canlılık ile hazır olma ayrımı ve dayanıklılık kalıplarının kod içindeki yeri.

## Telemetri

Üç sinyalin uygulama kodunda üretilmesi: günlük, ölçüt ve izin hangi soruyu yanıtladığı, alan tabanlı günlüğün ve bağıntı kimliğinin isteği uçtan uca izlenebilir kılması, sayaç, ölçer ve histogramın ayrı sorulara karşılık gelmesi, yayılım bağlamının süreç sınırını geçmesi ve iz örneklemenin bilgi kaybı, satıcıdan bağımsız toplama arayüzü ve üç sinyalin tek olayda birleştirilmesi.

### Gözlemlenebilirliğin Üç Sinyali

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/gozlemlenebilirligin-uc-sinyali

Günlük, ölçüt ve iz sinyallerinin bölünmüş bir ödünç sisteminde gerçekten üretilmesi: aynı üç sorunun her sinyalle kaç adımda yanıtlandığı, hangi sorunun hangi sinyalde yanıtsız kaldığı ve üç sinyalin istek sayısıyla nasıl büyüdüğü.

Önceki kurs sistemi beş sürece böldü ve her sınırın yeni bir arıza kipi doğurduğunu ölçtü. Kapanışta
duran soru şuydu: bir isteğin nerede yavaşladığını ya da nerede kaybolduğunu görmenin aracı hiç
kurulmadı. Bu ders o aracın üç parçasını kurar ve her parçanın neyi yanıtlayıp neyi yanıtlayamadığını
sayar.

Üç sinyal şunlardır. **Günlük**, tek tek olayların metin kaydıdır. **Ölçüt**, olayların önceden
seçilmiş bir toplama indirgenmiş hâlidir. **İz**, bir işin adımlarını süreleriyle birlikte tutan
kayıttır ve her adımı bir **aralık**tır. Üçü de ölçmek içindir, ama üçü farklı soruya yanıt verir;
ders bunu iddia olarak değil, adım sayısı olarak gösterir.

İzleme kategorileri ve uyarı eşiği Başarım Karşıt Kalıpları ve İzleme kursunda
kuruldu; burada kategori listesi tekrarlanmaz. Bu kursun konusu üretim tarafıdır: sinyal uygulama
kodunda nasıl doğar, hangi alanı taşır, hangi alanı taşımaz.

#### Üç Sinyali Üreten Servis

Düzenek önceki kursun bıraktığı hâliyle sürüyor: katalog, üyelik, ödünç, bildirim ve ücretlendirme
ayrı süreçler. Ödünç servisi üyelik, katalog ve bildirim servislerini çağırır; üyelik servisi
ücretlendirmeyi çağırır. Aşağıdaki tek kaynak dosya hepsinin rolünü üstlenir ve her istekte üç
sinyali de üretir.

**TL1 — üç sinyal de isteği işleyen sürecin içinde, ek bir toplayıcı süreç olmadan üretilir.**
Gerekçe: ölçülecek olan sinyalin taşınması değil, doğduğu yerde hangi alanı taşıdığıdır. Toplayıcıya
bağlanma sorunu beşinci derste ayrı ele alınır.

```js
// servis.mjs — <ad> <port> [ardil...]; her istek uc sinyali de uretir; uzun omurlu surec
import { createServer } from "node:http";
import { appendFileSync, writeFileSync } from "node:fs";
const [ad, port, ...ardil] = process.argv.slice(2);
writeFileSync(`gunluk-${ad}.txt`, ""); writeFileSync(`iz-${ad}.txt`, "");
const olcut = { istek: 0, hata: 0, sure: 0 };      // olcut: yalniz toplam, kimlik yok
let yerelNo = 0;                                   // iz numarasi yalniz bu surecte anlamli
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));

createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://y");
  if (u.pathname === "/olcut") { yanit.end(JSON.stringify({ ad, ...olcut, sure: Math.round(olcut.sure) })); return; }
  const uye = u.searchParams.get("uye"), nusha = Number(u.searchParams.get("nusha"));
  const t0 = performance.now(), no = ++yerelNo;
  olcut.istek += 1;
  await bekle(ad === "katalog" && nusha % 5 === 0 ? 100 : 5);  // her besinci nusha yavas raf
  let durum = ad === "ucretlendirme" && uye === "4023" ? 402 : 200;   // odenmemis ucret
  for (const p of ardil) {
    const y = await fetch(`http://127.0.0.1:${p}/is?uye=${uye}&nusha=${nusha}`);
    if (y.status !== 200) durum = y.status;
  }
  const sure = performance.now() - t0;
  olcut.sure += sure; if (durum !== 200) olcut.hata += 1;
  appendFileSync(`gunluk-${ad}.txt`,
    `[${new Date().toISOString()}] ${ad}: uye ${uye} icin ${nusha} numarali nusha istegi ` +
    `${Math.round(sure)} ms surdu, sonuc ${durum}\n`);
  appendFileSync(`iz-${ad}.txt`, `${no} ${ad} ${Math.round(sure)}\n`);
  yanit.statusCode = durum; yanit.end(JSON.stringify({ ad, durum }));
}).listen(Number(port));
```

Üç sinyalin bu ilk hâli kasten yalındır. Günlük satırı düz cümledir. Ölçüt üç toplamdır. Aralık
yalnız sürecin kendi içinde artan bir numara taşır. Sonraki dört ders bu üç eksikliği tek tek
kapatır.

Yavaşlık ve hata kaynağı sabittir: numarası beşin katı olan her nüshada katalog servisi yüz
milisaniye harcar, üye 4023'ün her isteği ücretlendirme servisinden 402 ile döner ve bu kod üyelik
üzerinden ödünç servisine kadar yükselir. İstemci ödünç ucuna sırayla istek gönderir, üyeleri dört
üyelik bir döngüden seçer.

```js
// kos.mjs — <istek sayisi>; odunc ucuna sirayla istek gonderir
const n = Number(process.argv[2]);
const UYE = ["4021", "4022", "4023", "4024"];      // 4023 odenmemis ucreti olan uye
for (let i = 1; i <= n; i += 1) {
  await fetch(`http://127.0.0.1:9101/is?uye=${UYE[(i - 1) % 4]}&nusha=${i}`);
}
console.log(`${n} istek gonderildi`);
```

#### Aynı Soru, Üç Sinyal

Sorgu aracı üç soruyu üç sinyalle yanıtlamayı dener. Sorular gerçek sorulardır: bu istek nerede
yavaşladı, kaç istek hata aldı, hangi kullanıcı etkilendi.

**Adım**, sorgunun bir kayıt deposu üzerinde yaptığı geçidir: bir dosyayı ya da bir ucu okumak bir
adım, düz metni ayrıştırmak bir adım, süzmek bir adım, iki depoyu birleştirmeyi denemek bir adım.
Sayaç kodun içindedir, tahmin değildir. Yanıtlanamayan soruda da adımlar sayılır — vazgeçmeden önce
harcanan iş de bir maliyettir.

```js
// sor.mjs — uc soruyu uc sinyalle yanitlamayi dener; her depo gecisi bir adimdir
import { readFileSync, statSync } from "node:fs";
const SERVIS = ["odunc", "uyelik", "katalog", "ucretlendirme", "bildirim"];
const PORT = { odunc: 9101, uyelik: 9102, katalog: 9103, ucretlendirme: 9104, bildirim: 9105 };
const RE = /uye (\d+) icin (\d+) numarali nusha istegi ([\d.]+) ms surdu, sonuc (\d+)/;
let adim = 0; const say = () => { adim += 1; };
const satirlar = (on, s) => { say(); return readFileSync(`${on}-${s}.txt`, "utf8").split("\n").filter(Boolean); };
const gunlukAyristir = () => {                       // duz metin: servis basina bir ayristirma gecisi
  const o = {};
  for (const s of SERVIS) o[s] = satirlar("gunluk", s).map((l) => { const m = l.match(RE); return m && { uye: m[1], sure: +m[3], durum: +m[4] }; });
  for (const s of SERVIS) say();
  return o;
};
const izOku = () => { const o = {}; for (const s of SERVIS) o[s] = satirlar("iz", s).map((l) => { const [no, , sure] = l.split(" "); return { no: +no, sure: +sure }; }); return o; };
const olcutOku = async () => { const o = {}; for (const s of SERVIS) { say(); o[s] = await (await fetch(`http://127.0.0.1:${PORT[s]}/olcut`)).json(); } return o; };
const yaz = (soru, sinyal, yanit) => console.log(`${soru.padEnd(12)}${sinyal.padEnd(9)}${String(adim).padStart(5)}  ${yanit}`);

console.log(`${"soru".padEnd(12)}${"sinyal".padEnd(9)}${"adim".padStart(5)}  yanit`);
const olcut = await olcutOku(); const bosAdim = adim;

// S1 — bu istek nerede yavasladi (hedef: odunc ucundaki en yavas istek)
const ESIK = 60;                                     // 60 ms ustu = yavas kabul edilen istek
adim = 0; const g = gunlukAyristir(); say();
const yavas = g.odunc.filter((r) => r.sure > ESIK).length; say();
yaz("S1 nerede", "gunluk", `yanitlanamadi (${ESIK} ms ustunde ${yavas} odunc kaydi; ardil kayitla ortak kimlik yok)`);
adim = bosAdim; say();
yaz("S1 nerede", "olcut", `yanitlanamadi (istek basina ayrim yok; servis ortalamasi var)`);
adim = 0; const iz = izOku(); say();
const yavasAralik = iz.odunc.filter((a) => a.sure > ESIK).length; say();
yaz("S1 nerede", "iz", `kismi (${ESIK} ms ustunde ${yavasAralik} aralik; hangi ardilda gectigi yok)`);

// S2 — kac istek hata aldi
adim = 0; const g2 = gunlukAyristir(); say();
yaz("S2 kac hata", "gunluk", `${g2.odunc.filter((r) => r.durum !== 200).length} istek`);
adim = bosAdim; say(); yaz("S2 kac hata", "olcut", `${olcut.odunc.hata} istek`);
adim = 0; izOku(); say(); yaz("S2 kac hata", "iz", `yanitlanamadi (iz kaydinda durum alani yok)`);

// S3 — hangi kullanici etkilendi
adim = 0; const g3 = gunlukAyristir(); say();
yaz("S3 kim", "gunluk", `uye ${[...new Set(g3.odunc.filter((r) => r.durum !== 200).map((r) => r.uye))].join(",")}`);
adim = bosAdim; say(); yaz("S3 kim", "olcut", `yanitlanamadi (toplam sayac, kimlik tasimaz)`);
adim = 0; izOku(); say(); yaz("S3 kim", "iz", `yanitlanamadi (aralikta kimlik alani yok)`);

const bayt = (on) => SERVIS.reduce((t, s) => t + statSync(`${on}-${s}.txt`).size, 0);
const kayit = (on) => SERVIS.reduce((t, s) => t + satirlar(on, s).length, 0);
const ort = (on) => Math.round(bayt(on) / kayit(on));   // kayit basina bayt: basamak gurultusune dayanikli
const olcutBayt = SERVIS.reduce((t, s) => t + JSON.stringify(olcut[s]).length, 0);
console.log(`\nhacim: gunluk ${kayit("gunluk")} kayit x ort. ${ort("gunluk")} bayt, ` +
  `iz ${kayit("iz")} aralik x ort. ${ort("iz")} bayt, ` +
  `olcut ${SERVIS.length * 4} alan (istek sayisindan bagimsiz) / ${olcutBayt} bayt`);
```

Aşağıdaki blok beş süreci ayağa kaldırır, yirmi istek gönderip üç soruyu sorar, sonra yüz seksen
istek daha gönderip yalnız hacim satırını yeniden basar.

```bash
node servis.mjs ucretlendirme 9104 &
node servis.mjs katalog 9103 &
node servis.mjs bildirim 9105 &
node servis.mjs uyelik 9102 9104 &
node servis.mjs odunc 9101 9102 9103 9105 &
sleep 1

node kos.mjs 20
node sor.mjs
node kos.mjs 180
node sor.mjs | tail -1
pkill -f "node servis.mjs"
```

```
20 istek gonderildi
soru        sinyal    adim  yanit
S1 nerede   gunluk      12  yanitlanamadi (60 ms ustunde 4 odunc kaydi; ardil kayitla ortak kimlik yok)
S1 nerede   olcut        6  yanitlanamadi (istek basina ayrim yok; servis ortalamasi var)
S1 nerede   iz           7  kismi (60 ms ustunde 4 aralik; hangi ardilda gectigi yok)
S2 kac hata gunluk      11  5 istek
S2 kac hata olcut        6  5 istek
S2 kac hata iz           6  yanitlanamadi (iz kaydinda durum alani yok)
S3 kim      gunluk      11  uye 4023
S3 kim      olcut        6  yanitlanamadi (toplam sayac, kimlik tasimaz)
S3 kim      iz           6  yanitlanamadi (aralikta kimlik alani yok)

hacim: gunluk 100 kayit x ort. 98 bayt, iz 100 aralik x ort. 14 bayt, olcut 20 alan (istek sayisindan bagimsiz) / 240 bayt
180 istek gonderildi
hacim: gunluk 1000 kayit x ort. 99 bayt, iz 1000 aralik x ort. 15 bayt, olcut 20 alan (istek sayisindan bagimsiz) / 253 bayt
```

#### Dokuz Denemenin Beşi Boş Döndü

Tablo dokuz satırdır ve beşi yanıtsızdır. Yanıtlananlar şunlar: hata sayısını ölçüt altı adımda,
günlük on bir adımda veriyor; etkilenen üyeyi yalnız günlük veriyor, o da on bir adımda. Birinci
soruyu hiçbir sinyal tam yanıtlamıyor.

Aradaki fark sinyalin kalitesi değil, taşıdığı alandır. Ölçüt en ucuz yanıtı verir çünkü sorulacak
soru ölçüt yazılırken zaten seçilmiştir: `hata` sayacı "kaç istek hata aldı" sorusunun kodda
donmuş hâlidir. Aynı nedenle "hangi üye" sorusunu yanıtlayamaz — toplamın içinde kimlik yoktur ve
sonradan çıkarılamaz. Günlük tersidir: her olayın kendi satırı vardır, o yüzden kimlik sorusuna
yanıt verir, ama düz cümle olduğu için her sorguda beş ayrı ayrıştırma geçişi ister ve düzenli
ifade satır biçimine bağlıdır.

Birinci sorunun yanıtsız kalması bu dersin asıl bulgusudur. Yirmi istekten dördü altmış
milisaniyenin üstünde kaldı — bu sayı koşumdan bağımsızdır, çünkü yavaşlığı üreten kural nüsha
numarasına bağlıdır. Koşumdan bağımsız olan asıl şey ise o dört isteğin süresinin nerede
harcandığının **hiçbir depodan** okunamamasıdır. Günlükte katalog servisinin de yüz milisaniyelik
satırları duruyor, ama o satırı bu ödünç satırıyla birleştirecek ortak bir alan yok.
İz kaydı en yakınına geliyor: ödünç sürecindeki aralığın süresini veriyor, ama numarası yalnız kendi
sürecinde artan bir sayaç olduğu için katalog sürecindeki aralıkla eşleşmiyor. Beş süreç, beş ayrı
numara uzayı, sıfır bağ.

#### Hacim

Son iki satır üçüncü ölçüdür. Yirmi dış istek beş süreçte yüz günlük kaydı ve yüz aralık üretti;
iki yüz dış istek bin kayıt ve bin aralık üretti. Oran sabittir: dış istek başına beş kayıt, çünkü
çağrı çarpanı beştir ve her süreç kendi kaydını yazar. Kayıt başına ortalama boy günlükte doksan
sekiz-doksan dokuz bayt, izde on dört-on beş bayt; aradaki yedi katlık fark, günlüğün her satırda
zaman damgasını ve cümle kalıbını yeniden taşımasından geliyor.

Ölçüt bu büyümenin dışındadır. Beş servis, servis başına dört alan: yirmi alan, istek sayısı ne
olursa olsun. Toplam yük 240 bayttan 253 bayta çıktı; artışın tamamı biriken süre toplamının
basamak sayısından geliyor, kayıt sayısından değil. Ölçütün maliyeti istek sayısıyla değil, **alan
sayısıyla** büyür; günlük ve izin maliyeti istek sayısıyla büyür. Üç sinyalin bir arada tutulmasının
nedeni de budur: aynı soruyu farklı maliyetlerle ve farklı çözünürlükte yanıtlarlar.

#### Özet

- Günlük tek tek olayları, ölçüt önceden seçilmiş toplamları, iz bir işin adımlarını aralık olarak
  kaydeder; üçü aynı istekten aynı süreçte doğdu.
- Dokuz denemenin beşi yanıtsız kaldı: ölçüt kimlik sorusuna, iz durum ve kimlik sorusuna yanıt
  veremedi, "bu istek nerede yavaşladı" sorusunu hiçbiri tam yanıtlamadı.
- Ölçüt en az adımı harcadı (6), günlük en çoğunu (11–12); fark düz metnin servis başına bir
  ayrıştırma geçişi istemesinden geliyor.
- Ölçüt sorulacak soruyu yazılırken seçer: `hata` sayacı bir soruyu ucuzlatır, kimlik sorusunu ise
  sonsuza kadar kapatır.
- Hacim iki farklı yasaya uyuyor: 20 istek 100 kayıt, 200 istek 1000 kayıt üretti; ölçüt her iki
  durumda da 20 alan kaldı ve yalnız basamak sayısı kadar büyüdü.

#### Sonraki Adım

Beş yanıtsız sorunun üçünün tek bir ortak nedeni var: kayıtlarda isteği tanımlayan bir alan yok.
Ödünç servisinin yazdığı satırla katalog servisinin yazdığı satırı birbirine bağlayacak hiçbir şey
bulunmuyor, çünkü satırlar cümle olarak yazılıyor ve cümlede yalnız o sürecin bildiği şeyler var.
Sonraki ders günlüğü düz cümle olmaktan çıkarıp alanlara böler ve isteğe süreç sınırını geçen bir
kimlik takar. Ölçülecek olanlar bellidir: aynı sorunun süzme ve ayrıştırma adımlarının kaç tanesinin
düştüğü, bir isteğin kaç serviste izlenebilir hâle geldiği ve kimliğin taşınmadan bırakıldığı çağrı
yerlerinin sayısı.

### Yapılandırılmış Günlükleme

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/yapilandirilmis-gunlukleme

Düz cümle günlüğün alan tabanlı kayda dönüştürülmesi ve isteğe süreç sınırını geçen bir bağıntı kimliği takılması: aynı sorunun kaç ayrıştırma ve süzme adımıyla yanıtlandığı, bir isteğin kaç serviste izlenebildiği ve kimliğin taşınmadan bırakıldığı çağrı yerlerinin sayılması.

Önceki ders beş yanıtsız soru bıraktı ve üçünün ortak nedeni tekti: kayıtlarda isteği tanımlayan bir
alan yok. Günlük satırı cümleydi, cümlede yalnız o sürecin bildiği şeyler vardı ve iki servisin
satırını birleştirecek hiçbir şey yoktu. Bu ders iki değişiklik yapar: kayıt cümle olmaktan çıkıp
alanlara bölünür, isteğe süreç sınırını geçen bir **bağıntı kimliği** takılır. İkisinin de bedeli
ölçülür.

**Yapılandırılmış günlükleme**, kaydın serbest metin yerine adı belli alanlardan oluşmasıdır.
**Bağıntı kimliği**, bir dış isteğe verilen ve o istekten doğan bütün iç isteklerde taşınan
değerdir. İkincisi birincisi olmadan işe yaramaz: kimlik de sonuçta bir alandır ve serbest cümlenin
içinde aranması gereken bir dize değil, adıyla süzülebilen bir değer olmalıdır.

#### Alanlara Bölünen Kayıt

Günlükçü kendi yazılır ve tek işi vardır: sabit alanlarla çağrı anındaki alanları birleştirip tek
satır olarak eklemek.

```js
// gunlukcu.mjs — alan tabanli gunlukcu; her kayit tek satir, her alan adiyla durur
import { appendFileSync } from "node:fs";
export const gunlukcu = (dosya, sabit) => (alanlar) =>
  appendFileSync(dosya, JSON.stringify({ ...sabit, ...alanlar }) + "\n");
```

Servis her istekte **iki** kayıt yazar: önceki dersin düz cümlesi ve alan tabanlı karşılığı. İkisi
de aynı olguyu, aynı koşumda, aynı değerlerle anlatır; aradaki tek fark biçimdir, o yüzden ölçülen
adım farkı biçimin farkıdır.

**TL2 — bağıntı kimliği, dış isteği ilk karşılayan servis tarafından üretilir; başlıkta gelen bir
kimlik varsa üretilmez, olduğu gibi sürdürülür.** Gerekçe: kimliğin üretim yeri tek olmalıdır, yoksa
aynı istek her serviste yeni bir kimlik alır ve birleştirme yine yapılamaz. Üretim kuralı burada
nüsha numarasına bağlanmıştır, böylece ölçümdeki kimlikler koşumdan bağımsızdır.

```js
// servis2.mjs — <ad> <port> [ardil...]; ayni istek icin iki gunluk bicimi yazar
import { createServer } from "node:http";
import { writeFileSync, appendFileSync } from "node:fs";
import { gunlukcu } from "./gunlukcu.mjs";
const [ad, port, ...ardil] = process.argv.slice(2);
const BILDIRIM = "9205";
writeFileSync(`duz-${ad}.txt`, ""); writeFileSync(`alan-${ad}.txt`, "");
const alanYaz = gunlukcu(`alan-${ad}.txt`, { servis: ad });
let sayac = 0;
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));
const uc = (p, u) => `http://127.0.0.1:${p}/is?uye=${u.get("uye")}&nusha=${u.get("nusha")}`;
const cagir = (p, u, baginti) => fetch(uc(p, u), { headers: { "x-baginti": baginti } });
const haberVer = (p, u) => fetch(uc(p, u));      // eski yardimci: baslik parametresi hic olmadi

createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://y").searchParams;
  const uye = u.get("uye"), nusha = Number(u.get("nusha"));
  const baginti = istek.headers["x-baginti"] ?? `${ad}-${nusha}`;   // kimlik yoksa uretilir
  const t0 = performance.now(); sayac += 1;
  await bekle(ad === "katalog" && nusha % 5 === 0 ? 45 : 5);
  let durum = ad === "ucretlendirme" && uye === "4023" ? 402 : 200;
  for (const p of ardil) {
    const y = p === BILDIRIM ? await haberVer(p, u) : await cagir(p, u, baginti);
    if (y.status !== 200) durum = y.status;
  }
  const sure = Math.round(performance.now() - t0);
  appendFileSync(`duz-${ad}.txt`, `${ad}: uye ${uye} icin ${nusha} numarali nusha istegi ` +
    `${sure} ms surdu, sonuc ${durum}\n`);
  alanYaz({ baginti, uye, nusha, sure, durum });
  yanit.statusCode = durum; yanit.end("bitti");
}).listen(Number(port));
```

Kimliğin sınırı geçmesi tek satırda olur: `cagir` başlığı ekler. Yanındaki `haberVer` ise başlık
parametresi hiç almamış eski bir yardımcıdır ve bildirim çağrısı onunla yapılır. Bu, kurgu bir hata
değil, kimlik eklemenin tipik sonucudur: kimlik istemci yardımcısına eklenir, ama sistemde o
yardımcıyı kullanmayan bir çağrı yolu kalır. Ölçümün işi o yolu saymaktır.

#### Ölçüm

Sorgu aracı iki soruyu iki biçimde sorar. Adım tanımı önceki dersle aynıdır: bir dosyayı okumak,
ayrıştırmak, süzmek ya da iki depoyu birleştirmeyi denemek birer adımdır. Düz metinde her servisin
satır kalıbı ayrıdır, bu yüzden ayrıştırma servis başına bir geçiştir; alan tabanlı kayıtta biçim
genel olduğu için ayrıştırma tek geçiştir.

```js
// sor2.mjs — ayni sorular iki gunluk biciminde; adim = bir depo gecisi
import { readFileSync } from "node:fs";
const SERVIS = ["odunc", "uyelik", "katalog", "ucretlendirme", "bildirim"];
const CAGRI = [["odunc", "uyelik"], ["odunc", "katalog"], ["odunc", "bildirim"], ["uyelik", "ucretlendirme"]];
const RE = /uye (\d+) icin (\d+) numarali nusha istegi (\d+) ms surdu, sonuc (\d+)/;
let adim = 0; const say = () => { adim += 1; };
const satir = (on, s) => { say(); return readFileSync(`${on}-${s}.txt`, "utf8").split("\n").filter(Boolean); };
const duzOku = () => {                       // her servisin satir kalibi ayri: servis basina bir ayristirma
  const o = {};
  for (const s of SERVIS) o[s] = satir("duz", s).map((l) => { const m = l.match(RE); return m && { uye: m[1], nusha: +m[2], sure: +m[3], durum: +m[4] }; });
  for (const s of SERVIS) say();
  return o;
};
const alanOku = () => {                      // alan tabanli: bicim genel, tek ayristirma gecisi
  const o = {}; for (const s of SERVIS) o[s] = satir("alan", s);
  say(); for (const s of SERVIS) o[s] = o[s].map((l) => JSON.parse(l));
  return o;
};
const yaz = (soru, bicim, yanit) => console.log(`${soru.padEnd(11)}${bicim.padEnd(13)}${String(adim).padStart(5)}  ${yanit}`);
console.log(`${"soru".padEnd(11)}${"bicim".padEnd(13)}${"adim".padStart(5)}  yanit`);

// S3 — hangi uye etkilendi (iki bicim de yanitlayabiliyor)
adim = 0; const d = duzOku(); say();
yaz("S3 kim", "duz metin", `uye ${[...new Set(d.odunc.filter((r) => r.durum !== 200).map((r) => r.uye))].join(",")}`);
adim = 0; const a = alanOku(); say();
yaz("S3 kim", "alan tabanli", `uye ${[...new Set(a.odunc.filter((r) => r.durum !== 200).map((r) => r.uye))].join(",")}`);

// S1 — bu istek nerede yavasladi; hedef: 5 numarali nushanin istegi
const HEDEF = "odunc-5";
adim = 0; duzOku(); say(); say();
yaz("S1 nerede", "duz metin", "yanitlanamadi (satirda kimlik alani yok, servisler baglanamiyor)");
adim = 0; const a2 = alanOku(); say();
const yol = SERVIS.filter((s) => a2[s].some((r) => r.baginti === HEDEF));
const enUzun = yol.filter((s) => s !== "odunc").sort((x, y) =>
  a2[y].find((r) => r.baginti === HEDEF).sure - a2[x].find((r) => r.baginti === HEDEF).sure)[0];
say(); yaz("S1 nerede", "alan tabanli", `${HEDEF}: ${yol.join(" ")} (${yol.length}/${SERVIS.length} servis), en uzun ardil ${enUzun}`);

console.log(`\n${"cagri yeri".padEnd(26)}${"kimlik".padEnd(8)}kimlikli kayit`);
let tasiyan = 0;
for (const [ust, alt] of CAGRI) {
  const kume = new Set(a[ust].map((r) => r.baginti));
  const ortak = a[alt].filter((r) => kume.has(r.baginti)).length;
  if (ortak > 0) tasiyan += 1;
  console.log(`${`${ust} -> ${alt}`.padEnd(26)}${(ortak > 0 ? "evet" : "hayir").padEnd(8)}${ortak}/${a[alt].length}`);
}
console.log(`kimlik tasiyan cagri yeri ${tasiyan}/${CAGRI.length}, tasimayan ${CAGRI.length - tasiyan}; ` +
  `bir istek ${yol.length}/${SERVIS.length} serviste izlenebiliyor`);
const bayt = (on) => Math.round(SERVIS.reduce((t, s) => t + readFileSync(`${on}-${s}.txt`, "utf8").length, 0) / 100);
console.log(`kayit basina ort. boy: duz metin ${bayt("duz")} bayt, alan tabanli ${bayt("alan")} bayt (100 kayit)`);
```

```bash
cat > kos.mjs <<'EOF'
const UYE = ["4021", "4022", "4023", "4024"];      // 4023 odenmemis ucreti olan uye
for (let i = 1; i <= 20; i += 1)
  await fetch(`http://127.0.0.1:9201/is?uye=${UYE[(i - 1) % 4]}&nusha=${i}`);
EOF
node servis2.mjs ucretlendirme 9204 &
node servis2.mjs katalog 9203 &
node servis2.mjs bildirim 9205 &
node servis2.mjs uyelik 9202 9204 &
node servis2.mjs odunc 9201 9202 9203 9205 &
sleep 1

node kos.mjs
node sor2.mjs
pkill -f "node servis2.mjs"
```

```
soru       bicim         adim  yanit
S3 kim     duz metin       11  uye 4023
S3 kim     alan tabanli     7  uye 4023
S1 nerede  duz metin       12  yanitlanamadi (satirda kimlik alani yok, servisler baglanamiyor)
S1 nerede  alan tabanli     8  odunc-5: odunc uyelik katalog ucretlendirme (4/5 servis), en uzun ardil katalog

cagri yeri                kimlik  kimlikli kayit
odunc -> uyelik           evet    20/20
odunc -> katalog          evet    20/20
odunc -> bildirim         hayir   0/20
uyelik -> ucretlendirme   evet    20/20
kimlik tasiyan cagri yeri 3/4, tasimayan 1; bir istek 4/5 serviste izlenebiliyor
kayit basina ort. boy: duz metin 71 bayt, alan tabanli 88 bayt (100 kayit)
```

#### Dört Adım ve Bir Kör Nokta

Birinci karşılaştırma sıradan bir kazançtır. Aynı soru düz metinde on bir, alan tabanlı kayıtta yedi
adım sürdü. Dört adımın tamamı ayrıştırmadan geldi: düz metinde her servisin satır kalıbı ayrı
olduğu için düzenli ifade servis başına bir kez uygulanmak zorunda, alan tabanlı kayıtta biçim genel
olduğu için tek geçiş yetiyor. Bu fark servis sayısıyla büyür — beş yerine yirmi servis olsa düz
metin yirmi beş, alan tabanlı kayıt yirmi iki adım harcardı ve aradaki fark on dokuza çıkardı.
Ayrıştırmanın kırılganlığı da buradadır: satır kalıbı değişince düzenli ifade sessizce eşleşmeyi
bırakır, alan adı değişince kayıt açıkça o alanı kaybeder.

İkinci karşılaştırma dersin asıl noktasıdır. Önceki derste hiçbir sinyalin yanıtlayamadığı soru,
alan tabanlı kayıtla sekiz adımda yanıtlandı: `odunc-5` kimliği dört serviste görünüyor ve
ardıllardan en uzunu katalog. Düz metin aynı soruda hâlâ yanıtsız — sorun sürelerin eksikliği
değildi, süreleri birbirine bağlayacak alanın eksikliğiydi.

Üçüncü tablo kazancın sınırını gösteriyor. Dört çağrı yerinin üçü kimliği taşıyor, biri taşımıyor;
bildirim servisinin yirmi kaydının hiçbirinde ödünç isteğinin kimliği yok. Bunun ölçülen sonucu
şudur: bir istek beş servisten **dördünde** izlenebiliyor. Kimlik eklemenin bedeli tek satır, ama
kapsamı çağrı yolu sayısıyla ölçülür ve tek bir atlanan çağrı yeri bütün bir servisi haritanın
dışında bırakır. Bildirim burada rastgele seçilmedi: "bu bildirim neden gitmedi" bu sistemin en sık
sorulan sorusudur ve tam da o servis kör noktada kaldı.

Son satır bedeli veriyor. Alan tabanlı kayıt kayıt başına 88 bayt, düz metin 71 bayt: alan adları
her satırda yeniden yazıldığı için hacim dörtte bir arttı. Karşılığında sorgu adımı azaldı ve
yanıtlanamaz bir soru yanıtlanır oldu. Ölçülen takas budur — yazma tarafında bayt, okuma tarafında
adım.

#### Özet

- Alan tabanlı kayıt aynı soruyu 11 yerine 7 adımda yanıtladı; farkın tamamı ayrıştırmadan geliyor
  ve servis sayısıyla büyüyor.
- Bağıntı kimliği, önceki dersin hiçbir sinyalle yanıtlanamayan sorusunu 8 adımda yanıtlanır hâle
  getirdi: `odunc-5` kimliği dört serviste izlendi, en uzun ardıl katalog çıktı.
- Kimliği taşıyan çağrı yeri 4'te 3; taşımayan tek çağrı yeri bildirim servisini bütünüyle haritanın
  dışında bıraktı, bir istek 5 servisten 4'ünde izlenebiliyor.
- Atlanan çağrı yeri kimlik eklemenin doğal artığıdır: kimlik istemci yardımcısına eklenir, o
  yardımcıyı kullanmayan eski çağrı yolu sessizce kimliksiz kalır.
- Bedel yazma tarafındadır: kayıt başına 71 bayttan 88 bayta çıkış, alan adlarının her satırda
  yinelenmesinden geliyor.

#### Sonraki Adım

Günlük artık alanlara bölündü ve bir isteğin yolu dört serviste izlenebiliyor. Ama her sorgu hâlâ
bütün kayıtları okuyup süzmekle başlıyor: yirmi istek için yüz kayıt okundu, iki yüz istek için bin
kayıt okunacak. Önceki dersin ölçüt sinyali bu maliyetin dışındaydı, çünkü sorulacak soru kayıt
yazılırken seçilmişti. Sonraki ders o seçimin kendisine bakar: aynı olgu sayaç, ölçer ve histogram
olarak üç ayrı tiple ölçülür, her tipin yanıtlayabildiği ve kapattığı soru sayılır, histogramın kova
sayısına göre bellek maliyeti ile yüzdelik hatası karşılaştırılır.

### Ölçüt Türleri

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/olcut-turleri

Aynı olgunun sayaç, ölçer ve histogramla ölçülmesi: her tipin yanıtladığı ve kapattığı soru, ölçerin okumalar arasında kaybettiği gözlem sayısı, histogramın kova sayısına göre bellek maliyeti ve yüzdelik hatası.

Önceki iki ders günlüğü olgunlaştırdı: kayıt alanlara bölündü, bir istek dört serviste izlenir oldu.
Ama her sorgu hâlâ bütün kayıtları okumakla başlıyor. İlk derste ölçüt bu maliyetin dışındaydı,
çünkü sorulacak soru kayıt yazılırken seçilmişti. Bu ders o seçime bakar: aynı olgu üç ölçüt tipiyle
ölçülür ve her tipin hangi soruyu açtığı, hangisini kapattığı sayılır.

Ölçülen olgu tektir: **açık ödünç sayısı**. Her istek bir ödünç açar, aralarda toplu iadeler ve
seyrek toplu ödünç günleri olur. Aynı sayı üç tiple ölçülür. **Sayaç** yalnız artar. **Ölçer** o anki
değeri tutar. **Histogram** değerleri kovalara dağıtır. Üçü de aynı olgudan beslenir, üçü de farklı
bir soruyu yanıtlar.

Enstrümantasyonun kendi bedeli — ölçüt üretmenin isteğe eklediği yük — Başarım Karşıt
Kalıpları ve İzleme kursunda ölçüldü ve burada tekrarlanmaz. Buradaki bedel bellek ve
doğruluktur: bir tipin kaç alan tuttuğu ve yanıtının gerçekten ne kadar uzakta olduğu.

#### Üç Tip

```js
// olcutler.mjs — uc olcut tipi; ucu de elle yazildi, disaridan hicbir sey gelmiyor
export class Sayac {                       // yalniz artar; gecmis tek sayida toplanir
  constructor() { this.deger = 0; }
  art(n = 1) { this.deger += n; }
  oku() { return this.deger; }
}
export class Olcer {                       // o anki degeri tutar; onceki deger kaybolur
  constructor() { this.deger = 0; }
  yaz(v) { this.deger = v; }
  oku() { return this.deger; }
}
export class Histogram {                   // kova sayimlari; dagilim yaklasik olarak korunur
  constructor(sinir) { this.sinir = sinir; this.kova = new Array(sinir.length + 1).fill(0); this.adet = 0; }
  gozle(v) { let i = 0; while (i < this.sinir.length && v > this.sinir[i]) i += 1; this.kova[i] += 1; this.adet += 1; }
  alan() { return this.kova.length; }
  yuzdelik(p) {                            // hedefi asan kovanin icinde dogrusal aradegerleme
    const hedef = p * this.adet; let birikim = 0;
    for (let i = 0; i < this.kova.length; i += 1) {
      if (birikim + this.kova[i] >= hedef) {
        const alt = i === 0 ? 0 : this.sinir[i - 1];
        const ust = i < this.sinir.length ? this.sinir[i] : alt * 2;
        return alt + ((hedef - birikim) / this.kova[i]) * (ust - alt);
      }
      birikim += this.kova[i];
    }
    return this.sinir[this.sinir.length - 1];
  }
}
export const esitSinir = (k, enBuyuk) =>   // k kova: [0, enBuyuk] araligi esit bolunur
  Array.from({ length: k - 1 }, (_, i) => Math.round(((i + 1) * enBuyuk) / (k - 1)));
```

Üç sınıfın toplam durumu şudur: sayaç bir sayı, ölçer bir sayı, histogram kova sayısı kadar sayı.
Tiplerin farkı davranışlarında değil, **neyi unuttuklarında**dır. Sayaç her gözlemi toplama katıp
gözlemi unutur. Ölçer son gözlemi tutup öncekini unutur. Histogram gözlemin hangi kovaya düştüğünü
tutup değerin kendisini unutur.

**TL3 — açık ödünç sayısının değişim deseni koşumdan bağımsızdır: iade ve toplu ödünç kararları
tohumu görünen bir üreteçten gelir.** Gerekçe: karşılaştırılan şey ölçüt tiplerinin davranışıdır,
ortamın hızı değil; aynı gözlem dizisi her koşumda üretilmezse hata payları karşılaştırılamaz.

#### Aynı Olgu, Üç Tip

```js
// servis3.mjs — odunc servisi; ayni olguyu (acik odunc sayisi) uc tiple olcer; uzun omurlu surec
import { createServer } from "node:http";
import { Sayac, Olcer, Histogram, esitSinir } from "./olcutler.mjs";
const acilan = new Sayac(), acik = new Olcer(), dagilim = new Histogram(esitSinir(8, 400));
let tohum = 20260731;                      // gorunur tohum: iade deseni kosumdan bagimsiz
const sonraki = () => (tohum = (tohum * 1103515245 + 12345) % 2147483648);
let deger = 0, tepe = 0;                   // tepe: olcerin kacirdigini gostermek icin tutuluyor
const ornek = [];                          // tam orneklem: yalniz karsilastirma icin saklaniyor

createServer((istek, yanit) => {
  if (istek.url === "/olcut") {
    yanit.end(JSON.stringify({ acilan: acilan.oku(), acik: acik.oku(),
      kova: dagilim.kova, sinir: dagilim.sinir, tepe }));
    return;
  }
  if (istek.url === "/ornek") { yanit.end(JSON.stringify(ornek)); return; }
  deger += 1; acilan.art();                          // her istek bir odunc acar
  if (sonraki() % 60 === 0) deger = Math.max(0, deger - Math.ceil(deger / 2));  // toplu iade
  if (sonraki() % 211 === 0) deger += 180;           // toplu odunc gunu
  tepe = Math.max(tepe, deger);
  acik.yaz(deger); dagilim.gozle(deger); ornek.push(deger);
  yanit.end("bitti");
}).listen(9301);
```

Servis ayrıca iki şeyi karşılaştırma için tutuyor: gerçek tepe değeri ve tam örneklem. İkisi de
gerçek bir sistemde tutulmaz — tam da bu dersin ölçtüğü maliyet yüzünden. Burada duruyorlar ki üç
tipin verdiği yanıt gerçeğiyle karşılaştırılabilsin.

#### Ölçüm

Sorgu aracı iki şey yapar. Önce üç tipi yan yana koyup her birinin yanıtladığı ve yanıtlayamadığı
soruyu yazar. Sonra aynı gözlem dizisini dört, sekiz, on altı, otuz iki ve altmış dört kovalı
histogramlarla yeniden özetleyip her birinin bellek maliyetini ve p95 hatasını çıkarır.

```js
// sor3.mjs — uc tipin yanitladigi soru ve histogram kova sayisinin bedeli
import { Histogram, esitSinir } from "./olcutler.mjs";
const m = await (await fetch("http://127.0.0.1:9301/olcut")).json();
const ornek = await (await fetch("http://127.0.0.1:9301/ornek")).json();
const sirali = [...ornek].sort((a, b) => a - b);
const gercek = (p) => sirali[Math.floor(p * sirali.length)];
const h = new Histogram(m.sinir); h.kova = m.kova; h.adet = ornek.length;

console.log(`${"tip".padEnd(11)}${"alan".padStart(5)}  ${"yanitladigi soru".padEnd(36)}yanitlayamadigi soru`);
console.log(`${"sayac".padEnd(11)}${"1".padStart(5)}  ${`kac odunc acildi: ${m.acilan}`.padEnd(36)}su an kac odunc acik`);
console.log(`${"olcer".padEnd(11)}${"1".padStart(5)}  ${`su an kac odunc acik: ${m.acik}`.padEnd(36)}gecmiste tepe neydi`);
console.log(`${"histogram".padEnd(11)}${String(m.kova.length).padStart(5)}  ` +
  `${`gozlemlerin %95'i ${Math.round(h.yuzdelik(0.95))} altinda`.padEnd(36)}hangi gozlem tepe yapti`);
console.log(`gercek p95 ${gercek(0.95)}, gercek tepe ${m.tepe}; ` +
  `tam orneklem ${ornek.length} deger x 8 bayt = ${ornek.length * 8} bayt`);

console.log(`\n${"kova".padStart(5)}${"bellek".padStart(9)}${"p95 tahmin".padStart(12)}${"hata".padStart(7)}${"bagil".padStart(8)}`);
for (const k of [4, 8, 16, 32, 64]) {
  const hh = new Histogram(esitSinir(k, 400));
  for (const v of ornek) hh.gozle(v);
  const t = hh.yuzdelik(0.95), hata = Math.abs(t - gercek(0.95));
  console.log(`${String(k).padStart(5)}${`${hh.alan() * 8} bayt`.padStart(9)}${t.toFixed(1).padStart(12)}` +
    `${hata.toFixed(1).padStart(7)}${`%${((100 * hata) / gercek(0.95)).toFixed(1)}`.padStart(8)}`);
}
```

```bash
cat > kos.mjs <<'EOF'
const OKUMA = 100;                                 // olcer her 100 istekte bir okunur
let enYuksek = 0, okuma = 0;
for (let i = 1; i <= 2000; i += 1) {
  await fetch("http://127.0.0.1:9301/is");
  if (i % OKUMA !== 0) continue;
  const m = await (await fetch("http://127.0.0.1:9301/olcut")).json();
  enYuksek = Math.max(enYuksek, m.acik); okuma += 1;
}
const m = await (await fetch("http://127.0.0.1:9301/olcut")).json();
console.log(`olcer: 2000 gozlem, ${okuma} okuma, okunan en yuksek ${enYuksek}, ` +
  `gercek tepe ${m.tepe}; okumalar arasinda gorulmeyen gozlem ${2000 - okuma}`);
EOF
node servis3.mjs &
sleep 1

node kos.mjs
echo
node sor3.mjs
pkill -f "node servis3.mjs"
```

```
olcer: 2000 gozlem, 20 okuma, okunan en yuksek 199, gercek tepe 293; okumalar arasinda gorulmeyen gozlem 1980

tip         alan  yanitladigi soru                    yanitlayamadigi soru
sayac          1  kac odunc acildi: 2000              su an kac odunc acik
olcer          1  su an kac odunc acik: 26            gecmiste tepe neydi
histogram      8  gozlemlerin %95'i 159 altinda       hangi gozlem tepe yapti
gercek p95 159, gercek tepe 293; tam orneklem 2000 deger x 8 bayt = 16000 bayt

 kova   bellek  p95 tahmin   hata   bagil
    4  32 bayt       209.1   50.1   %31.5
    8  64 bayt       158.5    0.5    %0.3
   16 128 bayt       157.1    1.9    %1.2
   32 256 bayt       161.5    2.5    %1.6
   64 512 bayt       158.5    0.5    %0.3
```

#### Her Tip Bir Soruyu Kapatıyor

Üst tablo tiplerin bölüşümünü veriyor. Sayaç iki bin ödüncün açıldığını tek alanda söylüyor, ama
kaçının kapandığını bilmediği için "şu an kaç ödünç açık" sorusuna yanıt veremiyor — artışın geri
alınamaması sayacın tanımıdır. Ölçer o soruyu tek alanda yanıtlıyor: yirmi altı. Histogram ikisinin
de yanıtlayamadığını yanıtlıyor: gözlemlerin yüzde doksan beşi 159'un altında kalmış.

Ölçerin bedeli ilk satırda duruyor ve dersin en keskin sayısı odur. İki bin gözlem üretildi, ölçer
yirmi kez okundu, okunan en yüksek değer 199 çıktı — gerçek tepe ise 293. Okumalar arasında kalan
bin dokuz yüz seksen gözlem hiç görülmedi ve tepe yüzde otuz iki eksik raporlandı. Ölçerin tuttuğu
şey "o anki değer" olduğu için, okuma sıklığı ne olursa olsun okumalar arasındaki hareket kaybolur.
Bir eşiğin aşılıp aşılmadığı ölçerle sorulduğunda, yanıtın "hayır" olması eşiğin aşılmadığı değil,
aşıldığı ana denk gelinmediği anlamına gelebilir.

Alt tablo histogramın kova sayısını bedele bağlıyor. Dört kova otuz iki bayt tutuyor ve p95'i 209
diye veriyor: 159 olan gerçeğe göre yüzde otuz bir buçuk sapma. Sekiz kovada hata yarım birime,
yüzde 0,3'e düşüyor. Buradan sonrası şaşırtıcıdır ve dersin ikinci bulgusudur: **hata kova sayısıyla
düzgün azalmıyor**. On altı kovada 1,9, otuz iki kovada 2,5, altmış dörtte yine 0,5. Neden açıktır —
hata, kova sınırlarının dağılımın hangi noktasına düştüğüne bağlıdır. Yüzde doksan beşinci yüzdelik
159 civarındadır; bir sınır oraya yakın düşerse aradeğerleme kısa mesafede yapılır ve hata küçülür,
sınır uzaksa kovanın içi doğrusal varsayılır ve hata büyür. Kova sayısını artırmak beklenen hatayı
düşürür, ama tek bir yüzdelik için garanti vermez.

Karşılaştırma çizgisi son satırdadır: tam örneklem 16.000 bayt tutuyor ve p95'i kesin veriyor.
Sekiz kovalı histogram 64 bayt tutuyor ve aynı yanıtı yarım birim hatayla veriyor — iki yüz elli
kat daha az bellekle. Ölçütün asıl işi budur: cevabı yaklaşık ama sabit maliyetle tutmak. Sabit
maliyet, önceki dersteki günlük hacminin istek sayısıyla büyümesinin karşıtıdır.

#### Özet

- Aynı olgu üç tiple ölçüldü: sayaç 2000 açılışı, ölçer 26 açık ödüncü, histogram gözlemlerin
  %95'inin 159 altında olduğunu verdi; hiçbiri diğerinin sorusunu yanıtlamadı.
- Tiplerin farkı neyi unuttuklarındadır: sayaç gözlemi, ölçer önceki değeri, histogram değerin
  kendisini unutur.
- Ölçer 2000 gözlemin 20'sini gördü; okunan tepe 199, gerçek tepe 293 — okumalar arasındaki 1980
  gözlem kayboldu ve tepe eksik raporlandı.
- Histogramın kova sayısı belleği doğrusal büyütüyor (4 kova 32 bayt, 64 kova 512 bayt), ama hata
  düzgün azalmıyor: %31,5 → %0,3 → %1,2 → %1,6 → %0,3.
- 8 kovalı histogram, 16.000 baytlık tam örneklemin verdiği p95'i 64 baytla ve yarım birim hatayla
  verdi; ölçütün maliyeti gözlem sayısıyla değil alan sayısıyla büyür.

#### Sonraki Adım

Üç sinyalin ikisi artık kodda üretiliyor: alan tabanlı günlük bir isteği dört serviste izliyor,
ölçütler sabit maliyetle özet veriyor. Geriye ilk dersin yanıtsız bıraktığı asıl soru kalıyor. Bağıntı
kimliği bir isteğin hangi servislerden geçtiğini söylüyor, ama sürelerin nasıl iç içe geçtiğini
söylemiyor: katalog çağrısı ödünç isteğinin içinde mi başladı, üyelik çağrısından önce mi bitti,
bekleme nerede oldu. Sonraki ders iz bağlamını kendi yazar ve süreç sınırından geçirir. Ölçülecek
olanlar bellidir: bağlamın taşınmadığı yerde kopan iz sayısı, örnekleme oranının yakalanan sorunlu
istek yüzdesine etkisi ve iz kaydının hacmi.

### Dağıtık İzleme

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/dagitik-izleme

İz bağlamının elle yazılıp süreç sınırından geçirilmesi ve örnekleme oranının ölçülmesi: bağlamın taşınmadığı çağrı yerinde kopan iz sayısı, örnekleme oranının yakalanan sorunlu istek yüzdesine etkisi ve iz kaydının aralık başına hacmi.

Bağıntı kimliği bir isteğin hangi servislerden geçtiğini söylüyor, ölçütler sabit maliyetle özet
veriyor. İkisi de bir şeyi söylemiyor: sürelerin nasıl iç içe geçtiğini. Katalog çağrısı ödünç
isteğinin içinde nerede başladı, üyelik dalıyla aynı anda mı koştu, bekleme hangi çağrıda oluştu.
Bu ders o yapıyı kuran sinyali yazar.

**İz**, bir dış isteğin doğurduğu bütün işlerin ortak kaydıdır; **aralık** o kaydın tek bir
parçasıdır — bir işin başlangıcıyla bitişi arasında geçen süre ve o işin üst aralığa bağı. Buradaki
aralık, veri erişimi derslerinde geçen sorgu aralığından bütünüyle ayrı bir şeydir: orada aralık bir
anahtar kümesinin alt ve üst sınırıdır, burada bir işin zaman içindeki başlangıç ve bitiş noktasıdır.
**Yayılım**, bağlamın süreç sınırından geçirilmesidir; olmadığı yerde iz kopar.

#### İz Bağlamı

Bağlam üç değerden oluşur: izin kimliği, üst aralığın kimliği ve örneklenip örneklenmediği. Üçü tek
bir başlıkta taşınır, çünkü sınırın karşısına geçebilen tek şey isteğin kendisidir.

```js
// iz.mjs — iz baglami ve aralik; baglam bir baslikta tasinir, aralik dosyaya yazilir
import { appendFileSync } from "node:fs";
export const BASLIK = "x-iz";
export const baglamCoz = (deger) => {            // "<iz>|<ust aralik>|<orneklendi>"
  if (!deger) return null;
  const [iz, ust, o] = deger.split("|");
  return { iz, ust, ornek: o === "1" };
};
export const baglamKur = (b) => `${b.iz}|${b.ust}|${b.ornek ? 1 : 0}`;
export const karma = (s) => {                    // kimligi esit dagitan karma; ornekleme karari buna dayanir
  let h = 2166136261;
  for (const c of s) { h ^= c.charCodeAt(0); h = Math.imul(h, 16777619) >>> 0; }
  return h;
};
export const orneklemeKarari = (iz, oran) => karma(iz) % 1000 < oran * 1000;

export class Iz {
  constructor(dosya, servis) { this.dosya = dosya; this.servis = servis; this.no = 0; }
  basla(ad, baglam) {
    return { iz: baglam.iz, aralik: `${this.servis}${(this.no += 1)}`, ust: baglam.ust,
      ad, t0: performance.now(), ornek: baglam.ornek };
  }
  bitir(a) {
    if (!a.ornek) return;                        // orneklenmemis aralik hic yazilmaz
    appendFileSync(this.dosya, JSON.stringify({ iz: a.iz, aralik: a.aralik, ust: a.ust,
      servis: this.servis, ad: a.ad, sure: Math.round(performance.now() - a.t0) }) + "\n");
  }
}
```

**TL4 — örnekleme kararı yalnız kökte verilir ve bağlamla taşınır.** Gerekçe: karar her serviste
ayrı verilseydi aynı izin bazı aralıkları kaydedilir, bazıları kaydedilmezdi ve yarım izler tam
izlerden ayırt edilemezdi. Kararın kökte verilmesi, kaydedilen her izin bütün olmasını sağlar; bedeli
de sonraki bölümde ölçülen şeydir.

#### Bağlamı Sınırdan Geçirmek

Servisin yaptığı üç şey var: gelen başlıktan bağlamı çözmek, gelen bağlam yoksa kök olup iz kimliği
ile örnekleme kararını üretmek, ardıla giderken bağlamı kendi aralık kimliğiyle güncelleyip başlığa
koymak. Önceki derslerdeki bildirim çağrı yeri burada da bağlamı taşımıyor.

```js
// servis4.mjs — <ad> <port> [ardil...]; iz baglami surec sinirini gecer; uzun omurlu surec
import { createServer } from "node:http";
import { writeFileSync } from "node:fs";
import { Iz, BASLIK, baglamCoz, baglamKur, orneklemeKarari } from "./iz.mjs";
const [ad, port, ...ardil] = process.argv.slice(2);
const BILDIRIM = "9405";
const dosya = `iz-${ad}.txt`; writeFileSync(dosya, "");
const iz = new Iz(dosya, ad);
let oran = 1;
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));
const uc = (p, u) => `http://127.0.0.1:${p}/is?uye=${u.get("uye")}&nusha=${u.get("nusha")}`;

createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://y").searchParams;
  if (istek.url.startsWith("/oran")) {           // orani ayarla ve iz dosyasini bosalt
    oran = Number(u.get("deger")); writeFileSync(dosya, ""); yanit.end("tamam"); return;
  }
  const uye = u.get("uye"), nusha = Number(u.get("nusha"));
  let baglam = baglamCoz(istek.headers[BASLIK]);
  if (!baglam) {                                 // kok: iz kimligi ve ornekleme karari burada verilir
    const izNo = `${ad}-${nusha}`;
    baglam = { iz: izNo, ust: "", ornek: orneklemeKarari(izNo, oran) };
  }
  const a = iz.basla(`${ad}/is`, baglam);
  await bekle(ad === "katalog" && nusha % 5 === 0 ? 40 : 3);
  let durum = ad === "ucretlendirme" && uye === "4023" ? 402 : 200;
  for (const p of ardil) {
    const bas = p === BILDIRIM ? {}                // bildirim cagri yeri baglami tasimiyor
      : { [BASLIK]: baglamKur({ ...baglam, ust: a.aralik }) };
    const y = await fetch(uc(p, u), { headers: bas });
    if (y.status !== 200) durum = y.status;
  }
  iz.bitir(a);
  yanit.statusCode = durum; yanit.end("bitti");
}).listen(Number(port));
```

Bildirim servisi bağlamsız bir istek alınca kendini kök sanıyor ve kendi izini açıyor. Sonuç, kayıp
bir aralık değil, **fazladan bir iz**: ödünç isteğinin izi dört aralıkta bitiyor, bildirim işi ise
hiçbir yere bağlanmayan ayrı bir iz olarak duruyor.

#### Ölçüm

Sorgu aracı iz dosyalarını okuyup aralıkları iz kimliğine göre topluyor. Ödünç kimliğiyle başlayan
izler gerçek izlerdir; bildirim kimliğiyle başlayanlar kopuk izlerdir. Sorunlu istek tanımı koddadır:
beşin katı nüshalar yavaş raftan geliyor, dört üyeden biri ödenmemiş ücret nedeniyle hata alıyor.

```js
// sor4.mjs — <oran>; iz butunlugu ve ornekleme oraninin sorunlu isteklere etkisi
import { readFileSync, statSync } from "node:fs";
const SERVIS = ["odunc", "uyelik", "katalog", "ucretlendirme", "bildirim"];
const N = 100;
const sorunlu = (n) => n % 5 === 0 || (n - 1) % 4 === 2;   // yavas raf ya da odenmemis ucret
const oran = process.argv[2];
const aralik = SERVIS.flatMap((s) =>
  readFileSync(`iz-${s}.txt`, "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l)));
const izler = new Map();
for (const a of aralik) { if (!izler.has(a.iz)) izler.set(a.iz, []); izler.get(a.iz).push(a); }
const kok = [...izler.keys()].filter((k) => k.startsWith("odunc"));
const kopuk = [...izler.keys()].filter((k) => k.startsWith("bildirim"));
const tam = kok.filter((k) => izler.get(k).length === 4).length;
const yakalanan = kok.filter((k) => sorunlu(Number(k.split("-")[1]))).length;
const hepsi = Array.from({ length: N }, (_, i) => i + 1).filter(sorunlu).length;
const bayt = SERVIS.reduce((t, s) => t + statSync(`iz-${s}.txt`).size, 0);
console.log(`${oran.padStart(5)}${String(aralik.length).padStart(9)}${String(kok.length).padStart(5)}` +
  `${String(tam).padStart(8)}${String(kopuk.length).padStart(10)}${`${yakalanan}/${hepsi}`.padStart(12)}` +
  `${`%${Math.round((100 * yakalanan) / hepsi)}`.padStart(7)}` +
  `${String(aralik.length ? Math.round(bayt / aralik.length) : 0).padStart(12)}`);
```

```bash
cat > kos.mjs <<'EOF'
const UYE = ["4021", "4022", "4023", "4024"];      // 4023 odenmemis ucreti olan uye
const PORT = [9401, 9402, 9403, 9404, 9405];
for (const oran of ["1.00", "0.50", "0.10", "0.02"]) {
  for (const p of PORT) await fetch(`http://127.0.0.1:${p}/oran?deger=${oran}`);
  for (let i = 1; i <= 100; i += 1)
    await fetch(`http://127.0.0.1:9401/is?uye=${UYE[(i - 1) % 4]}&nusha=${i}`);
  await new Promise((c) => setTimeout(c, 100));
  const { execSync } = await import("node:child_process");
  process.stdout.write(execSync(`node sor4.mjs ${oran}`).toString());
}
EOF
node servis4.mjs ucretlendirme 9404 &
node servis4.mjs katalog 9403 &
node servis4.mjs bildirim 9405 &
node servis4.mjs uyelik 9402 9404 &
node servis4.mjs odunc 9401 9402 9403 9405 &
sleep 1

printf '%5s%9s%5s%8s%10s%12s%7s%12s\n' oran aralik iz "tam iz" "kopuk iz" yakalanan yuzde "bayt/aralik"
node kos.mjs
pkill -f "node servis4.mjs"
```

```
 oran   aralik   iz  tam iz  kopuk iz   yakalanan  yuzde bayt/aralik
 1.00      500  100     100       100       40/40   %100         102
 0.50      246   49      49        50       22/40    %55         103
 0.10       46    8       8        14        4/40    %10         103
 0.02        2    0       0         2        0/40     %0         101
```

#### Kopan İz ve Örneklemenin Bedeli

İlk satır bağlamın gerçekten sınırı geçtiğini gösteriyor: yüz dış istek, yüz iz ve her biri **tam**,
yani dört aralıklı. Ödünç aralığı üst, üyelik ve katalog aralıkları onun altında, ücretlendirme
aralığı üyeliğin altında. Önceki derslerin yanıtlayamadığı "bekleme nerede oldu" sorusu artık kaydın
kendi yapısında duruyor.

Aynı satırın yanında yüz **kopuk iz** var. Bildirim çağrı yeri bağlamı taşımadığı için o servisteki
her iş kendi kökünü açıyor: yüz istek için yüz ayrı, tek aralıklı iz. Ölçünün önemi şudur — kopan iz
eksik veri gibi görünmez, **fazla veri** gibi görünür. Kayıtlarda iki yüz iz duruyor ve hiçbir şey
bunların yüzünün aslında diğer yüzün parçası olduğunu söylemiyor. Bir isteğin yolunu izlemeye çalışan
kişi, bildirimin hiç çağrılmadığı sonucuna varır.

Alt üç satır örnekleme oranının bedelini veriyor. Oran yarıya inince yazılan aralık beş yüzden iki
yüz kırk altıya, onda bire inince kırk altıya, elliye bire inince ikiye düşüyor. Hacim oranla
neredeyse doğrusal azalıyor ve aralık başına boy 101–103 bayt aralığında sabit kalıyor: örnekleme
kaydın boyunu değil, sayısını değiştiriyor.

Karşılığında kaybedilen şey sağ sütunlarda. Kırk sorunlu isteğin tamamı ancak tam örneklemede
yakalandı. Yüzde ellilik oranda kırk sorunlu isteğin yirmi ikisi, onda birlik oranda dördü,
ellide birlik oranda **hiçbiri** yakalandı. Sayılar oranın kendisine yakın çıkıyor ve dersin
bulgusu tam olarak budur: **kökte verilen örnekleme kararı sorunlu isteği kayırmaz.** Karar
isteğin başında, henüz yavaşlayacağı ya da hata alacağı bilinmeden veriliyor; dolayısıyla sorunlu
istekler de diğerleri gibi oranla seyreltiliyor. Yüzde iki örnekleme, "izlerin yüzde ikisini
tutuyorum" demek değil, "sorunların yüzde doksan sekizini hiç görmeyeceğim" demektir.

Örnekleme kararının kökte verilmesi bir şeyi kazandırdı: kaydedilen izlerin **tamamı** tam. Yüzde
elli oranda yazılan kırk dokuz izin kırk dokuzu da dört aralıklı. Karar her serviste ayrı verilseydi
aynı hacimde çok daha fazla, ama yarım iz elde edilirdi — ve yarım iz, kopuk iz gibi, veriyi
yanıltıcı hâle getirirdi.

#### Özet

- İz bağlamı üç değerden oluşuyor (iz kimliği, üst aralık, örnekleme kararı) ve tek başlıkla süreç
  sınırını geçiyor; tam örneklemede 100 istek 100 tam ize ve 400 bağlı aralığa dönüştü.
- Bağlamı taşımayan tek çağrı yeri 100 kopuk iz üretti; kopan iz eksik veri olarak değil, birbirine
  bağlanmamış fazla iz olarak görünüyor.
- Örnekleme oranı hacmi neredeyse doğrusal düşürüyor: 500, 246, 46 ve 2 aralık; aralık başına boy
  101–103 bayt sabit kaldı.
- Kökte verilen örnekleme kararı sorunlu isteği kayırmıyor: 40 sorunlu isteğin yakalananı sırasıyla
  40, 22, 4 ve 0 oldu.
- Kararın kökte verilmesi kaydedilen her izin tam olmasını sağladı: her oranda yazılan izlerin
  tamamı dört aralıklıydı.

#### Sonraki Adım

Üç sinyal de artık kodda üretiliyor, ama üçü de kendi yolunu izliyor. Günlükçü kendi dosyasına
yazıyor, ölçütler bir uçta duruyor, aralıklar başka bir dosyaya düşüyor; her birinin nereye
yazacağı servis kodunun içine gömülü. Bu, uygulama kodunu tek bir toplama biçimine bağlar: yazma
yeri değişirse beş serviste de arama yapmak gerekir. Sonraki ders bu bağı keser ve bir arayüz
tasarlar. Ölçülecek olan tek şeydir: toplayıcı gerçekleştirimi değiştiğinde kaç dosyaya dokunulur.

### Enstrümantasyon Standartları

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/enstrumantasyon-standartlari

Telemetri toplamanın uygulama kodundan ayrılması: aynı arayüzün iki toplayıcı gerçekleştirmesiyle karşılanması, gerçekleştirme değiştiğinde dokunulan dosya sayısının bağlı ve arayüzlü yerleşimde ölçülmesi ve toplayıcı adının kaç uygulama dosyasında geçtiğinin sayılması.

Üç sinyal de kodda üretiliyor, ama her birinin nereye yazılacağı servis kodunun içinde duruyor:
günlükçü kendi dosyasını açıyor, ölçütler bir uçtan sunuluyor, aralıklar başka bir dosyaya
düşüyor. Bu bağ ilk bakışta zararsızdır. Zararı ancak toplama biçimi değiştiğinde görünür ve
görüldüğü anda ölçülebilir bir sayıya dönüşür: kaç dosyaya dokunmak gerekti.

**Satıcıdan bağımsız enstrümantasyon**, uygulama kodunun telemetriyi nereye ve nasıl gönderdiğini
bilmemesidir. Bu dersin kurduğu şey bir ad değil, bir **arayüz**tür: uygulama kodu yalnız "şu olay
oldu" der; olayın dosyaya mı yazılacağı, sayılacağı mı, bir yere mi gönderileceği başka bir yerin
işidir. Ölçü de tek bir soruya iner — gerçekleştirme değişince kaç dosya değişir.

#### Aynı Arayüzün İki Gerçekleştirmesi

Arayüz iki işlemden oluşur: `topla` bir olayı alır, `ozet` toplananın durumunu döndürür. İki
gerçekleştirme davranış olarak taban tabana zıttır. Birincisi her olayı ayrı satır olarak diske
yazar; ikincisi tekil olayı hiç saklamaz, yalnız türüne göre sayar.

```js
// toplayicilar.mjs — ayni arayuzun iki gerceklestirmesi; ikisi de topla ve ozet uygular
import { appendFileSync, writeFileSync } from "node:fs";
export const dosyaToplayici = (dosya) => {        // her olayi ayri satir olarak yazar
  writeFileSync(dosya, "");
  return { topla: (o) => appendFileSync(dosya, JSON.stringify(o) + "\n"),
    ozet: () => `olaylar ${dosya} dosyasinda` };
};
export const ozetToplayici = () => {              // olaylari turune gore sayar, tekil olay yazmaz
  const say = new Map();
  return { topla: (o) => say.set(o.tur, (say.get(o.tur) ?? 0) + 1),
    ozet: () => [...say].map(([t, n]) => `${t}=${n}`).join(" ") };
};
```

Uygulama kodunun gördüğü yüz ayrıdır. Servis kodu `topla` çağırmaz; üç sinyale karşılık gelen üç
işlem çağırır ve olayın nereye gittiğini bilmez. Bağlama tek bir dosyada, tek bir satırda yapılır.

```js
// olcum.mjs — uygulama kodunun gordugu tek yuz; hangi toplayicinin baglandigini bilmez
import { toplayici } from "./baglanti.mjs";
export const gunluk = (alanlar) => toplayici.topla({ tur: "gunluk", ...alanlar });
export const olcut = (ad, deger) => toplayici.topla({ tur: "olcut", ad, deger });
export const aralik = (ad, sure) => toplayici.topla({ tur: "iz", ad, sure });
export const ozet = () => toplayici.ozet();
```

```js
// baglanti.mjs — tek baglama noktasi; gerceklestirme yalniz bu satirda seciliyor
import { dosyaToplayici } from "./toplayicilar.mjs";
export const toplayici = dosyaToplayici("olay-arayuzlu.txt");        // BAGLAMA
```

**TL5 — beş servis ayrı dağıtım birimleridir, dolayısıyla ayrı kaynak dosyalardır.** Gerekçe:
ölçülen şey bir değişikliğin yayılma yüzeyidir; tek kaynak dosyadan rol argümanıyla türetilen bir
düzenekte her değişiklik tanım gereği tek dosyaya düşer ve ölçü anlamını yitirir. Beş dosya aynı
şablondan üretilir, aralarındaki tek fark rol adı ve porttur.

#### İki Yerleşim

İki yerleşim aynı işi yapar. Bağlı yerleşimde servis dosyası toplayıcıyı adıyla çağırır ve olayları
kendisi biçimlendirir. Arayüzlü yerleşimde servis dosyasında hiçbir toplayıcı adı geçmez.

```js
// sablon-bagli.mjs — ROL ve PORT yerine konarak bes servis dosyasi uretilir
import { createServer } from "node:http";
import { dosyaToplayici } from "./toplayicilar.mjs";
const toplayici = dosyaToplayici("olay-bagli.txt");                  // BAGLAMA
let istek = 0;
createServer((i, y) => {
  if (i.url === "/ozet") { y.end(toplayici.ozet()); return; }
  const t0 = performance.now(); istek += 1;
  toplayici.topla({ tur: "gunluk", servis: "ROL", olay: "odunc istegi islendi" });
  toplayici.topla({ tur: "olcut", ad: "ROL_istek", deger: istek });
  toplayici.topla({ tur: "iz", ad: "ROL/is", sure: Math.round(performance.now() - t0) });
  y.end("bitti");
}).listen(PORT);
```

```js
// sablon-arayuzlu.mjs — ROL ve PORT yerine konarak bes servis dosyasi uretilir
import { createServer } from "node:http";
import { gunluk, olcut, aralik, ozet } from "./olcum.mjs";
let istek = 0;
createServer((i, y) => {
  if (i.url === "/ozet") { y.end(ozet()); return; }
  const t0 = performance.now(); istek += 1;
  gunluk({ servis: "ROL", olay: "odunc istegi islendi" });
  olcut("ROL_istek", istek);
  aralik("ROL/is", Math.round(performance.now() - t0));
  y.end("bitti");
}).listen(PORT);
```

#### Ölçüm

Ölçüm geçişi gerçekten yapar. Önce iki yerleşim de dosya toplayıcısıyla koşar, sonra `gecir`
işlevi ilgili dosyalarda gerçekleştirmeyi değiştirir, sonra ikisi de yeniden koşar. Dokunulan dosya
sayısı tahmin edilmez: geçişten önce alınan dizin kopyasıyla sonrası karşılaştırılır.

```bash
SERVIS="odunc uyelik katalog ucretlendirme bildirim"
rm -rf bagli arayuzlu bagli-once arayuzlu-once; mkdir -p bagli arayuzlu
cp toplayicilar.mjs bagli/ ; cp toplayicilar.mjs olcum.mjs baglanti.mjs arayuzlu/
i=0
for s in $SERVIS; do
  sed -e "s/ROL/$s/g" -e "s/PORT/$((9501 + i))/" sablon-bagli.mjs > bagli/$s.mjs
  sed -e "s/ROL/$s/g" -e "s/PORT/$((9511 + i))/" sablon-arayuzlu.mjs > arayuzlu/$s.mjs
  i=$((i + 1))
done
gecir() {   # dosya toplayicisini ozet toplayicisiyla degistirir
  perl -pi -e 's/dosyaToplayici/ozetToplayici/g; s/\("olay-[a-z]+\.txt"\)/()/' "$1"
}
kosur() {   # $1 dizininde odunc servisini ayaga kaldirir, 5 istek atar, ozeti yazar
  (cd "$1" && node odunc.mjs) & PID=$!
  sleep 0.7
  for n in 1 2 3 4 5; do curl -s -o /dev/null "http://127.0.0.1:$2/is"; done
  printf '%-10s %s\n' "$1" "$(curl -s http://127.0.0.1:$2/ozet)"
  kill $PID 2>/dev/null; wait $PID 2>/dev/null
}

echo "gecis oncesi:"
kosur bagli 9501
kosur arayuzlu 9511
echo "ilk olay kaydi: $(head -1 bagli/olay-bagli.txt)"

cp -r bagli bagli-once; cp -r arayuzlu arayuzlu-once
for s in $SERVIS; do gecir bagli/$s.mjs; done
gecir arayuzlu/baglanti.mjs

echo
echo "gecis sonrasi:"
kosur bagli 9501
kosur arayuzlu 9511

echo
printf '%-10s%34s%28s\n' yerlesim "toplayici adi gecen uygulama dosyasi" "gecis icin dokunulan dosya"
for y in bagli arayuzlu; do
  ad=$(grep -l "Toplayici" $y/*.mjs | grep -v toplayicilar | grep -v baglanti | wc -l | tr -d ' ')
  dk=$(diff -rq $y-once $y | wc -l | tr -d ' ')
  printf '%-10s%34s%28s\n' "$y" "$ad" "$dk"
done
echo "dokunulan dosyalar (arayuzlu): $(diff -rq arayuzlu-once arayuzlu | awk '{print $4}' | paste -sd' ' -)"
```

```
gecis oncesi:
bagli      olaylar olay-bagli.txt dosyasinda
arayuzlu   olaylar olay-arayuzlu.txt dosyasinda
ilk olay kaydi: {"tur":"gunluk","servis":"odunc","olay":"odunc istegi islendi"}

gecis sonrasi:
bagli      gunluk=5 olcut=5 iz=5
arayuzlu   gunluk=5 olcut=5 iz=5

yerlesim  toplayici adi gecen uygulama dosyasi  gecis icin dokunulan dosya
bagli                                      5                           5
arayuzlu                                   0                           1
dokunulan dosyalar (arayuzlu): arayuzlu/baglanti.mjs
```

#### Beş Dosya ile Bir Dosya

İki yerleşim de geçişten sonra aynı özeti veriyor: `gunluk=5 olcut=5 iz=5`. Davranış eşit, maliyet
değil. Bağlı yerleşimde beş dosya değişti, arayüzlü yerleşimde bir. Değişen tek dosyanın adı da
basılıyor: `baglanti.mjs`. Beş servis dosyasının hiçbirine dokunulmadı — geçişten önceki ve sonraki
hâlleri bayt bayt aynı.

Orta sütun farkın nedenini veriyor. Bağlı yerleşimde toplayıcı adı beş uygulama dosyasında geçiyor,
arayüzlü yerleşimde sıfırında. Dokunulan dosya sayısı bunun doğrudan sonucudur: bir ad kaç yerde
yazılıysa, o adı değiştirmek o kadar yere dokunmayı gerektirir. Arayüzün yaptığı şey adı tek bir
yere hapsetmektir; yaptığı başka bir şey yoktur.

Sayının nasıl büyüdüğü de ölçünün parçası. Beş servis bugün beş dosya demek; sistem büyüdükçe bu
sayı servis sayısıyla birlikte artar ve her artış aynı geçişi bir kat daha pahalı yapar. Arayüzlü
yerleşimde ise sayı servis sayısından bağımsızdır: elli servis olsa da bağlama noktası birdir. Bu,
önceki derslerdeki ölçüt alanının istek sayısından bağımsız olmasıyla aynı biçimdedir — maliyet
büyüyen bir şeye değil, sabit bir şeye bağlanmıştır.

Arayüzün bedeli de ortadadır ve küçümsenmemelidir. İki dosya fazladan yazıldı (`olcum.mjs` ve
`baglanti.mjs`), uygulama kodu ile toplayıcı arasına bir dolaylılık girdi ve arayüzün kendisi bir
karar hâline geldi: `topla` yalnız `tur` alanı olan bir nesne alıyor, dolayısıyla bir toplayıcının
ihtiyaç duyduğu bir bilgi bu biçime sığmıyorsa arayüz değişmek zorunda. Arayüz değişikliği ise
uygulama dosyalarına geri döner — kazanılan bağımsızlık, arayüzün doğru çizildiği ölçüde
bağımsızlıktır.

Bir sınır daha var. Geçiş burada bir satırın değiştirilmesiyle yapıldı, çünkü ölçülen şey
değişikliğin **yayılma yüzeyi**ydi. Toplayıcının çalışma anında seçilmesi, birden çok toplayıcıya
aynı anda yazılması ya da bir toplayıcı ayakta değilken olayların ne olacağı bu dersin ölçüsünün
dışındadır; bunların hiçbiri dosya sayısını değiştirmez.

#### Özet

- Arayüz iki işlemden ibaret: `topla` bir olay alır, `ozet` durumu döndürür; iki gerçekleştirme
  aynı olayları biri diske yazarak, öteki yalnız sayarak karşıladı.
- Geçişten sonra iki yerleşim de aynı özeti verdi (`gunluk=5 olcut=5 iz=5`); fark davranışta değil,
  değişikliğin maliyetindeydi.
- Toplayıcı adı bağlı yerleşimde 5 uygulama dosyasında geçiyor, arayüzlü yerleşimde 0'ında.
- Aynı geçiş bağlı yerleşimde 5 dosyaya, arayüzlü yerleşimde 1 dosyaya dokundu; beş servis dosyası
  geçiş öncesi ve sonrası bayt bayt aynı kaldı.
- Bağlı yerleşimde maliyet servis sayısıyla büyür, arayüzlü yerleşimde sabittir; karşılığında iki
  dosyalık dolaylılık ve arayüzün kendisinin bir karara dönüşmesi vardır.

#### Sonraki Adım

Üç sinyal artık kodda üretiliyor, biri diğerinin yerini almıyor ve hiçbiri toplayıcıya bağlı değil.
Ama hâlâ üç ayrı yerde duruyorlar: bir soru sorulduğunda günlük ayrı, ölçüt ayrı, iz ayrı okunuyor
ve aralarındaki bağ her seferinde elle kuruluyor. İkinci ders bağıntı kimliğini üretti, dördüncü
ders iz bağlamını sınırdan geçirdi; ikisi de aynı isteği adlandırıyor, ama hiçbir kayıt üçünü bir
arada tutmuyor. Sonraki ders bunu yapar ve konuyu kapatır: üç sinyal tek olayda birleştirilir, bir
sorunun birleştirmeli ve birleştirmesiz kaç adımda ve kaç sinyalle yanıtlandığı karşılaştırılır.

### Bağıntılandırma

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/bagintilandirma

Üç sinyalin bağıntı kimliği üzerinden tek olayda birleştirilmesi: aynı sorunun birleştirmesiz ve birleştirmeli kaç adımda ve kaç sinyalle yanıtlandığı, birleştirmenin bir kez ödenen maliyeti ve örnekleme oranının üç sinyalli olay sayısına etkisi.

Üç sinyal de kodda üretiliyor, hiçbiri toplayıcıya bağlı değil, hepsi aynı bağıntı kimliğini
taşıyor. Ama hâlâ üç ayrı yerde duruyorlar. Bir soru sorulduğunda günlük ayrı, ölçüt ayrı, iz ayrı
okunuyor ve aralarındaki bağ her seferinde yeniden kuruluyor. Bu ders o bağı bir kez kurup saklar
ve iki yolun maliyetini karşılaştırır.

Sorulan soru üç parçalıdır ve üçü de ayrı bir sinyale düşer: bu istek **neden** başarısız oldu
(günlük), **nerede** yavaşladı (iz), bu yavaşlık **olağan mı** (ölçüt). Hedef, hem yavaş raftan
gelen hem ödenmemiş ücreti olan üyeye ait bir istektir; üç sorunun üçü de aynı anda geçerlidir.

#### Üç Sinyal, Tek Kimlik

Servis üç sinyali de yazar ve üçünde de aynı bağıntı kimliği vardır. İkinci dersin kapsanmayan
çağrı yeri burada kapatılmıştır: bildirim çağrısı da bağlamı taşır, dolayısıyla kopuk iz kalmaz.

**TL6 — histogramın kovaladığı nicelik, isteğin harcadığı bildirilen iş süresidir.** Gerekçe: duvar
saati süresi koşumdan koşuma değişir ve kova sınırına yakın istekler koşuma göre yer değiştirir;
bildirilen iş her koşumda aynıdır, dolayısıyla kova payları karşılaştırılabilir kalır. Duvar saati
süresi ayrıca iz aralığında durur ve "nerede yavaşladı" sorusunu o yanıtlar.

```js
// servis6.mjs — <ad> <port> [ardil...]; uc sinyal de ayni baginti kimligini tasir
import { createServer } from "node:http";
import { appendFileSync, writeFileSync } from "node:fs";
const [ad, port, ...ardil] = process.argv.slice(2);
const SINIR = [10, 45, 150, 400];                  // bildirilen is kovalari (ms)
const kova = new Array(SINIR.length + 1).fill(0);
const sayac = { istek: 0, hata: 0 };
let oran = 1, no = 0;
const karma = (s) => { let h = 2166136261; for (const c of s) { h ^= c.charCodeAt(0); h = Math.imul(h, 16777619) >>> 0; } return h; };
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));
const uc = (p, u) => `http://127.0.0.1:${p}/is?uye=${u.get("uye")}&nusha=${u.get("nusha")}`;
const sifirla = () => { writeFileSync(`gunluk-${ad}.txt`, ""); writeFileSync(`iz-${ad}.txt`, ""); };
sifirla();

createServer(async (istek, yanit) => {
  const u = new URL(istek.url, "http://y").searchParams;
  if (istek.url.startsWith("/oran")) {
    oran = Number(u.get("deger")); sayac.istek = 0; sayac.hata = 0; kova.fill(0); no = 0;
    sifirla(); yanit.end("tamam"); return;
  }
  if (istek.url === "/olcut") { yanit.end(JSON.stringify({ servis: ad, ...sayac, sinir: SINIR, kova })); return; }
  const uye = u.get("uye"), nusha = Number(u.get("nusha"));
  const gelen = istek.headers["x-iz"];
  const [izNo, ust, orn] = gelen ? gelen.split("|") : [`odunc-${nusha}`, "", ""];
  const ornek = gelen ? orn === "1" : karma(izNo) % 1000 < oran * 1000;
  const aralik = `${ad}${(no += 1)}`, t0 = performance.now();
  sayac.istek += 1;
  const kendiIs = ad === "katalog" && nusha % 5 === 0 ? 80 : 3;   // bildirilen is: kosumdan bagimsiz
  await bekle(kendiIs);
  let durum = ad === "ucretlendirme" && uye === "4023" ? 402 : 200, altIs = 0;
  for (const p of ardil) {
    const y = await fetch(uc(p, u), { headers: { "x-iz": `${izNo}|${aralik}|${ornek ? 1 : 0}` } });
    const govde = await y.json(); altIs += govde.is;
    if (y.status !== 200) durum = y.status;
  }
  const is = kendiIs + altIs, sure = Math.round(performance.now() - t0);
  if (durum !== 200) sayac.hata += 1;
  let k = 0; while (k < SINIR.length && is > SINIR[k]) k += 1; kova[k] += 1;
  appendFileSync(`gunluk-${ad}.txt`, JSON.stringify({ baginti: izNo, servis: ad, uye, nusha, durum }) + "\n");
  if (ornek) appendFileSync(`iz-${ad}.txt`,
    JSON.stringify({ iz: izNo, aralik, ust, servis: ad, sure, is, kova: k }) + "\n");
  yanit.statusCode = durum; yanit.end(JSON.stringify({ is, durum }));
}).listen(Number(port));
```

#### İki Yol

Sorgu aracı aynı soruyu iki kez yanıtlar. Birinci yolda üç depo ayrı ayrı okunur, her biri hedef
kimliğe göre süzülür ve üç sonuç elde hizalanır. İkinci yolda önce bütün kayıtlar bağıntı kimliğine
göre tek olayda toplanır, sonra soru o tek depodan sorulur. Adım tanımı bütün konuda olduğu gibidir:
bir dosya ya da uç okuma, bir ayrıştırma, bir süzme, bir hizalama birer adımdır.

```js
// sor6.mjs — <oran> [soru]; ayni soru once uc depodan, sonra birlesik olaydan yanitlanir
import { readFileSync } from "node:fs";
const SERVIS = ["odunc", "uyelik", "katalog", "ucretlendirme", "bildirim"];
const PORT = { odunc: 9601, uyelik: 9602, katalog: 9603, ucretlendirme: 9604, bildirim: 9605 };
const HEDEF = "odunc-15";                          // hem yavas raf hem odenmemis ucret
const [oran, kip] = process.argv.slice(2);
let adim = 0; const say = () => { adim += 1; };
const oku = (on, alan) => {                        // 5 dosya okuma + 1 ayristirma gecisi
  const k = SERVIS.flatMap((s) => { say(); return readFileSync(`${on}-${s}.txt`, "utf8").split("\n").filter(Boolean); });
  say(); return k.map((l) => JSON.parse(l)).map((r) => ({ ...r, anahtar: r[alan] }));
};
const olcutOku = async () => { const o = {}; for (const s of SERVIS) { say(); o[s] = await (await fetch(`http://127.0.0.1:${PORT[s]}/olcut`)).json(); } return o; };
const etiket = (m, k) => `[${k === 0 ? 0 : m.sinir[k - 1]},${k < m.sinir.length ? m.sinir[k] : "+"})`;

// birlestirmesiz: her sinyal ayri okunur, sonuclar elde hizalanir
adim = 0;
const g = oku("gunluk", "baginti"), iz = oku("iz", "iz"), m = await olcutOku();
const gh = g.filter((r) => r.anahtar === HEDEF); say();
const ih = iz.filter((r) => r.anahtar === HEDEF); say();
const kaynak = gh.find((r) => r.durum !== 200 && r.servis !== "odunc" && r.servis !== "uyelik");
const enUzun = ih.filter((r) => r.servis !== "odunc").sort((a, b) => b.sure - a.sure)[0];
const kokIz = ih.find((r) => r.servis === "odunc");
const pay = Math.round((100 * m.odunc.kova[kokIz.kova]) / m.odunc.istek);
say();
const yanit = `uye ${gh[0].uye}, durum ${gh[0].durum} (kaynak ${kaynak.servis}); en uzun ardil ` +
  `${enUzun.servis}; is ${etiket(m.odunc, kokIz.kova)} kovasinda, isteklerin %${pay}'si`;
if (kip === "soru") {
  console.log(`soru: ${HEDEF} istegi neden basarisiz oldu, nerede yavasladi, bu is olagan mi`);
  console.log(`${"yontem".padEnd(17)}${"adim".padStart(5)}${"sinyal".padStart(8)}  yanit`);
  console.log(`${"birlestirmesiz".padEnd(17)}${String(adim).padStart(5)}${"3".padStart(8)}  ${yanit}`);
}

// birlestirme: uc sinyal baginti kimligine gore tek olayda toplanir
adim = 0;
const g2 = oku("gunluk", "baginti"), iz2 = oku("iz", "iz"), m2 = await olcutOku();
const olay = new Map();
for (const r of g2) {
  if (!olay.has(r.anahtar)) olay.set(r.anahtar, { baginti: r.anahtar, sinyal: 2, aralik: 0 });
  const o = olay.get(r.anahtar);
  if (r.servis === "odunc") { o.uye = r.uye; o.durum = r.durum; }
  if (r.durum !== 200 && r.servis !== "odunc" && r.servis !== "uyelik") o.kaynak = r.servis;
}
for (const r of iz2) {
  const o = olay.get(r.anahtar); o.aralik += 1;
  if (o.sinyal === 2) o.sinyal = 3;
  if (r.servis === "odunc") { o.kova = etiket(m2.odunc, r.kova); o.pay = Math.round((100 * m2.odunc.kova[r.kova]) / m2.odunc.istek); }
  else if (!o.enUzun || r.sure > o.enUzunSure) { o.enUzun = r.servis; o.enUzunSure = r.sure; }
}
say();
const birlestirme = adim;
adim = 0; say(); const o = olay.get(HEDEF); say();
if (kip === "soru") {
  console.log(`${"birlestirmeli".padEnd(17)}${String(adim).padStart(5)}${"1".padStart(8)}  ` +
    `uye ${o.uye}, durum ${o.durum} (kaynak ${o.kaynak}); en uzun ardil ${o.enUzun}; ` +
    `is ${o.kova} kovasinda, isteklerin %${o.pay}'si`);
  console.log(`birlestirme maliyeti: ${birlestirme} adim, ${g2.length + iz2.length} kayit -> ` +
    `${olay.size} olay (bir kez odenir, her sorguda degil)`);
  console.log(`\n${"oran".padStart(5)}${"olay".padStart(6)}${"3 sinyalli".padStart(12)}${"2 sinyalli".padStart(12)}  eksik sinyal`);
}
const uc = [...olay.values()].filter((x) => x.sinyal === 3).length;
console.log(`${oran.padStart(5)}${String(olay.size).padStart(6)}${String(uc).padStart(12)}` +
  `${String(olay.size - uc).padStart(12)}  ${olay.size - uc === 0 ? "-" : "iz (orneklenmeyen istek)"}`);
```

```bash
cat > kos.mjs <<'EOF'
const UYE = ["4021", "4022", "4023", "4024"];      // 4023 odenmemis ucreti olan uye
const PORT = [9601, 9602, 9603, 9604, 9605];
const oran = process.argv[2];
for (const p of PORT) await fetch(`http://127.0.0.1:${p}/oran?deger=${oran}`);
for (let i = 1; i <= 100; i += 1)
  await fetch(`http://127.0.0.1:9601/is?uye=${UYE[(i - 1) % 4]}&nusha=${i}`);
EOF
node servis6.mjs ucretlendirme 9604 &
node servis6.mjs katalog 9603 &
node servis6.mjs bildirim 9605 &
node servis6.mjs uyelik 9602 9604 &
node servis6.mjs odunc 9601 9602 9603 9605 &
sleep 1

node kos.mjs 1.00 && node sor6.mjs 1.00 soru
node kos.mjs 0.50 && node sor6.mjs 0.50
pkill -f "node servis6.mjs"
```

```
soru: odunc-15 istegi neden basarisiz oldu, nerede yavasladi, bu is olagan mi
yontem            adim  sinyal  yanit
birlestirmesiz      20       3  uye 4023, durum 402 (kaynak ucretlendirme); en uzun ardil katalog; is [45,150) kovasinda, isteklerin %20'si
birlestirmeli        2       1  uye 4023, durum 402 (kaynak ucretlendirme); en uzun ardil katalog; is [45,150) kovasinda, isteklerin %20'si
birlestirme maliyeti: 18 adim, 1000 kayit -> 100 olay (bir kez odenir, her sorguda degil)

 oran  olay  3 sinyalli  2 sinyalli  eksik sinyal
 1.00   100         100           0  -
 0.50   100          49          51  iz (orneklenmeyen istek)
```

#### Yirmi Adım ile İki Adım

İki yanıt kelimesi kelimesine aynı. Fark maliyettedir: birleştirmesiz yol yirmi adım harcadı ve üç
sinyale ayrı ayrı gitti, birleştirmeli yol iki adım harcadı ve tek bir olaya baktı. Yirmi adımın
dağılımı da öğreticidir — on beşi yalnız okuma (beş günlük dosyası, beş iz dosyası, beş ölçüt ucu),
ikisi ayrıştırma, üçü süzme ve hizalama. Soru üç parçalı olduğu için üç depoya da gitmek zorunluydu.

Birleştirme bu işi ortadan kaldırmıyor, **bir kere** yapıyor. On sekiz adımlık maliyet çıktıda ayrıca
duruyor ve şu cümleyle işaretli: bir kez ödenir, her sorguda değil. Bin kayıt okunup yüz olaya
indirgendi. İkinci soru sorulduğunda birleştirmesiz yol yine yirmi adım harcayacak, birleştirmeli yol
yine iki. Bağıntılandırmanın kazancı tek bir sorguda değil, sorgu sayısında büyür.

Birleştirmenin mümkün olmasını sağlayan tek şey, üç sinyalin de aynı kimliği taşımasıdır. O kimlik
ikinci derste günlüğe, dördüncü derste iz bağlamına girdi; ölçüt ise kimlik taşımıyor ve taşıyamaz —
üçüncü ders bunu kimliğin toplamın içinde kaybolması olarak ölçmüştü. Ölçütün olaya katılma biçimi
bu yüzden farklıdır: tekil istek ölçüte bağlanamaz, ama isteğin **hangi kovaya** düştüğü ve o
kovanın popülasyondaki payı bağlanabilir. Birleşik olayın son alanı budur ve "bu yavaşlık olağan mı"
sorusunu yanıtlayan tek şeydir: 15 numaralı isteğin işi, isteklerin yüzde yirmisinin düştüğü kovada.
Tek başına iz "seksen milisaniye katalogda geçti" der, tek başına ölçüt "isteklerin yüzde yirmisi bu
kovada" der; ikisi birleşince "bu istek, olağan olan yavaş yüzde yirmiden biri" olur.

Alt tablo birleştirmenin sınırını veriyor. Tam örneklemede yüz olayın yüzü de üç sinyal taşıyor.
Örnekleme oranı yarıya inince olay sayısı değişmiyor — günlük her istek için yazıldığı için yüz olay
duruyor — ama elli biri yalnız iki sinyal taşıyor: izleri hiç yazılmamış. Bu olaylarda "neden" ve
"olağan mı" sorulabiliyor, "nerede" sorulamıyor. Dördüncü dersin örnekleme ölçüsü burada ikinci kez
görünüyor: seyreltilen şey iz kayıtlarının hacmi, kaybedilen şey birleşik olayların çözünürlüğü.

Üç sinyalin bölüşümü de son hâlini alıyor. **Günlük** her istekte yazılır, kimlik taşır ve "kim,
ne, neden" sorularını yanıtlar; maliyeti istek sayısıyla büyür. **İz** örneklenen isteklerde yazılır,
kimlik ve yapı taşır, "nerede" sorusunu yanıtlar; maliyeti örnekleme oranıyla ayarlanır. **Ölçüt**
kimlik taşımaz, sabit alan sayısı tutar ve "olağan mı, ne kadar sık" sorularını yanıtlar. Üçü aynı
kimlikte buluşunca tek bir kayıttan üç ayrı soruya yanıt çıkıyor.

#### Özet

- Üç sinyal de aynı bağıntı kimliğini taşıyınca tek olayda birleştirilebildi: 1000 kayıt 100 olaya
  indi.
- Aynı soru birleştirmesiz 20 adım ve 3 sinyal, birleştirmeli 2 adım ve 1 sinyal harcadı; iki yanıt
  birebir aynı çıktı.
- Birleştirmenin 18 adımlık maliyeti bir kez ödeniyor; kazanç tek sorguda değil, sorgu sayısında
  birikiyor.
- Ölçüt kimlik taşımadığı için olaya tekil olarak değil, isteğin düştüğü kova ve o kovanın payı
  olarak katıldı — "bu yavaşlık olağan mı" sorusunu yanıtlayan tek alan bu oldu.
- Örnekleme birleştirmenin çözünürlüğünü sınırlıyor: oran 0,50'ye inince 100 olayın 51'i iz
  sinyalinden yoksun kaldı ve "nerede" sorusu o olaylarda kapandı.

#### Sonraki Adım

Konu burada kapanıyor. Bir isteğin yolu artık görünüyor: kim istedi, nerede yavaşladı, hangi serviste
hata doğdu, bu istek popülasyonun neresine düşüyor — hepsi tek kayıttan, iki adımda. Beş süreç
arasında kaybolan istek diye bir şey kalmadı.

Kalan şey şu: bu kayıtların hiçbiri isteğin **iyi mi kötü mü** olduğunu söylemiyor. Ölçüm 15
numaralı isteğin işinin isteklerin yüzde yirmisinin düştüğü kovada olduğunu söylüyor; o yüzde
yirminin kabul edilebilir olup olmadığını söylemiyor. Ücretlendirme servisinin 402 döndürdüğünü
söylüyor; yüz istekte yirmi beş hatanın normal mi felaket mi olduğunu söylemiyor. Telemetri
**gözlem** üretir, **karar** üretmez; kararın dayanacağı şey henüz kurulmadı. Sonraki ders o eksiği
kapatır: ölçülen sinyalden bir gösterge tanımlanır, göstergeye bir hedef bağlanır ve hedefin dışarıya
verilen sözle ilişkisi kurulur.

## Servis Düzeyi Yönetimi

Telemetriden karara giden yol: gösterge, hedef ve anlaşma kavramlarının ayrımı, gecikme, trafik, hata ve doygunluktan oluşan altın sinyallerin uygulama kodunda hesaplanması, hata bütçesinin değişim hızıyla kararlılığı dengelemesi, belirti tabanlı uyarı tasarımının gürültüyü azaltması ve canlılık ile hazır olma uçlarının ayrılması.

### Gösterge, Hedef ve Anlaşma

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/gosterge-hedef-ve-anlasma

Telemetrinin karara çevrilmesi: gösterge, hedef ve anlaşmanın ayrı cümleler olduğunun gösterilmesi, ödünç sisteminin otuz günlük istek kaydından istek, dakika ve üye tabanlı üç gösterge tanımının hesaplanması ve aynı dönemin on iki farklı sayıyla anlatılabildiğinin sayılması.

Önceki konu üç sinyali tek bir olayda birleştirdi: bir isteğin hangi süreçten geçtiği, nerede
beklediği, nerede kaybolduğu artık tek bir bağıntı kimliğiyle okunabiliyor. Eksik olan tek şey, o
kaydın **iyi mi kötü mü** olduğunu söyleyen cümledir. Telemetri gözlem üretir, karar üretmez. Ödünç
servisinin bir isteği 380 milisaniyede yanıtladığını bilmek, o isteğin başarılı sayılıp
sayılmayacağını söylemez.

Bu ders karar katmanının ilk taşını koyar. Birbirine karıştırılan üç kavram ayrı ayrı tanımlanır,
sonra aynı telemetriden üç ayrı gösterge tanımı çıkarılır ve aynı otuz günün kaç farklı sayıyla
anlatılabildiği sayılır.

#### Üç Kavram, Üç Ayrı Cümle

**Gösterge** (service level indicator) ölçülen bir orandır. Telemetriden hesaplanır ve tek başına
hiçbir taahhüt taşımaz: "ödünç sisteminin isteklerinin yüzde kaçı iyi" bir göstergedir.

**Hedef** (service level objective) o göstergenin üzerine konan iç taahhüttür ve üç parçadan
oluşur: bir tanım, bir pencere, bir sayı. "Otuz günlük pencerede istek tabanlı gösterge yüzde
99,5'in altına düşmez" bir hedeftir. Karşılığında kimseye para ödenmez, ama hedef aşıldığında
ekibin ne yapacağı önceden yazılıdır.

**Anlaşma** (service level agreement) dışarıya verilen taahhüttür ve bir yaptırım taşır: ihlal
edilirse ücret iadesi, üyelik uzatması ya da başka bir bedel doğar. Anlaşma her zaman hedeften
gevşektir, çünkü hedef aşıldıktan sonra düzeltmek için zaman kalması gerekir.

Üçünü ayıran soru şudur: gösterge "ne ölçülüyor", hedef "ne zaman müdahale edilir", anlaşma "ihlal
edilirse ne ödenir" sorusunu yanıtlar. Üçü de aynı telemetriden beslenir. Bu yüzden gösterge tanımı
değiştiğinde diğer ikisi de anlamını değiştirir ve bu dersin ölçtüğü şey tam olarak budur.

#### Otuz Günlük Kayıt

Ölçü, bölünmüş ödünç sisteminin istek kaydından çıkar. Aşağıdaki üreteç beş servisin otuz günlük
isteklerini tohumlu tam sayı aritmetiğiyle yazar; dört bozulma penceresi ve bir planlı bakım
penceresi bilinen takvimlerle yerleştirilir.

```js
// slo/telemetri.mjs — bolunmus odunc sisteminin 30 gunluk istek kaydi. Tohumlu tam sayi
// aritmetigiyle uretilir. Kayit bicimi: [dakika, servis, durum, sure_ms, uye, tur].
export const DK = 30 * 1440, UYE = 4000, TOHUM = 20260731;
export const SERVIS = ["katalog", "uyelik", "odunc", "bildirim", "ucret"];
const TABAN = [40, 25, 60, 30, 45];
// SD1 (varsayim): dakikadaki istek sayisi gun ritmine baglidir (08-22 arasi 40, disinda 10).
// SD2 (varsayim): dort bozulma [gun, saat, sure dk, servis (-1 hepsi), kip, siddet (binde)].
export const OLAY = [
  [4, 8, 90, 2, "hata", 60], [11, 14, 240, 0, "yavas", 1000],
  [19, 2, 30, -1, "hata", 600], [26, 20, 120, 3, "hata", 250],
];
// SD3 (varsayim): planli bakim penceresi 15. gunun 03:00-04:00 dilimi.
export const BAKIM = [15 * 1440 + 180, 60];

export function kayitlar() {
  let s = TOHUM;
  const rast = (n) => Math.floor(((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32) * n);
  const k = [];
  for (let t = 0; t < DK; t += 1) {
    const saat = Math.floor(t / 60) % 24;
    const bakim = t >= BAKIM[0] && t < BAKIM[0] + BAKIM[1];
    for (let i = 0, n = saat >= 8 && saat < 22 ? 40 : 10; i < n; i += 1) {
      const sv = rast(5);
      let durum = 200, sure = TABAN[sv] + rast(60);
      if (rast(1000) < 8) durum = 404;
      if (rast(1000) < 1) durum = 500;
      if (rast(1000) === 0) sure += 400 + rast(600);
      for (const [g, sa, sr, sd, kip, si] of OLAY) {
        const bas = g * 1440 + sa * 60;
        if (t < bas || t >= bas + sr || (sd >= 0 && sd !== sv)) continue;
        if (kip === "hata" && rast(1000) < si) durum = 500;
        if (kip === "yavas") sure += 300 + rast(si);
      }
      if (bakim) durum = 503;
      k.push([t, SERVIS[sv], durum, sure, rast(UYE), rast(50) === 0 ? "tarama" : "insan"]);
    }
  }
  return k;
}

if (import.meta.filename === process.argv[1]) {
  const K = kayitlar();
  const say = (f) => K.filter(f).length;
  console.log(`tohum ${TOHUM}, ${DK} dakika, ${K.length} istek, ${UYE} uye`);
  console.log(`durum 5xx ${say((r) => r[2] >= 500)}, durum 4xx ${say((r) => r[2] >= 400 && r[2] < 500)}, ` +
    `sure > 400 ms ${say((r) => r[3] > 400)}, tarama ${say((r) => r[5] === "tarama")}`);
}
```

```
tohum 20260731, 43200 dakika, 1188000 istek, 4000 uye
durum 5xx 2214, durum 4xx 9457, sure > 400 ms 3087, tarama 23868
```

#### Aynı Telemetriden Üç Gösterge

Kötü isteğin tanımı üç gösterge için de ortaktır: durum kodu 400 ve üstü olan ya da 400
milisaniyeyi aşan istek kötüdür. Değişen şey **neyin bölünen, neyin bölen olduğudur**.

- **İstek tabanlı**: iyi istek sayısı bölü toplam istek sayısı.
- **Dakika tabanlı**: bir dakika, kötü oranı yüzde 5'in altındaysa iyidir; gösterge iyi dakika
  sayısı bölü dolu dakika sayısı.
- **Üye tabanlı**: bir üye-günü, o üyenin o günkü isteklerinin en çok yüzde 1'i kötüyse iyidir;
  gösterge günlük iyi üye oranının otuz günlük ortalamasıdır.

Buna dört süzgeç eklenir. Süzgeç, göstergenin **neyi saymadığını** söyler: tarama istekleri,
istemci hatası olan istekler ve planlı bakım penceresi sırayla dışarıda bırakılır.

```js
// slo/gosterge.mjs — ayni telemetriden uc gosterge tanimi ve dort suzgec. Kotu istek:
// durum 400 ve ustu ya da sure 400 ms ustu. Uc tanimin sonucu %99,5 hedefiyle karsilastirilir.
import { kayitlar, DK, UYE, BAKIM } from "./telemetri.mjs";
const K = kayitlar(), HEDEF = 0.995, y = (x) => `%${(x * 100).toFixed(3)}`;
const kotu = (r) => r[2] >= 400 || r[3] > 400;

const istekTabanli = (ks) => 1 - ks.filter(kotu).length / ks.length;

function dakikaTabanli(ks) {                  // bir dakika: kotu orani %5 altindaysa iyi
  const top = new Int32Array(DK), kot = new Int32Array(DK);
  for (const r of ks) { top[r[0]] += 1; if (kotu(r)) kot[r[0]] += 1; }
  let iyi = 0, dolu = 0;
  for (let t = 0; t < DK; t += 1) if (top[t] > 0) { dolu += 1; if (kot[t] <= top[t] * 0.05) iyi += 1; }
  return iyi / dolu;
}

function uyeTabanli(ks) {                     // bir uye-gunu: kotu orani %1 altindaysa iyi
  let toplam = 0, istek = 0, gunSay = 0;
  for (let g = 0; g < 30; g += 1) {
    const top = new Int32Array(UYE), kot = new Int32Array(UYE);
    for (const r of ks) {
      if (Math.floor(r[0] / 1440) !== g) continue;
      top[r[4]] += 1; if (kotu(r)) kot[r[4]] += 1;
    }
    let etkin = 0, iyi = 0, n = 0;
    for (let u = 0; u < UYE; u += 1) if (top[u] > 0) {
      etkin += 1; n += top[u]; if (kot[u] <= top[u] * 0.01) iyi += 1;
    }
    toplam += iyi / etkin; istek += n / etkin; gunSay += 1;
  }
  return [toplam / gunSay, istek / gunSay];
}

const SUZGEC = [
  ["ham", () => true],
  ["tarama haric", (r) => r[5] !== "tarama"],
  ["+ istemci hatasi haric", (r) => r[5] !== "tarama" && (r[2] < 400 || r[2] >= 500)],
  ["+ bakim haric", (r) => r[5] !== "tarama" && (r[2] < 400 || r[2] >= 500)
    && (r[0] < BAKIM[0] || r[0] >= BAKIM[0] + BAKIM[1])],
];

console.log(`${"suzgec".padEnd(24)}${"istek".padStart(9)}${"elenen".padStart(8)}` +
  `${"istek tabanli".padStart(15)}${"dakika tabanli".padStart(16)}${"uye tabanli".padStart(13)}` +
  `${"hedefi gecen".padStart(14)}`);
let uyeIstek = 0;
for (const [ad, f] of SUZGEC) {
  const ks = K.filter(f), [ut, n] = uyeTabanli(ks);
  const u = [istekTabanli(ks), dakikaTabanli(ks), ut];
  uyeIstek = n;
  console.log(`${ad.padEnd(24)}${String(ks.length).padStart(9)}` +
    `${String(K.length - ks.length).padStart(8)}` +
    `${u.map((v, i) => y(v).padStart([15, 16, 13][i])).join("")}` +
    `${`${u.filter((v) => v >= HEDEF).length}/3`.padStart(14)}`);
}
console.log(`hedef ${y(HEDEF)}; ayni 30 gun, ayni ${K.length} kayit, 12 sayi`);
console.log(`uye basina gunde ortalama ${uyeIstek.toFixed(1)} istek: ` +
  `%1'lik uye esigi ${Math.floor(uyeIstek * 0.01)} kotu istege izin verir`);
```

```
suzgec                      istek  elenen  istek tabanli  dakika tabanli  uye tabanli  hedefi gecen
ham                       1188000       0        %98.760         %94.655      %88.763           0/3
tarama haric              1164132   23868        %98.761         %93.074      %88.980           0/3
+ istemci hatasi haric    1154873   33127        %99.553         %98.123      %96.089           1/3
+ bakim haric             1154280   33720        %99.604         %98.259      %96.540           1/3
hedef %99.500; ayni 30 gun, ayni 1188000 kayit, 12 sayi
uye basina gunde ortalama 9.6 istek: %1'lik uye esigi 0 kotu istege izin verir
```

#### Tanım Neyi Sayıyor

Tek bir kayıt değişmeden aynı otuz gün on iki sayı üretti ve bunlar yüzde 88,763 ile yüzde 99,604
arasına yayıldı — 10,8 puanlık bir aralık. Yüzde 99,5 hedefi on iki sayının yalnız ikisini geçiyor.
"Hedefi tutturduk" cümlesi, tanım yazılmadan doğrulanamaz.

**Elenen kayıt sayısı ile göstergeye etkisi arasında ilişki yok.** Tarama istekleri 23.868 kayıt
eledi ve istek tabanlı göstergeyi yüzde 98,760'tan yüzde 98,761'e taşıdı; istemci hatası 9.259
kayıt eledi ve aynı göstergeyi 0,79 puan sıçrattı. Belirleyici olan elenen kaydın sayısı değil,
kötü olup olmadığıdır. Bakım penceresinin 593 kaydı 0,05 puan getirdi; küçük bir sayı, ama hedefe
uzaklık 0,05 puana indiğinde bu kayıtların hariç tutulup tutulmadığı sonucu belirler.

Tarama isteklerini elemek dakika tabanlı göstergeyi **düşürdü** (yüzde 94,655'ten yüzde 93,074'e).
Elenen istekler kötü olmadığı için bu ters görünür; nedeni bölendedir. Kırk isteklik bir dakikada
iki kötü istek tam yüzde 5 eder ve dakika iyi sayılır. Bir tarama isteği elenince bölen 39 olur,
aynı iki istek yüzde 5,1 eder ve dakika kötüye döner. **İyi istekleri elemek oran tabanlı bir
göstergeyi bozar**; süzgeç, kötü istekleri elediğinde göstergeyi yükseltir, iyi istekleri
elediğinde düşürür.

Üye tabanlı gösterge üçünün en sertidir: istek tabanlı yüzde 99,553 derken o yüzde 96,089 diyor.
Sertliğin kaynağı eşik değil bölendir. Üye başına günde ortalama 9,6 istek düşüyor ve yüzde 1'lik
eşik bu bölende sıfır kötü isteğe izin veriyor; yani tanım pratikte "üye o gün hiç kötü istek
görmesin" hâline geliyor. Üç tanım aynı olayın üç ayrı yüzünü ölçer: istek tabanlı olayın
**hacmini**, dakika tabanlı **süresini**, üye tabanlı **yayılımını**.

#### Hedef ile Anlaşma Arasındaki Boşluk

Hedef, tanımıyla birlikte yazılmadıkça ölçülemez. "Yüzde 99,5" tek başına yukarıdaki on iki sayıdan
hangisine bakıldığını söylemez ve bakılan sayıya göre aynı ay hem tutturulmuş hem kaçırılmış olur.
Yazılı hedefin üç parçası — tanım, pencere, sayı — üçü birden bulunmak zorundadır.

Anlaşma bir adım daha ister: **karşı taraf da aynı sayıyı hesaplayabilmelidir.** Üye tabanlı bir
gösterge üzerine anlaşma yazılamaz, çünkü üye kendi üye-günlerini görmez. Bu yüzden anlaşma en kaba
ve en doğrulanabilir tanımı kullanır: istek tabanlı, tarama ve istemci hatası hariç, bakım
penceresi hariç. Ödünç sisteminde anlaşma yüzde 99,0'a, hedef yüzde 99,5'e konursa aradaki 0,5
puan müdahale alanıdır; hedef aşıldığında yaptırım henüz doğmamıştır ve düzeltmek için ay içinde
yer kalır.

Bu ayrımın kodda görünen karşılığı `SUZGEC` dizisidir. Anlaşma metnindeki her hariç tutma cümlesi
o dizide bir satırdır: dört süzgeç seviyesi, üç hariç tutma kuralı. Metinde yazan ile dizide duran
ayrışırsa taahhüt edilen sayı ile ölçülen sayı farklı olur — bakım penceresinin 593 kaydı metinde
hariç tutulmamışsa, ölçüm doğru çalışırken anlaşma ihlal edilmiş görünür.

#### Özet

- Gösterge ölçülen orandır, hedef o oran üzerine konan iç taahhüttür, anlaşma yaptırımı olan dış
  taahhüttür; anlaşma her zaman hedeften gevşektir.
- Aynı otuz günlük 1.188.000 kayıttan üç tanım ve dört süzgeçle on iki sayı çıktı: yüzde 88,763 ile
  yüzde 99,604 arası ve yüzde 99,5 hedefini bunların yalnız ikisi geçiyor.
- Hariç tutmanın etkisi elenen kayıt sayısıyla değil, elenen kayıtların kötü olup olmadığıyla
  ölçülür: 23.868 tarama kaydı göstergeyi 0,001 puan, 9.259 istemci hatası 0,79 puan değiştirdi.
- İyi istekleri elemek dakika tabanlı göstergeyi düşürür, çünkü böleni küçültür: yüzde 94,655'ten
  yüzde 93,074'e.
- Üye tabanlı gösterge, üye başına günde 9,6 istek düşerken yüzde 1'lik eşiği sıfır toleransa
  çevirir; üç tanım aynı olayın hacmini, süresini ve yayılımını ölçer.
- Anlaşmanın hariç tutma listesi koda dönüşür; süzgeç dizisi ile anlaşma metni ayrışırsa ölçülen
  sayı ile taahhüt edilen sayı da ayrışır.

#### Sonraki Adım

Buradaki üç tanım da tek bir "kötü istek" ölçütü üzerine kuruldu: durum kodu ya da süre. Oysa
ödünç sisteminde bir isteğin kötüye gitmesinin tek yolu bu değil. Katalog servisi hiç hata
döndürmeden yavaşlayabilir, bildirim servisi hatasız çalışırken kuyruğu doldurabilir, ücret
servisine hiç istek gelmemesi tek başına bir arıza belirtisi olabilir. Bunların hepsi aynı
telemetride yazılıdır, ama tek bir orana bakan gösterge bir kısmını göremez. Sonraki ders aynı
kayıtlardan dört ayrı sinyal hesaplar ve her sinyalin **tek başına hangi olay sınıfını kaçırdığını**
sayar.

### Altın Sinyaller

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/altin-sinyaller

Gecikme, trafik, hata ve doygunluğun ödünç sisteminin yedi günlük kayıtlarından hesaplanması: altı olay sınıfı üzerinde her sinyalin tek başına kaçırdığı olayların sayılması, doygunluğun hatadan kaç dakika önce tetiklediğinin ölçülmesi ve dört sinyalin birlikte bile göremediği sessiz kaybın gösterilmesi.

Önceki ders üç gösterge tanımını tek bir kötü istek ölçütü üzerine kurdu: durum kodu ya da süre.
Ödünç sisteminde bir isteğin kötüye gitmesinin tek yolu bu değil. Katalog servisi hiç hata
döndürmeden yavaşlayabilir, bildirim kuyruğu hatasız çalışırken dolabilir, ücret servisine hiç
istek gelmemesi tek başına bir arıza belirtisi olabilir. Bunların hepsi aynı telemetride yazılıdır,
ama tek bir orana bakan gösterge bir kısmını göremez.

Bu ders aynı kayıtlardan dört sinyal hesaplar, altı ayrı olay sınıfının üzerinden geçirir ve her
sinyalin **tek başına neyi kaçırdığını** sayar.

#### Dört Sinyal, Dört Ayrı Soru

- **Gecikme**: bir isteğin ne kadar sürdüğü. Sorusu "yavaş mı".
- **Trafik**: birim zamanda kaç istek geldiği. Sorusu "yük ne".
- **Hata**: kaç isteğin başarısız döndüğü. Sorusu "bozuk mu".
- **Doygunluk**: sistemin en kıt kaynağının ne kadarının dolu olduğu. Sorusu "ne kadar yerim kaldı".

Dördü bir arada seçilir çünkü ilk üçü **olmuş olanı**, dördüncüsü **olacak olanı** anlatır. Bu
sinyalin adı Sistem Tasarımına Giriş kursunda `doyum` olarak geçen kavramın aynısıdır; sinyal
listesinin katalog biçimi olduğu için burada `doygunluk` yazılır.

İlk üçü istek kaydından doğrudan çıkar. Doygunluk çıkmaz: kıt kaynağın ne olduğu uygulamaya
özgüdür ve **kodda açıkça ölçülmek zorundadır**. Bölünmüş ödünç sisteminde bu kaynak bildirim
kuyruğudur; her bildirim isteği kuyruğa yazılırken o anki derinliği kendi kaydına da yazar. Kuyruk
derinliği bir ölçerdir, sayaç değil: artar ve azalır.

#### Yedi Günlük Kayıt ve Altı Olay Sınıfı

```js
// sinyal/kayit.mjs — odunc sisteminin 7 gunluk istek kaydi ve bildirim kuyrugu olceri.
// Kayit: [dakika, servis, durum, sure_ms, kuyruk_derinligi, teslim(-1: bildirim disi)].
export const DK = 7 * 1440, TOHUM = 20260731, SINIR = 500, TUKETIM = 25;
export const SERVIS = ["katalog", "uyelik", "odunc", "bildirim", "ucret"];
const TABAN = [40, 25, 60, 30, 45];
// SD1 (varsayim): dakikada 08-22 arasi 100, disinda 30 istek; 0. gun olaysizdir (taban gunu).
// SD2 (varsayim): alti olay sinifi [gun, saat, sure dk, ad].
export const OLAY = [
  [1, 10, 60, "hata patlamasi"], [2, 14, 90, "sessiz yavaslama"],
  [3, 3, 120, "trafik cokusu"], [4, 15, 120, "kuyruk doygunlugu"],
  [5, 12, 30, "trafik sicramasi"], [6, 9, 180, "sessiz teslim kaybi"],
];
export const pencere = (i) => [OLAY[i][0] * 1440 + OLAY[i][1] * 60, OLAY[i][2]];

export function kayitlar() {
  let s = TOHUM;
  const rast = (n) => Math.floor(((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32) * n);
  const k = [];
  let kuyruk = 0;
  for (let t = 0; t < DK; t += 1) {
    const saat = Math.floor(t / 60) % 24;
    const ic = (i) => t >= pencere(i)[0] && t < pencere(i)[0] + OLAY[i][2];
    let n = saat >= 8 && saat < 22 ? 100 : 30;
    if (ic(2)) n = Math.round(n * 0.1);
    if (ic(4)) n = Math.round(n * 2.5);
    kuyruk = Math.max(0, kuyruk - (ic(3) ? 14 : TUKETIM));
    for (let i = 0; i < n; i += 1) {
      const sv = rast(5);
      let durum = 200, sure = TABAN[sv] + rast(60), teslim = -1;
      if (rast(1000) < 1) durum = 500;
      if (ic(0) && rast(1000) < 80) durum = 500;
      if (ic(1) && sv === 0) sure += 700 + rast(400);
      if (sv === 3 && durum === 200) {
        if (kuyruk >= SINIR) durum = 503;
        else { kuyruk += 1; teslim = rast(1000) < (ic(5) ? 970 : 4) ? 0 : 1; }
      }
      k.push([t, SERVIS[sv], durum, sure, kuyruk, teslim]);
    }
  }
  return k;
}

if (import.meta.filename === process.argv[1]) {
  const K = kayitlar();
  console.log(`tohum ${TOHUM}, ${DK} dakika, ${K.length} istek, kuyruk siniri ${SINIR}`);
  console.log(`olay siniflari: ${OLAY.map((o) => o[3]).join(", ")}`);
}
```

```
tohum 20260731, 10080 dakika, 715260 istek, kuyruk siniri 500
olay siniflari: hata patlamasi, sessiz yavaslama, trafik cokusu, kuyruk doygunlugu, trafik sicramasi, sessiz teslim kaybi
```

Altı olay sınıfı ayrı ayrı seçildi: biri yalnız durum kodunu, biri yalnız süreyi, biri yalnız istek
sayısını, biri yalnız kuyruğu kıpırdatır. Sonuncusunda bildirim istekleri 200 döner, süreleri
değişmez, kuyruk normal boşalır — ama gönderilen bildirim üyeye ulaşmaz.

#### Dördünü Hesaplamak

Her sinyal bir eşikle karara çevrilir. Gecikme ve trafik eşiği olaysız 0. günden türetilir, hata ve
doygunluk eşiği sabittir. Bir sinyalin olayı yakalamış sayılması için pencere içinde **üst üste üç
dakika** tetiklemesi gerekir; tek dakikalık tetik sayılmaz.

```js
// sinyal/altin.mjs — dort altin sinyalin ayni kayitlardan dakika dakika hesaplanmasi ve her
// sinyalin tek basina hangi olay sinifini kacirdiginin sayilmasi. Yakalama olcutu: pencere
// icinde ust uste 3 dakika tetiklemek.
import { kayitlar, DK, OLAY, pencere, SINIR } from "./kayit.mjs";
const K = kayitlar();
const say = new Int32Array(DK), hat = new Int32Array(DK), kuy = new Int32Array(DK);
const ms = Array.from({ length: DK }, () => []);
for (const [t, , durum, sure, kuyruk] of K) {
  say[t] += 1; ms[t].push(sure); kuy[t] = Math.max(kuy[t], kuyruk);
  if (durum >= 500) hat[t] += 1;
}
const p95 = (a) => [...a].sort((x, z) => x - z)[Math.floor(a.length * 0.95)];
const gec = new Int32Array(DK);
for (let t = 0; t < DK; t += 1) gec[t] = say[t] ? p95(ms[t]) : 0;

// SD3 (varsayim): gecikme ve trafik esigi 0. gunden turetilir, hata ve doygunluk esigi sabittir.
const P95 = p95(K.filter((r) => r[0] < 1440).map((r) => r[3])), st = new Float64Array(24);
for (let t = 0; t < 1440; t += 1) st[Math.floor(t / 60)] += say[t] / 60;
const T = {
  gecikme: (t) => gec[t] > 2 * P95,
  trafik: (t) => say[t] < 0.5 * st[Math.floor(t / 60) % 24] || say[t] > 2 * st[Math.floor(t / 60) % 24],
  hata: (t) => say[t] > 0 && hat[t] / say[t] > 0.01,
  doygunluk: (t) => kuy[t] / SINIR > 0.85,
};
const AD = Object.keys(T);
const ilk3 = (a, bas, sr) => {
  for (let t = bas, ard = 0; t < bas + sr; t += 1)
    if ((ard = T[a](t) ? ard + 1 : 0) === 3) return t - bas - 2;
  return -1;
};
console.log(`taban p95 ${P95} ms, gecikme esigi ${2 * P95} ms, hata esigi %1, ` +
  `doygunluk esigi ${0.85 * SINIR}/${SINIR}\n`);
console.log(`${"olay sinifi".padEnd(22)}${"sure".padStart(7)}` +
  `${AD.map((a) => a.padStart(14)).join("")}${"yakalayan".padStart(11)}`);
const tut = AD.map(() => new Set());
OLAY.forEach(([, , sr, ad], i) => {
  const [bas] = pencere(i);
  const s = AD.map((a) => {
    let n = 0;
    for (let t = bas; t < bas + sr; t += 1) if (T[a](t)) n += 1;
    return [n, ilk3(a, bas, sr)];
  });
  s.forEach(([, k], j) => { if (k >= 0) tut[j].add(i); });
  console.log(`${ad.padEnd(22)}${(sr + " dk").padStart(7)}` +
    `${s.map(([n, k]) => (k >= 0 ? `${n} dk / ${k}.dk` : "-").padStart(14)).join("")}` +
    `${String(s.filter(([, k]) => k >= 0).length).padStart(11)}`);
});
const ici = (t) => OLAY.some((o, i) => t >= pencere(i)[0] && t < pencere(i)[0] + o[2]);
console.log(`\n${"sinyal".padEnd(12)}${"yakaladigi".padStart(11)}${"olay disi tetik".padStart(17)}` +
  `  kacirdigi olay siniflari`);
AD.forEach((a, j) => {
  let d = 0;
  for (let t = 0; t < DK; t += 1) if (T[a](t) && !ici(t)) d += 1;
  console.log(`${a.padEnd(12)}${`${tut[j].size}/${OLAY.length}`.padStart(11)}${(d + " dk").padStart(17)}` +
    `  ${OLAY.filter((_, i) => !tut[j].has(i)).map((o) => o[3]).join(", ")}`);
});
const hepsi = new Set(tut.flatMap((s) => [...s]));
console.log(`\ndordu birlikte: ${hepsi.size}/${OLAY.length} olay sinifi; ` +
  `kacan: ${OLAY.filter((_, i) => !hepsi.has(i)).map((o) => o[3]).join(", ")}`);
const teslim = (a, b) => {
  const bs = K.filter((r) => r[5] >= 0 && r[0] >= a && r[0] < b);
  return `%${((100 * bs.filter((r) => r[5] === 1).length) / bs.length).toFixed(1)}`;
};
const [b5, s5] = [pencere(5)[0], OLAY[5][2]];
console.log(`teslim orani: olay oncesi ${teslim(b5 - s5, b5)}, olay sirasinda ${teslim(b5, b5 + s5)}`);
let dusuk = 0, disi = 0, ilkKuyruk = -1;
for (let t = 0; t < DK; t += 1) if (!ici(t)) {
  if (T.hata(t)) { disi += 1; if (say[t] <= 40) dusuk += 1; }
  if (T.doygunluk(t) && ilkKuyruk < 0) ilkKuyruk = t;
}
const [bd, sd] = [pencere(3)[0], OLAY[3][2]];
console.log(`hatanin olay disi ${disi} tetiginin ${dusuk} tanesi 40 istek/dk altindaki ` +
  `dakikalarda; doygunlugun olay disi ilk tetigi ${ilkKuyruk}. dakika, olay ${bd + sd}. dakikada bitti`);
console.log(`kuyruk doygunlugunda: doygunluk ${ilk3("doygunluk", bd, sd)}. dakikada, ` +
  `hata ${ilk3("hata", bd, sd)}. dakikada yakaladi`);
```

```
taban p95 105 ms, gecikme esigi 210 ms, hata esigi %1, doygunluk esigi 425/500

olay sinifi              sure       gecikme        trafik          hata     doygunluk  yakalayan
hata patlamasi          60 dk             -             -  60 dk / 0.dk             -          1
sessiz yavaslama        90 dk  90 dk / 0.dk             -             -             -          1
trafik cokusu          120 dk             - 120 dk / 0.dk             -             -          1
kuyruk doygunlugu      120 dk             -             - 43 dk / 72.dk 59 dk / 61.dk          2
trafik sicramasi        30 dk             -  30 dk / 0.dk 12 dk / 18.dk 15 dk / 15.dk          3
sessiz teslim kaybi    180 dk             -             -             -             -          0

sinyal       yakaladigi  olay disi tetik  kacirdigi olay siniflari
gecikme             1/6             0 dk  hata patlamasi, trafik cokusu, kuyruk doygunlugu, trafik sicramasi, sessiz teslim kaybi
trafik              2/6             0 dk  hata patlamasi, sessiz yavaslama, kuyruk doygunlugu, sessiz teslim kaybi
hata                3/6           162 dk  sessiz yavaslama, trafik cokusu, sessiz teslim kaybi
doygunluk           2/6            23 dk  hata patlamasi, sessiz yavaslama, trafik cokusu, sessiz teslim kaybi

dordu birlikte: 5/6 olay sinifi; kacan: sessiz teslim kaybi
teslim orani: olay oncesi %99.7, olay sirasinda %3.0
hatanin olay disi 162 tetiginin 136 tanesi 40 istek/dk altindaki dakikalarda; doygunlugun olay disi ilk tetigi 6780. dakika, olay 6780. dakikada bitti
kuyruk doygunlugunda: doygunluk 61. dakikada, hata 72. dakikada yakaladi
```

#### Her Sinyalin Kör Noktası

Hiçbir sinyal altı olayın dördünden fazlasını görmüyor. Gecikme altıda birini, trafik ile doygunluk
altıda ikisini, hata altıda üçünü yakalıyor. Bu sayılar sinyallerin kalitesiyle ilgili değil:
her biri **başka bir soruyu** yanıtladığı için başka bir olay sınıfını kaçırıyor.

En öğretici satır trafik çöküşüdür. İki saat boyunca istek sayısı onda birine düşüyor; hata oranı
bu sırada sıfır, gecikme taban değerinde, kuyruk boş. Yalnız hata oranına bakan bir gösterge o iki
saatte **yüzde yüz başarı** görür, çünkü gelen az sayıda isteğin hepsi başarılı dönmüştür. Bir üst
katmandaki üretici durduğunda ya da bir yönlendirme bozulduğunda görülen tablo tam olarak budur:
oran temizdir, çünkü bölen çökmüştür.

Kuyruk doygunluğu satırı sinyalin **zaman** değerini veriyor. Bildirim tüketicisi yavaşlıyor ve
kuyruk dakikada altı bildirim büyüyor. Doygunluk 61. dakikada üç dakikalık eşiğini geçiyor; kuyruk
sınıra dayanıp bildirim istekleri reddedilmeye başladığında hata sinyali de tetikliyor, ama bu 72.
dakikada oluyor. Aradaki **11 dakika**, arızanın üyeye ulaşmadan önce müdahale edilebildiği süredir.
Doygunluk ölçülmezse o on bir dakika kaybedilir ve olay ilk kez düşen isteklerle görünür.

Olay dışı tetikler iki ayrı şeyi gösteriyor. Hata sinyalinin 162 tetiğinin 136'sı dakikada 40'ın
altında istek alan dakikalarda: gece trafiğinde bölen 30 iken tek bir 5xx yüzde 3,3 eder ve yüzde
1'lik eşiği aşar. Oran tabanlı bir sinyal, bölen küçüldükçe gürültülenir — önceki dersin bölen
tartışması burada uyarı gürültüsü olarak geri döner. Doygunluğun 23 tetiği ise yanlış değil:
ilki tam olarak kuyruk doygunluğu olayının bittiği 6780. dakikada başlıyor ve kuyruğun boşalma
süresini ölçüyor. **Sinyalin penceresi olayın penceresiyle aynı olmak zorunda değildir.**

#### Dördünün Birlikte Göremediği

Dört sinyal birlikte altı olay sınıfının beşini kapsıyor. Kaçan altıncısı sessiz teslim kaybı:
bildirim istekleri 200 dönüyor, süreleri değişmiyor, kuyruk normal boşalıyor, hiçbir eşik
kıpırdamıyor. Aynı kayıtların içindeki teslim alanı ise olaydan önce yüzde 99,7, olay sırasında
yüzde 3,0 diyor.

Buradaki ayrım kalıcıdır: dört altın sinyal **taşımayı** ölçer, **doğruluğu** ölçmez. Bir isteğin
kabul edilmesi, hızlı yanıtlanması ve kaynak tüketmemesi, işin yapıldığı anlamına gelmez. Ödünç
sisteminde işin yapılmış olması "gecikme bildirimi üyeye ulaştı" demektir ve bunu ancak iş düzeyinde
bir sayaç söyler. Dört sinyal her servise aynı biçimde konur; beşinci sayaç her servis için ayrı
düşünülmek zorundadır ve bu yüzden çoğu zaman hiç yazılmaz.

#### Özet

- Dört altın sinyal dört ayrı soru sorar: gecikme "yavaş mı", trafik "yük ne", hata "bozuk mu",
  doygunluk "ne kadar yerim kaldı"; doygunluk Sistem Tasarımına Giriş kursundaki `doyum` ile aynı
  kavramdır.
- İlk üçü istek kaydından doğrudan çıkar; doygunluk çıkmaz ve kıt kaynak kodda ölçülmek zorundadır
  — burada her bildirim isteği kuyruk derinliğini kendi kaydına yazar.
- Altı olay sınıfında hiçbir sinyal tek başına üçten fazlasını yakalamadı: gecikme 1/6, trafik 2/6,
  doygunluk 2/6, hata 3/6.
- Trafik çöküşünde hata oranı sıfırdır ve gösterge kusursuz görünür; oran tabanlı sinyal bölen
  çöktüğünde kör kalır, bölen küçüldüğünde gürültülenir (162 olay dışı tetiğin 136'sı 40 istek/dk
  altındaki dakikalarda).
- Doygunluk, kuyruk dolarken 61. dakikada tetikledi; hata ancak istekler reddedilmeye başlayınca
  72. dakikada tetikledi — 11 dakikalık erken uyarı.
- Dördü birlikte altı sınıfın beşini kapsıyor; kaçan sınıf sessiz teslim kaybıdır (teslim oranı
  yüzde 99,7'den yüzde 3,0'a düştü), çünkü altın sinyaller taşımayı ölçer, doğruluğu ölçmez.

#### Sonraki Adım

Dört sinyal artık dakika dakika hesaplanıyor ve bir hedefle karşılaştırılabiliyor. Hedefin altına
düşüldüğünde ne olacağı ise hâlâ yazılı değil. Ödünç sistemine haftada iki yayın da yapılabilir, on
yayın da; her yayın kayıtlarda ölçülebilir bir bozulma bırakır ve hedefin altında kalan her dakika
bir yerden ödenir. Sonraki ders bu ödemeyi telemetriden hesaplar: aynı istek kayıtlarından bir
bütçe çıkarır, iki farklı yayın hızının bu bütçeyi hangi hızla tükettiğini sayar ve bütçe
bittiğinde kodda neyin değiştiğini gösterir.

### Hata Bütçesi

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/hata-butcesi

Hedefin tersinin harcanabilir bir sayıya çevrilmesi: bütçenin ödünç sisteminin gerçek istek kayıtlarından kötü istek adedi olarak hesaplanması, dört yayın hızının bu bütçeyi hangi hızla tükettiğinin sayılması ve bütçe bittiğinde yayın kapısının kaç yayını engelleyip aşımı ne kadar küçülttüğünün ölçülmesi.

Önceki ders dört sinyali dakika çözünürlüğünde hesapladı; artık bir hedefin altına düşüldüğü
anlaşılabiliyor. Altına düşüldüğünde **ne olacağı** hâlâ yazılı değil. Ödünç sistemine haftada iki
yayın da yapılabilir, günde iki yayın da; her yayın kayıtlarda ölçülebilir bir iz bırakır ve hedefin
altında geçen her dakika bir yerden ödenir. Bu ders o ödemenin defterini tutar.

#### Bütçe Telemetriden Çıkar

Hedef yüzde 99,9 ise, isteklerin binde biri kötü olabilir. Bu oran bir **adede** çevrildiğinde
harcanabilir bir büyüklük olur: otuz günde kaç istek geldiyse binde biri o ayın **hata bütçesidir**.
Bütçe, kalanı okunabilen, günlük tüketimi çizilebilen ve bittiği gün belli olan bir sayıdır.

Sistem Tasarımına Giriş kursu aynı büyüklüğü aylık kesinti dakikası olarak kurmuştu; yüzde ile
dakika arasındaki çevrim orada yapıldı ve burada tekrarlanmaz. Buradaki fark bölenin nereden
geldiğidir: bütçe istek kaydından hesaplandığında **trafikle birlikte değişir.** Trafik iki katına
çıkarsa izin verilen kötü istek sayısı da iki katına çıkar, çünkü aynı oran daha büyük bir tabana
uygulanır. Dakika cinsinden yazılmış bir bütçede bu esneme yoktur.

#### Yayın Hızı Bütçeyi Nasıl Yer

Aşağıdaki koşum otuz günü dakika dakika yürütür. Yayınlar sabit aralıklarla yapılır, her yayın bir
olasılıkla bozulma bırakır ve bozulmanın şiddeti ile süresi **yayının büyüklüğüne** bağlıdır. Yayın
büyüklüğünün ölçüsü, iki yayın arasında geçen gün sayısıdır: aralık uzadıkça yayın daha çok
değişiklik taşır.

```js
// butce/kosum.mjs — hata butcesi telemetriden cikar: hedef %99,9 ise izin verilen kotu istek
// sayisi toplam istegin binde 1'idir. Oranlar on binde birimindedir.
export const DK = 30 * 1440, TOHUM = 20260731, HEDEF = 0.999, TABAN = 2;
const uret = (t) => (n) => Math.floor(((t = (t * 1664525 + 1013904223) >>> 0) / 2 ** 32) * n);
const saat = (t) => Math.floor(t / 60) % 24;
export const istekDk = (t) => (saat(t) >= 8 && saat(t) < 22 ? 100 : 30);
export const TOPLAM = [...Array(DK)].reduce((a, _, t) => a + istekDk(t), 0);
export const BUTCE = Math.round(TOPLAM * (1 - HEDEF));
export const kapi = (kalan) => kalan > 0;        // yayin kapisi: butce bittiyse yayin yok

// SD1 (varsayim): yayin arasindaki gun buyudukce bozulmanin olasiligi, siddeti ve geri alma
// suresi buyur: olasilik %15 + %15*gun, siddet on binde 200 + 400*gun, sure 10 + 20*gun dk.
export function kosum(araGun, kapiAcik) {
  const rp = uret(TOHUM), rt = uret(TOHUM), periyot = Math.round(araGun * 1440);
  // araGun 0: hic yayin yapilmaz, taban hatanin tek basina yedigi olculur
  const p = Math.min(900, 150 + 150 * araGun), si = Math.min(2500, 200 + 400 * araGun);
  const sr = Math.min(120, Math.round(10 + 20 * araGun));
  let kalan = BUTCE, yayin = 0, bozulan = 0, engel = 0, bitti = 0, son = -1;
  const gunluk = [];
  for (let t = 0; t < DK; t += 1) {
    if (periyot > 0 && t >= 600 && (t - 600) % periyot === 0) {
      if (kapiAcik && !kapi(kalan)) engel += 1;
      else { yayin += 1; if (rp(1000) < p) { bozulan += 1; son = t + sr; } }
    }
    const oran = TABAN + (t < son ? si : 0);
    for (let i = 0, n = istekDk(t); i < n; i += 1) if (rt(10000) < oran) kalan -= 1;
    if (kalan <= 0 && bitti === 0) bitti = Math.floor(t / 1440) + 1;
    if ((t + 1) % 1440 === 0) gunluk.push(kalan / BUTCE);
  }
  return { yayin, bozulan, engel, bitti, harcanan: BUTCE - kalan, gunluk };
}

export const HIZ = [["yayin yok", 0], ["gunde 2", 0.5], ["gunde 1", 1],
  ["3 gunde 1", 3], ["5 gunde 1", 5]];

if (import.meta.filename === process.argv[1]) {
  console.log(`30 gunde ${TOPLAM} istek, hedef %99,9 -> butce ${BUTCE} kotu istek; taban hata ` +
    `on binde ${TABAN} tek basina ${Math.round((TOPLAM * TABAN) / 10000)} istek yer`);
  console.log(`\n${"yayin araligi".padEnd(15)}${"yayin".padStart(7)}${"bozulan".padStart(9)}` +
    `${"bozuk dakika".padStart(14)}${"harcanan".padStart(10)}${"butcenin".padStart(10)}` +
    `${"biten gun".padStart(11)}`);
  for (const [ad, g] of HIZ) {
    const r = kosum(g, false), dk = r.bozulan * Math.min(120, Math.round(10 + 20 * g));
    console.log(`${ad.padEnd(15)}${String(r.yayin).padStart(7)}${String(r.bozulan).padStart(9)}` +
      `${(dk + " dk").padStart(14)}${String(r.harcanan).padStart(10)}` +
      `${`%${(100 * r.harcanan / BUTCE).toFixed(1)}`.padStart(10)}` +
      `${(r.bitti ? `${r.bitti}.` : "-").padStart(11)}`);
  }
}
```

```
30 gunde 3060000 istek, hedef %99,9 -> butce 3060 kotu istek; taban hata on binde 2 tek basina 612 istek yer

yayin araligi    yayin  bozulan  bozuk dakika  harcanan  butcenin  biten gun
yayin yok            0        0          0 dk       631     %20.6          -
gunde 2             60       13        260 dk      1486     %48.6          -
gunde 1             30        6        180 dk      1717     %56.1          -
3 gunde 1           10        5        350 dk      5523    %180.5        16.
5 gunde 1            6        4        440 dk     10248    %334.9         6.
```

#### Sayı Değil, Büyüklük

İlk satır bütçenin bir kısmının değişimle hiç ilgisi olmadığını söylüyor: hiç yayın yapılmadığında
bile taban hata otuz günde bütçenin yüzde 20,6'sını yiyor. Ay boyunca dokunulmayan bir sistemin
bile bütçesi vardır ve tamamı değişime ayrılamaz.

Asıl karşıtlık ikinci ve dördüncü satırlar arasında. Günde iki yayın yapan takım ayda **60 yayın**
çıkardı ve bütçenin yüzde 48,6'sını harcadı; üç günde bir yayın yapan takım **10 yayın** çıkardı ve
bütçenin yüzde 180,5'ini harcadı, üstelik bütçe 16. günde bitti. Altı kat daha çok yayın, üçte bir
kadar tüketim.

Aradaki fark yayın başına düşen maliyette görünüyor. Taban tüketimi (631) çıkarıldığında günde iki
yayın hızında yayınlara düşen 855 kötü istek var: yayın başına 14. Üç günde bir hızında yayınlara
düşen 4.892 kötü istek var: yayın başına 489. Otuz beş kat. Bozuk dakika sütunu nedenini gösteriyor:
hızlı hızda 13 bozulma oldu ama toplam 260 dakika sürdü, yavaş hızda 5 bozulma oldu ve 350 dakika
sürdü. Bozulma **sayısı** hızlı takımda daha yüksek, bozulmanın **süresi ve şiddeti** yavaş takımda
daha yüksek ve bütçeyi yiyen ikincisidir.

Bu sonuç SD1'in içinde saklıdır ve orada tartışılmalıdır: model, bir bozulmanın olasılığının,
şiddetinin ve geri alma süresinin yayın büyüklüğüyle arttığını varsayar. Varsayım değişirse tablo
da değişir. Varsayımı ölçüye çevirmenin yolu telemetride yazılıdır: her yayının zamanı işaretlenir,
yayından sonraki bozulma penceresi kayıtlardan çıkarılır ve üç parametre — olasılık, şiddet, süre —
yayın büyüklüğüne karşı çizilir. Bu üç sayı ölçüldükten sonra yayın hızı tartışması bir tercih
tartışması olmaktan çıkar.

#### Bütçe Bittiğinde

Bütçe bir izin belgesidir: kalan pozitifken değişim serbesttir, tükendiğinde öncelik kararlılığa
döner. Bunun kodda karşılığı tek satırlık bir kapıdır — `kapi(kalan)` yanlış döndüğünde yayın
yapılmaz. Aşağıdaki koşum aynı dört hızı kapı kapalı ve açık olmak üzere iki kez yürütür.

```js
// butce/kapi.mjs — yayin kapisi acikken ne degisir: butce bittiginde yayin engellenir.
// Ayni dort yayin araligi, kapi kapali ve acik olmak uzere iki kez kosulur.
import { kosum, HIZ, BUTCE } from "./kosum.mjs";
const y = (x) => `%${(100 * x).toFixed(1)}`;

console.log(`butce ${BUTCE} kotu istek\n`);
console.log(`${"yayin araligi".padEnd(15)}${"kapi".padStart(8)}${"yayin".padStart(7)}` +
  `${"engellenen".padStart(12)}${"harcanan".padStart(10)}${"butcenin".padStart(10)}` +
  `${"asim".padStart(9)}`);
for (const [ad, g] of HIZ)
  for (const acik of [false, true]) {
    const r = kosum(g, acik);
    console.log(`${ad.padEnd(15)}${(acik ? "acik" : "kapali").padStart(8)}` +
      `${String(r.yayin).padStart(7)}${String(r.engel).padStart(12)}` +
      `${String(r.harcanan).padStart(10)}${y(r.harcanan / BUTCE).padStart(10)}` +
      `${(r.harcanan > BUTCE ? String(r.harcanan - BUTCE) : "-").padStart(9)}`);
  }

const GUN = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30];
console.log(`\nkalan butce (kapi kapali)\n${"gun".padEnd(15)}${GUN.map((g) => String(g).padStart(8)).join("")}`);
for (const [ad, g] of HIZ) {
  const r = kosum(g, false);
  console.log(`${ad.padEnd(15)}${GUN.map((d) => y(r.gunluk[d - 1]).padStart(8)).join("")}`);
}
```

```
butce 3060 kotu istek

yayin araligi      kapi  yayin  engellenen  harcanan  butcenin     asim
yayin yok        kapali      0           0       631     %20.6        -
yayin yok          acik      0           0       631     %20.6        -
gunde 2          kapali     60           0      1486     %48.6        -
gunde 2            acik     60           0      1486     %48.6        -
gunde 1          kapali     30           0      1717     %56.1        -
gunde 1            acik     30           0      1717     %56.1        -
3 gunde 1        kapali     10           0      5523    %180.5     2463
3 gunde 1          acik      6           4      3531    %115.4      471
5 gunde 1        kapali      6           0     10248    %334.9     7188
5 gunde 1          acik      2           4      5416    %177.0     2356

kalan butce (kapi kapali)
gun                   3       6       9      12      15      18      21      24      27      30
yayin yok         %98.0   %96.2   %93.5   %91.2   %89.2   %87.2   %85.3   %82.9   %81.2   %79.4
gunde 2           %98.0   %92.0   %86.7   %80.7   %76.2   %68.8   %63.9   %59.2   %56.8   %51.4
gunde 1           %98.0   %96.2   %93.5   %80.4   %78.3   %70.2   %62.1   %53.8   %45.7   %43.9
3 gunde 1         %67.0   %65.2   %62.5   %27.8   %25.8   %-7.6   %-9.5  %-43.9  %-45.7  %-80.5
5 gunde 1         %19.9  %-60.2  %-62.8  %-65.2  %-67.2 %-148.2 %-150.1 %-152.5 %-233.1 %-234.9
```

Kapı üç şeyi birden söylüyor. **Hızlı takımı hiç durdurmuyor:** günde iki ve günde bir yayın
satırlarında engellenen yayın sıfır, çünkü bütçe hiç tükenmiyor. Kapı bir yayın hızı sınırı değil,
bir bütçe sınırıdır; hızlı yayın yapan takım kapıyı hiç görmez.

**Yavaş takımda dört yayın engelleniyor** ve aşım 2.463'ten 471'e düşüyor: bütçenin ötesine geçen
tüketimin yüzde 81'i önleniyor. On yayının altısı yapılabiliyor, dördü ay sonuna kalıyor.

**Kapı geç kalır.** Açıkken bile 471 istek aşımı var, çünkü kapı bütçe bittikten sonra devreye
girer, önce değil. Kalan bütçe tablosu bunu gün gün gösteriyor: üç günde bir yayın yapan takımda
kalan 12. günde yüzde 27,8'e inmişti ve 18. günde eksiye geçti. Yüzde 27,8 görülen gün ile aşımın
başladığı gün arasında altı gün var ve o günlerde iki yayın daha yapıldı. Kapının tek başına
yetmemesinin nedeni budur: bir eşiği **aştıktan sonra** kapanan kapı, aşımın kendisini engellemez;
gereken şey bütçenin **tükenmeden önce** haber vermesidir.

#### Özet

- Hata bütçesi hedefin tersinin istek kaydına uygulanmış hâlidir: otuz günde 3.060.000 istek ve
  yüzde 99,9 hedefi 3.060 kötü isteklik bir bütçe verir ve bu bütçe trafikle birlikte esner.
- Bütçenin bir kısmı değişimle ilgisizdir: hiç yayın yapılmadan taban hata bütçenin yüzde 20,6'sını
  yedi.
- Altı kat daha çok yayın daha çok tüketim demek değil: 60 yayın bütçenin yüzde 48,6'sını, 10 yayın
  yüzde 180,5'ini harcadı ve ikincisinde bütçe 16. günde bitti.
- Yayın başına maliyet 14 kötü isteğe karşı 489 kötü istek; bozulma sayısı hızlı takımda yüksek
  (13'e 5), bozulmanın süresi yavaş takımda yüksek (260 dakikaya 350 dakika) ve bütçeyi yiyen
  süredir.
- Yayın kapısı tek satırlık bir koşuldur ve hızlı takımı hiç durdurmaz; yavaş takımda dört yayını
  engelleyerek aşımı 2.463'ten 471'e indirdi.
- Kapı bütçe bittikten sonra kapandığı için aşımın tamamını önleyemez; bütçe tükenmeden önce haber
  veren bir düzenek gerekir.

#### Sonraki Adım

Kalan bütçe tablosundaki her satır ay sonunda okunabiliyor, oysa karar 12. günde verilmeliydi. O
gün kimse tabloya bakmıyorsa sayının bir değeri yok — birinin çağrılması gerekir. Çağrının hangi
kuraldan doğacağı ise açık bir soru: kural bildirim kuyruğunun derinliğine mi bakar, ödünç
servisinin bellek kullanımına mı, yoksa doğrudan üyenin gördüğü hata oranına mı? Bu seçim uyarı
sayısını, aynı olayda kaç kez çalındığını ve hiç çalmayan olayların sayısını belirler. Sonraki ders
iki ayrı kural kümesini aynı olayların üzerinden geçirir ve ikisini bu üç sayıyla karşılaştırır.

### Uyarı Tasarımı

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/uyari-tasarimi

Uyarı kuralının kaynağının sonucu belirlemesi: on iki neden tabanlı kural ile dört belirti tabanlı kuralın aynı yedi günün üzerinden geçirilmesi, ürettikleri uyarı sayısının, aynı olayda kaç kuralın birden çaldığının ve hiç uyarı üretmeyen gerçek olayların sayılması.

Önceki dersin kalan bütçe tablosunda karar 12. günde verilmeliydi. O gün kimse tabloya bakmıyorsa
sayının bir değeri yok; birinin çağrılması gerekir. Çağrının hangi kuraldan doğacağı ise açık bir
soru. Kural bildirim kuyruğunun derinliğine mi bakar, ödünç servisinin bellek kullanımına mı, yoksa
doğrudan üyenin gördüğü hata oranına mı?

Bu ders eşik seçmez. Eşik ve pencere taraması Başarım Karşıt Kalıpları ve İzleme kursunda
yapıldı; buradaki soru başka: kuralın **kaynağı**. Aynı yedi günün üzerinden iki ayrı kural kümesi
geçirilir ve üç sayı karşılaştırılır — üretilen uyarı, aynı olayda birden çalan kural, hiç uyarı
üretmeyen olay.

#### Kuralın Kaynağı

**Neden tabanlı kural** bir bileşenin iç ölçütünü okur: bellek kullanımı, disk doluluğu, önbellek
isabet oranı, bağlantı havuzu doluluğu, çöp toplama süresi. Bu ölçütler süreçlerin içinden gelir ve
sayıları servis sayısıyla birlikte artar.

**Belirti tabanlı kural** üyenin gördüğü şeyi okur: hata oranı, gecikme, trafik sapması, bildirim
teslim oranı. Bu ölçütler sistemin dışından bakınca da aynıdır ve sayıları servis sayısından
bağımsızdır.

Ayrım keyfi değil: bir uyarı bir kişiyi çağırır ve çağrılan kişinin yapacak bir işi olmalıdır.
Ödünç servisinin belleğinin yüzde 91 olması tek başına bir iş tanımlamaz; ödünç isteklerinin yüzde
4'ünün başarısız dönmesi tanımlar. Bu, gösterge tablosu ile uyarının farklı sorular yanıtladığı
ayrımının kural yazımına düşen karşılığıdır.

#### On Altı Kural ve Altı Olay

Aşağıdaki üreteç yedi günü dakikalık örneklerle yazar. On iki bileşen ölçütü ve dört kullanıcı
ölçütü aynı dakikalarda üretilir; her ölçütün tabanı, gürültüsü ve eşiği kendi satırındadır. Altı
olay bu ölçütlerin bir kısmını hedef değere doğru taşır.

```js
// uyari/olcut.mjs — 7 gunluk dakikalik olcut ornegi. On iki bilesen olcutu (neden) ile dort
// kullanici olcutu (belirti) ayni dakikalarda uretilir. Kural bicimi: [ad, taban, gurultu,
// yon, esik]; yon "ust" ise esigin ustu, "alt" ise alti kotudur.
export const DK = 7 * 1440, TOHUM = 20260731;
export const NEDEN = [
  ["katalog.onbellek_isabet", 92, 2, "alt", 70], ["katalog.islemci", 38, 6, "ust", 85],
  ["odunc.bellek", 58, 3, "ust", 90], ["odunc.cop_toplama_ms", 25, 6, "ust", 120],
  ["odunc.yeniden_baslatma", 0, 0, "ust", 0], ["uyelik.havuz_doluluk", 44, 6, "ust", 90],
  ["uyelik.bagimlilik_ms", 40, 8, "ust", 300], ["bildirim.kuyruk", 12, 8, "ust", 400],
  ["bildirim.etkin_isci", 8, 1, "alt", 3], ["ucret.disk", 70, 1, "ust", 95],
  ["ucret.dosya_tanitici", 900, 60, "ust", 1800], ["genel.yeniden_deneme", 3, 2, "ust", 30],
];
export const BELIRTI = [
  ["hata_orani", 0.2, 0.15, "ust", 1], ["p95_gecikme_ms", 180, 25, "ust", 400],
  ["trafik_sapmasi", 0, 6, "ust", 45], ["teslim_orani", 99.5, 0.3, "alt", 97],
];
// SD1 (varsayim): alti olay [gun, saat, sure dk, ad, [[olcut, olay icindeki deger], ...]]
export const OLAY = [
  [1, 10, 90, "onbellek cokusu", [["katalog.onbellek_isabet", 35], ["katalog.islemci", 91],
    ["p95_gecikme_ms", 520]]],
  [2, 8, 240, "bellek sizintisi", [["odunc.bellek", 96], ["odunc.cop_toplama_ms", 190],
    ["odunc.yeniden_baslatma", 2]]],
  [3, 0, 300, "disk dolmasi", [["ucret.disk", 98], ["ucret.dosya_tanitici", 2100]]],
  [4, 14, 120, "bagimlilik yavaslamasi", [["uyelik.bagimlilik_ms", 680],
    ["uyelik.havuz_doluluk", 96], ["genel.yeniden_deneme", 48], ["p95_gecikme_ms", 690],
    ["hata_orani", 1.8]]],
  [5, 9, 180, "sessiz teslim kaybi", [["teslim_orani", 3]]],
  [6, 11, 60, "yayin sonrasi hata artisi", [["hata_orani", 4.6]]],
];
export const pencere = (i) => [OLAY[i][0] * 1440 + OLAY[i][1] * 60, OLAY[i][2]];

// SD2 (varsayim): bilesen olcutu tek surecten okundugu icin 1500 dakikada bir 3-7 dakikalik
// sivri yapar; kullanici olcutu binlerce istekten ortalandigi icin 6000 dakikada bir.
export function seri() {
  let s = TOHUM;
  const rast = (n) => Math.floor(((s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32) * n);
  const cikti = {};
  for (const [ad, taban, gur, yon, esik] of [...NEDEN, ...BELIRTI]) {
    const bilesen = NEDEN.some((k) => k[0] === ad);
    const v = new Float64Array(DK);
    let sivri = 0;
    for (let t = 0; t < DK; t += 1) {
      let d = taban + (gur ? (rast(2001) - 1000) / 1000 * gur : 0);
      if (sivri > 0) { sivri -= 1; d = yon === "ust" ? esik * 1.15 + 1 : esik * 0.85; }
      else if (rast(bilesen ? 1500 : 6000) === 0) sivri = 3 + rast(5);
      for (let i = 0; i < OLAY.length; i += 1) {
        const [bas, sr] = pencere(i), hedef = OLAY[i][4].find((h) => h[0] === ad);
        if (!hedef || t < bas || t >= bas + sr) continue;
        const ilerleme = Math.min(1, (t - bas + 1) / Math.max(1, sr * 0.2));
        d = taban + (hedef[1] - taban) * ilerleme;
      }
      v[t] = d;
    }
    cikti[ad] = v;
  }
  return cikti;
}

if (import.meta.filename === process.argv[1]) {
  const S = seri();
  console.log(`tohum ${TOHUM}, ${DK} dakika, ${NEDEN.length} bilesen olcutu, ` +
    `${BELIRTI.length} kullanici olcutu, ${OLAY.length} olay`);
  const [b, sr] = pencere(0);
  console.log(`ornek — onbellek cokusu penceresi (${b}. dakika, ${sr} dk): ` +
    `onbellek isabet ${S["katalog.onbellek_isabet"][b + 30].toFixed(1)}, ` +
    `islemci ${S["katalog.islemci"][b + 30].toFixed(1)}, ` +
    `p95 ${S["p95_gecikme_ms"][b + 30].toFixed(0)} ms, ` +
    `hata orani ${S["hata_orani"][b + 30].toFixed(2)}`);
}
```

```
tohum 20260731, 10080 dakika, 12 bilesen olcutu, 4 kullanici olcutu, 6 olay
ornek — onbellek cokusu penceresi (2040. dakika, 90 dk): onbellek isabet 35.0, islemci 91.0, p95 520 ms, hata orani 0.07
```

#### İki Kümeyi Aynı Haftadan Geçirmek

Bir kural, koşulu üst üste üç dakika sağlandığında bir uyarı üretir ve koşul düşene kadar
tekrarlamaz. İki küme aynı ölçütlerin üzerinde bağımsız çalıştırılır.

```js
// uyari/kural.mjs — ayni yedi gunun uzerinden iki kural kumesi gecirilir: on iki neden tabanli
// kural ve dort belirti tabanli kural. Bir kural, kosulu ust uste 3 dakika saglandiginda bir
// uyari uretir ve kosul dusene kadar yeniden uretmez.
import { seri, NEDEN, BELIRTI, OLAY, DK, pencere } from "./olcut.mjs";
const S = seri();
const asar = (k, v) => (k[3] === "ust" ? v > k[4] : v < k[4]);

function uyarilar(kume) {
  const m = new Map();
  for (const k of kume) {
    const v = S[k[0]], dk = [];
    let ard = 0, etkin = false;
    for (let t = 0; t < DK; t += 1) {
      if (asar(k, v[t])) { ard += 1; if (ard >= 3 && !etkin) { etkin = true; dk.push(t - 2); } }
      else { ard = 0; etkin = false; }
    }
    m.set(k[0], dk);
  }
  return m;
}
const KUME = [["neden tabanli", NEDEN, uyarilar(NEDEN)], ["belirti tabanli", BELIRTI, uyarilar(BELIRTI)]];

console.log(`${"olay".padEnd(26)}${"sure".padStart(7)}` +
  `${KUME.map(([ad]) => `${ad} kural`.padStart(24) + "ilk uyari".padStart(11)).join("")}`);
const yakalanan = KUME.map(() => new Set());
OLAY.forEach(([, , sr, ad], i) => {
  const [bas] = pencere(i);
  const s = KUME.map(([, , m], j) => {
    const c = [...m.values()].map((dk) => dk.filter((t) => t >= bas && t < bas + sr));
    const kural = c.filter((x) => x.length > 0).length;
    const ilk = Math.min(...c.flat().map((t) => t - bas), Infinity);
    if (kural > 0) yakalanan[j].add(i);
    return [kural, ilk];
  });
  console.log(`${ad.padEnd(26)}${(sr + " dk").padStart(7)}` +
    `${s.map(([kural, ilk]) => String(kural).padStart(24) +
      (ilk === Infinity ? "-" : `${ilk}. dk`).padStart(11)).join("")}`);
});

const ici = (t) => OLAY.some((o, i) => t >= pencere(i)[0] && t < pencere(i)[0] + o[2]);
console.log(`\n${"kume".padEnd(16)}${"kural".padStart(7)}${"uyari".padStart(7)}` +
  `${"olay disi".padStart(11)}${"yakalanan".padStart(11)}${"olay basina".padStart(13)}  kacan olay`);
KUME.forEach(([ad, kume, m], j) => {
  const hepsi = [...m.values()].flat();
  const disi = hepsi.filter((t) => !ici(t)).length;
  const ici2 = hepsi.length - disi;
  console.log(`${ad.padEnd(16)}${String(kume.length).padStart(7)}${String(hepsi.length).padStart(7)}` +
    `${String(disi).padStart(11)}${`${yakalanan[j].size}/${OLAY.length}`.padStart(11)}` +
    `${(ici2 / yakalanan[j].size).toFixed(1).padStart(13)}  ` +
    `${OLAY.filter((_, i) => !yakalanan[j].has(i)).map((o) => o[3]).join(", ") || "-"}`);
});

const birlikte = new Set([...yakalanan[0], ...yakalanan[1]]);
console.log(`\nikisi birlikte: ${NEDEN.length + BELIRTI.length} kural, ` +
  `${birlikte.size}/${OLAY.length} olay`);
const cok = OLAY.map((o, i) => {
  const [bas] = pencere(i);
  return [o[3], [...KUME[0][2].values()].filter((dk) =>
    dk.some((t) => t >= bas && t < bas + o[2])).length];
}).sort((a, b) => b[1] - a[1])[0];
console.log(`en gurultulu olay: ${cok[0]} — tek olayda ${cok[1]} neden tabanli uyari birden`);
const enCok = [...KUME[0][2]].sort((a, b) => b[1].length - a[1].length)[0];
console.log(`en cok uyaran tek kural: ${enCok[0]} — 7 gunde ${enCok[1].length} uyari`);
```

```
olay                         sure     neden tabanli kural  ilk uyari   belirti tabanli kural  ilk uyari
onbellek cokusu             90 dk                       2      6. dk                       1     11. dk
bellek sizintisi           240 dk                       5      0. dk                       0          -
disk dolmasi               300 dk                       3     45. dk                       0          -
bagimlilik yavaslamasi     120 dk                       3      9. dk                       2     10. dk
sessiz teslim kaybi        180 dk                       0          -                       1      0. dk
yayin sonrasi hata artisi   60 dk                       0          -                       1      2. dk

kume              kural  uyari  olay disi  yakalanan  olay basina  kacan olay
neden tabanli        12     81         67        4/6          3.5  sessiz teslim kaybi, yayin sonrasi hata artisi
belirti tabanli       4      9          4        4/6          1.3  bellek sizintisi, disk dolmasi

ikisi birlikte: 16 kural, 6/6 olay
en gurultulu olay: bellek sizintisi — tek olayda 5 neden tabanli uyari birden
en cok uyaran tek kural: bildirim.kuyruk — 7 gunde 10 uyari
```

#### Üç Sayı

**Üretilen uyarı.** On iki neden tabanlı kural yedi günde 81 uyarı üretti, dört belirti tabanlı
kural 9 uyarı. Kural sayısı üç kat, uyarı sayısı dokuz kat. Bunun 67'si hiçbir olaya denk gelmiyor:
neden tabanlı uyarıların yüzde 83'ü boşa çıkıyor. Belirti tabanlı kümede aynı oran 9'da 4.
Farkın kaynağı SD2'de yazılı: bileşen ölçütü tek bir süreçten okunur ve kısa sıvrılar yapar,
kullanıcı ölçütü binlerce isteğin ortalamasıdır ve düzgündür. Tek bir kural — bildirim kuyruğu —
yedi günde 10 uyarı üretti.

**Aynı olayda birden çalan kural.** Neden tabanlı kümede olay başına 3,5 uyarı düşüyor, belirti
tabanlı kümede 1,3. Bellek sızıntısı sırasında beş kural birden çaldı: üçü olayın kendisiydi
(bellek, çöp toplama, yeniden başlatma), ikisi o dört saat içinde denk gelen ilgisiz sıvrılardı.
Çağrılan kişi beş uyarıyı açtığında hangisinin kök neden olduğunu ayırt edemez; uyarıların sayısı
bilgi taşımaz, çünkü hepsi aynı olaydan doğmuştur.

**Hiç uyarı üretmeyen olay.** İki küme de altıda dört olay yakaladı, ama **farklı ikisini kaçırdı.**
Neden tabanlı kurallar sessiz teslim kaybını ve yayın sonrası hata artışını göremedi: ikisinde de
bileşen ölçütleri normaldi, bozuk olan işin kendisiydi. Belirti tabanlı kurallar bellek sızıntısını
ve disk dolmasını göremedi: ikisinde de üye hiçbir şey fark etmemişti, çünkü kaynak henüz
tükenmemişti.

#### Uyandıran Kural, Teşhis Eden Ölçüt

Zamanlama sütunu iki kümenin rollerini ayırıyor. Önbellek çöküşünde neden tabanlı kural 6.
dakikada, belirti tabanlı kural 11. dakikada çaldı; bağımlılık yavaşlamasında fark bir dakikaya
iniyor. Neden tabanlı ölçütler biraz daha erken, ama bu beş dakikanın bedeli 67 boş uyarıdır.

Buradan çıkan düzen şudur: **on iki bileşen ölçütü silinmez, uyarı kuralı olmaktan çıkarılır.**
Uyandıran kurallar belirtiden yazılır — dört kural, üyenin gördüğü dört ölçüt. Bileşen ölçütleri
gösterge tablosunda kalır ve çağrılan kişi uyarıyı aldıktan sonra oraya bakar. Uyarı "bir şey
yapılmalı" der, gösterge tablosu "nereye bakılmalı" der. İki kümenin birleşimi altı olayın altısını
kapsıyor, ama bu 16 uyarı kuralıyla değil, 4 uyarı kuralı ve 12 gösterge tablosu paneliyle olur.

Belirti tabanlı kümenin kaçırdığı iki olay bu düzenin gerçek boşluğudur ve eşik değiştirerek
kapanmaz. Disk yüzde 98 doluyken üye hâlâ bir şey görmez; belirti ancak disk dolduğunda ortaya
çıkar ve o an geri dönüşü pahalıdır. Tükenen kaynaklar için kural eşik üzerine değil **tükenme
süresi** üzerine yazılır: "bu doluş hızıyla disk dört saat içinde dolar" cümlesi bir belirtidir,
çünkü bir kişinin yapacağı işi tanımlar. Aynı cümle bellek sızıntısı için de yazılabilir; ikisi de
neden tabanlı bir ölçütten okunur ama belirti tabanlı bir soruya yanıt verir.

#### Özet

- Uyarı kuralının kaynağı iki türdür: bileşenin iç ölçütü (neden) ya da üyenin gördüğü ölçüt
  (belirti); ayrımın ölçütü, çağrılan kişinin yapacak bir işi olup olmadığıdır.
- On iki neden tabanlı kural yedi günde 81 uyarı üretti ve 67'si hiçbir olaya denk gelmedi; dört
  belirti tabanlı kural 9 uyarı üretti ve 4'ü boşa çıktı.
- Neden tabanlı kümede olay başına 3,5 uyarı düşüyor: bellek sızıntısında beş kural birden çaldı,
  üçü olayın kendisi, ikisi ilgisiz sıvrıydı.
- İki küme de altıda dört olay yakaladı ama farklı ikisini kaçırdı: neden tabanlı kurallar işin
  kendisinin bozulduğu olayları, belirti tabanlı kurallar üyeye henüz ulaşmamış tükenmeleri.
- Neden tabanlı kurallar önbellek çöküşünde beş dakika erken çaldı; bu beş dakikanın bedeli 67 boş
  uyarıdır.
- Çözüm bileşen ölçütlerini silmek değil, uyarı kuralı olmaktan çıkarmaktır: dört uyarı kuralı ve
  on iki gösterge tablosu paneli altı olayın altısını kapsar.

#### Sonraki Adım

Bu derste kurulan her kural bir insanı çağırıyor ve insan dakikalar içinde yanıt veriyor. Bazı
kararlar ise insana hiç sorulmaz. Ödünç sisteminin süreçlerinden birinin yaşayıp yaşamadığına ve
trafik alıp almayacağına makine karar verir, üstelik saniyeler içinde ve bir günde binlerce kez.
Bu karar iki ayrı soruya bakar — süreç ölmüş mü ve süreç şu an iş alabilir mi — ve iki soru tek
bir uca bağlandığında verilen yanıt yalnız yanlış olmaz, kendini besleyen bir döngü kurar. Sonraki
ders iki ucu kodda ayırır ve ayırmamanın bedelini yeniden başlatma sayısıyla düşen istek sayısıyla
ölçer.

### Sağlık Uçları

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/saglik-uclari

Canlılık ile hazır olmanın kodda ayrı tutulması: iki ucun hangi soruyu yanıtladığı, bağımlılık denetiminin canlılık ucuna girmesinin ürettiği yeniden başlatma döngüsü ve aynı senaryonun iki kipte koşularak düşen istek ile toparlanma süresinin sayılması.

Önceki derste kurulan her kural bir insanı çağırıyor ve insan dakikalar içinde yanıt veriyor. Bazı
kararlar ise insana hiç sorulmaz. Ödünç sisteminin süreçlerinden birinin yaşayıp yaşamadığına ve
trafik alıp almayacağına makine karar verir, saniyeler içinde ve günde binlerce kez. Bu karar iki
ayrı soruya bakar; iki soru tek bir uca bağlandığında verilen yanıt yalnız yanlış olmaz, kendini
besleyen bir döngü kurar.

Dayanıklılık ve Güvenilirlik kursu sağlık ucunun içeriğini ve denetim aralığını ölçtü; o tarama
burada tekrarlanmaz. Buradaki soru tek: iki uç kodda ayrı mı ve ayrılmadığında ne oluyor.

#### İki Soru, İki Eylem

**Canlılık** ucunun sorusu şudur: bu süreç ölmüş mü ve yeniden başlatmak durumu düzeltir mi.
Yanıtı olumsuzsa denetleyici tek bir eylem yapar — süreci öldürüp yeniden başlatır.

**Hazır olma** ucunun sorusu başkadır: bu süreç şu an iş alabilir mi. Yanıtı olumsuzsa eylem
başkadır — trafik kesilir, ama süreç yaşamaya devam eder ve kendi kendine toparlanması beklenir.

Ayrımın kuralı bu iki eylemden çıkar: **bir denetimin canlılık ucuna girebilmesi için, o denetim
başarısız olduğunda yeniden başlatmanın çözüm olması gerekir.** Sürecin kendi kilidi, kendi belleği,
kendi olay döngüsü bu ölçüte uyar; süreç yeniden doğduğunda temizlenirler. Bir bağımlılık uymaz.
Üyelik servisi erişilemezken ödünç sürecini yeniden başlatmak üyelik servisini geri getirmez;
denetim yeni süreçte de başarısız olur, denetleyici onu da öldürür ve döngü bağımlılık dönene kadar
sürer.

#### Düğüm: Ayrım İki Satırdadır

```js
// saglik/dugum.mjs — odunc dugumu: iki saglik ucu (/canlilik, /hazir) ve iki is ucu.
// Kip "ayri" ise canlilik yalniz surecin kendi durumunu okur; kip "birlesik" ise canlilik
// uyelik servisine olan bagimliligi da denetler. Ayrimin tamami su iki satirdadir.
import { createServer } from "node:http";
import { readFileSync } from "node:fs";
// SD1 (varsayim): yeniden baslayan surec yerel dizinini 24 istek boyunca yeniden kurar.
export const ISINMA = 24;

export function baslat(kip) {
  let islenen = 0, kilitli = false;
  const bagimli = () => readFileSync("bagimlilik.durum", "utf8").trim() === "acik";
  const s = createServer((istek, yanit) => {
    let kod = 200;
    if (istek.url === "/canlilik") kod = kilitli || (kip === "birlesik" && !bagimli()) ? 500 : 200;
    else if (istek.url === "/hazir") kod = bagimli() && islenen >= ISINMA ? 200 : 503;
    else {
      islenen += 1;
      if (islenen <= ISINMA) kod = 503;                          // yerel dizin henuz kurulmadi
      else if (istek.url === "/odunc" && !bagimli()) kod = 503;   // uyelik servisi gerekli
    }
    yanit.writeHead(kod).end();
  });
  s.listen(0, "127.0.0.1", () => process.send({ port: s.address().port }));
}

if (process.argv[2]) baslat(process.argv[2]);
else console.log(`uclar: /canlilik /hazir /ara /odunc; isinma ${ISINMA} istek; ` +
  `/ara bagimlilik gerektirmez, /odunc gerektirir`);
```

```
uclar: /canlilik /hazir /ara /odunc; isinma 24 istek; /ara bagimlilik gerektirmez, /odunc gerektirir
```

İki uç arasındaki bütün fark `bagimli()` çağrısının hangi satırda geçtiğidir. Hazır olma ucunda
geçer, çünkü bağımlılık yoksa süreç iş alamaz. Canlılık ucunda geçmemelidir; `birlesik` kipi tam
olarak bu hatayı yapar ve tek bir koşul terimi ekler. Süreç iki iş ucu sunar: `/ara` yerel dizinden
yanıt verir ve üyelik servisine ihtiyaç duymaz, `/odunc` duyar. Yeniden başlayan süreç yerel dizini
kaybeder ve yirmi dört istek boyunca ikisine de yanıt veremez.

#### Aynı Senaryo, İki Kip

```js
// saglik/denetleyici.mjs — ayni senaryo iki kipte kosulur: uyelik servisi 15. tikte kapanir,
// 40. tikte acilir. Denetleyici her 2 tikte canlilik ve hazir olma ucunu yoklar, 3 ardisik
// canlilik hatasinda sureci yeniden baslatir. Her tikte 2 arama ve 2 odunc istegi gonderilir.
import { fork } from "node:child_process";
import { writeFileSync } from "node:fs";
import { request } from "node:http";
// SD2 (varsayim): denetim araligi 2 tik, yeniden baslatma esigi 3 ardisik canlilik hatasi.
const TIK = 60, KAPALI = [15, 40], DENETIM = 2, ESIK = 3, ISINMA = 24;

const cagir = (port, yol) => new Promise((c) => {
  const r = request({ host: "127.0.0.1", port, path: yol }, (y) => { y.resume(); c(y.statusCode); });
  r.on("error", () => c(0));
  r.end();
});
const ac = (kip) => new Promise((c) => {
  const p = fork(new URL("dugum.mjs", import.meta.url), [kip],
    { stdio: ["ignore", "ignore", "ignore", "ipc"] });
  p.once("message", (m) => c([p, m.port]));
});

async function kosum(kip) {
  writeFileSync("bagimlilik.durum", "acik");
  let [p, port] = await ac(kip);
  for (let i = 0; i < ISINMA; i += 1) await cagir(port, "/ara");   // ilk isinma sayilmaz
  const s = { yeniden: 0, ara: 0, odunc: 0, hazirDegil: 0, toparlanma: -1 };
  let ardisik = 0;
  for (let t = 0; t < TIK; t += 1) {
    writeFileSync("bagimlilik.durum", t >= KAPALI[0] && t < KAPALI[1] ? "kapali" : "acik");
    if (t % DENETIM === 0) {
      ardisik = (await cagir(port, "/canlilik")) === 200 ? 0 : ardisik + 1;
      if (ardisik >= ESIK) {
        ardisik = 0; s.yeniden += 1;
        p.kill(); await new Promise((c) => p.once("exit", c));
        [p, port] = await ac(kip);
      }
      if ((await cagir(port, "/hazir")) !== 200) s.hazirDegil += 1;
    }
    let iyi = 0;
    for (const yol of ["/ara", "/ara", "/odunc", "/odunc"]) {
      if ((await cagir(port, yol)) === 200) iyi += 1;
      else if (yol === "/ara") s.ara += 1; else s.odunc += 1;
    }
    if (t >= KAPALI[1] && iyi === 4 && s.toparlanma < 0) s.toparlanma = t - KAPALI[1];
  }
  p.kill();
  return s;
}

const cikti = [];
for (const kip of ["ayri", "birlesik"]) cikti.push([kip, await kosum(kip)]);
console.log(`${TIK} tik, tik basina 4 istek; uyelik servisi ${KAPALI[0]}.-${KAPALI[1]}. tikler ` +
  `arasinda kapali (${KAPALI[1] - KAPALI[0]} tik), isinma ${ISINMA} istek`);
console.log(`\n${"kip".padEnd(10)}${"yeniden baslatma".padStart(18)}${"dusen /ara".padStart(12)}` +
  `${"dusen /odunc".padStart(14)}${"hazir degil".padStart(13)}${"toparlanma".padStart(12)}`);
for (const [kip, s] of cikti)
  console.log(`${kip.padEnd(10)}${String(s.yeniden).padStart(18)}${String(s.ara).padStart(12)}` +
    `${String(s.odunc).padStart(14)}${(s.hazirDegil + " yoklama").padStart(13)}` +
    `${(s.toparlanma + " tik").padStart(12)}`);
const [a, b] = cikti.map(([, s]) => s);
console.log(`\nbirlesik kipte dusen ${b.ara} arama istegi uyelik servisine hic ihtiyac duymuyordu; ` +
  `ayri kipte bu sayi ${a.ara}`);
console.log(`toplam dusen istek: ayri ${a.ara + a.odunc}, birlesik ${b.ara + b.odunc} ` +
  `(${b.ara + b.odunc - a.ara - a.odunc} istek yalnizca yanlis siniflandirmadan)`);
```

```
60 tik, tik basina 4 istek; uyelik servisi 15.-40. tikler arasinda kapali (25 tik), isinma 24 istek

kip         yeniden baslatma  dusen /ara  dusen /odunc  hazir degil  toparlanma
ayri                       0           0            50   12 yoklama       0 tik
birlesik                   4          48            58   14 yoklama       4 tik

birlesik kipte dusen 48 arama istegi uyelik servisine hic ihtiyac duymuyordu; ayri kipte bu sayi 0
toplam dusen istek: ayri 50, birlesik 106 (56 istek yalnizca yanlis siniflandirmadan)
```

#### Yanlış Sınıflandırmanın Bedeli

`ayri` kipinde yeniden başlatma yok. Üyelik servisi yirmi beş tik boyunca kapalıyken hazır olma ucu
on iki yoklamada olumsuz yanıt verdi — yani trafik yönlendiricisi bu düğümü listeden çıkarabilirdi —
ama süreç yaşadı. Düşen 50 istek yalnız `/odunc` isteğidir: bunlar zaten üyelik servisi olmadan
yapılamayacak işlerdi. Arama isteklerinin hiçbiri düşmedi. Bağımlılık 40. tikte döndüğünde
toparlanma **aynı tikte** oldu, çünkü süreç bellekteki dizinini hiç kaybetmemişti.

`birlesik` kipinde canlılık ucu bağımlılığa bakınca zincir kuruldu. Üç ardışık olumsuz yoklamadan
sonra denetleyici süreci öldürdü, yeni süreç aynı bağımlılığı bulamadığı için o da öldürüldü:
kapalı geçen yirmi beş tikte **dört yeniden başlatma**. Her yeniden başlatma yerel dizini sıfırladı
ve yirmi dört istek boyunca `/ara` da yanıt veremedi. Sonuç: 48 arama isteği düştü, oysa bu
isteklerin üyelik servisine hiç ihtiyacı yoktu. `/odunc` düşüşü de 50'den 58'e çıktı, çünkü
bağımlılık döndükten sonra süreç hâlâ ısınıyordu.

İki sayı bu dersin ölçüsüdür. **Toplam düşen istek 50'den 106'ya çıktı; 56 istek yalnızca yanlış
sınıflandırma yüzünden düştü.** Ve toparlanma süresi 0 tikten 4 tike çıktı: bağımlılık geri
geldikten sonra bile sistem, son yeniden başlatmanın ısınma borcunu ödeyene kadar yanıt veremedi.
Bu ikinci sayı döngünün asıl niteliğini gösteriyor — yeniden başlatma arızayı uzatır, kısaltmaz.

#### Ayrımı Kodda Korumak

İki ucun tek bir yardımcı işleve indirgenmesi en sık yapılan hatadır: `saglikDurumu()` adında bir
işlev yazılır, bütün denetimleri koşar ve iki uç da onu çağırır. Bu yazımda ayrım bir gün sonra
kaybolur, çünkü yeni bir bağımlılık denetimi eklemek isteyen kişi tek bir yere ekler ve o yer her
iki uca birden bağlıdır. Yukarıdaki düğümde iki uç ayrı satırlardır ve aralarında paylaşılan tek
şey `bagimli()` okuyucusudur; hangi ucun onu çağıracağı her seferinde açıkça yazılır.

Karar için üç maddelik bir ölçüt yeter. Sürecin kendi durumu — kilit, bellek, olay döngüsü —
canlılığa girer, çünkü yeniden başlatmak onları düzeltir. Bağımlılıklar ve ısınma hazır olmaya
girer, çünkü beklemek düzeltir, öldürmek düzeltmez. Tükenen kaynaklar — disk, bağlantı sayısı —
hiçbirine girmez; onlar önceki dersin uyarı kuralına aittir, çünkü ne yeniden başlatma ne trafik
kesme onları çözer.

#### Özet

- Canlılık "bu süreç ölmüş mü" sorusunu yanıtlar ve yeniden başlatmayı tetikler; hazır olma "şu an
  iş alabilir mi" sorusunu yanıtlar ve yalnız trafiği keser.
- Bir denetim canlılık ucuna ancak başarısızlığında yeniden başlatmak çözümse girer; bağımlılık
  denetimi bu ölçüte uymaz.
- Aynı yirmi beş tiklik bağımlılık kesintisinde ayrı tutulan uçlar sıfır yeniden başlatma üretti,
  birleşik uç dört yeniden başlatma üretti.
- Yanlış sınıflandırma, bağımlılığa hiç ihtiyacı olmayan 48 arama isteğini düşürdü; toplam düşen
  istek 50'den 106'ya çıktı ve 56 istek yalnızca bu hatadan kaynaklandı.
- Yeniden başlatma arızayı uzattı: bağımlılık döndükten sonra toparlanma 0 tik yerine 4 tik sürdü,
  çünkü her yeniden başlatma yerel dizini sıfırladı.
- Ayrım kodda tek bir koşul terimidir ve iki ucu ortak bir `saglikDurumu()` işlevine indirgemek onu
  ilk eklemede kaybettirir.

#### Sonraki Adım

Bu konu telemetriyi karara çevirdi: gösterge tanımlandı, dört sinyal hesaplandı, hedefin tersi
harcanabilir bir bütçeye dönüştü, uyarı kuralı belirtiden yazıldı ve iki sağlık ucu ayrıldı. Artık
bir sınırın aşıldığı **söylenebiliyor.** Söylenmesiyle birlikte değişen bir şey ise yok. Üyelik
servisi kapalıyken ödünç düğümü aynı isteği aynı biçimde göndermeye, aynı süre boyunca beklemeye ve
aynı hatayı almaya devam etti; hazır olma ucu bunu bildirdi ama isteği durduran, kısaltan ya da
başka bir yola çeviren hiçbir kod yoktu. Ölçmek gözlemi değiştirir, davranışı değiştirmez. Sonraki
konu davranışı değiştiren kalıpları kodun içine yerleştirir ve her birinin hangi katmanda durduğunu,
kaç çağrı yerini sardığını ve hangi çağrı yerini atladığını sayar.

## Dayanıklılık

Kısmi arızaya dayanan kalıpların uygulama kodundaki yeri: her dış çağrının süre sınırı, yeniden deneme ile geri çekilmenin fırtınaya dönüşmemesi, arızalı bağımlılığın devre kesiciyle yalıtılması, kaynak havuzlarının bölmelerle ayrıştırılması, çekirdek hizmetin sürdürüldüğü zarif bozulma, adil paylaşım için hız sınırlama ile kısıtlama, kısmi arıza türlerinin sınıflandırılması ve suçsuz kök neden çalışmasıyla olay sonrası çözümleme.

### Zaman Aşımları

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/zaman-asimlari

Her dış çağrının süre sınırı: sınırın çağrı yerine mi istemci sarmalayıcısına mı konduğunun kaynakta ölçülmesi, sarmalanan ve atlanan çağrı yerinin sayılması, iç sınır dış sınırdan büyük olduğunda üretilen öksüz tamamlanma ve iki yerleşimin sınanabilirlik farkı.

Bir önceki konu göstergeleri kurdu ve şunu bıraktı: bir sınırın aşıldığı artık görülüyor, ama
sistem o sırada hâlâ aynı davranıyor — ölçmek bir şeyi değiştirmiyor. Değiştiren şey koda konan bir
sınırdır ve bu dersin işi o sınırın **nereye** konduğunu ölçmektir.

Zaman aşımının etkisi **M19/K05 Dayanıklılık ve Güvenilirlik** kursunda ölçüldü ve burada
tekrarlanmaz. Burada ölçülen şey başkadır: aynı sınır iki ayrı katmana konduğunda kaynakta kaç
satır ve kaç yapılandırma değeri oluşur, kaç çağrı yeri kapsanır ve **kaç çağrı yeri atlanır**.

**DY1 — ölçü birimi çağrı yeridir:** kaynakta dış çağrı kuran her ifade bir çağrı yeridir. Ödünç
servisi dört bağımlılığa yedi çağrı yerinden gidiyor.

#### Düzenek

Bağımlılıklar yerel süreçlerdir: katalog ve üyelik hızlı yanıtlar, ücretlendirme 500 ms sürer,
bildirim ise bağlantıyı açık tutar ve yanıtı **hiç yazmaz**. Ödünç servisinin kendisi de ayaktadır;
iç içe sınırlar bölümünde orta katman odur.

```js
// servisler.mjs — bolunmus odunc sisteminin bagimliliklari ve odunc servisinin kendisi.
// DY2: bildirim baglantiyi acik tutar, yanit hic yazmaz. 8103/sayac tum sayaclari verir.
import { createServer, request } from "node:http";
const s = { acik: { bildirim: 0 }, toplam: { bildirim: 0 }, oksuz: 0 };  // oksuz: cagiran vazgectikten sonra biten is
for (const [port, ad, gecikme] of [[8101, "katalog", 5], [8102, "uyelik", 5], [8104, "ucretlendirme", 500]]) {
  s.acik[ad] = 0; s.toplam[ad] = 0;
  createServer((i, y) => { s.acik[ad] += 1; s.toplam[ad] += 1;
    setTimeout(() => { s.acik[ad] -= 1; y.end("{}"); }, gecikme);
  }).listen(port);
}
createServer((i, y) => {                            // 8103 bildirim + sayac ucu
  if (i.url === "/sayac") return y.end(JSON.stringify(s));
  if (i.url === "/sifirla") { s.oksuz = 0; for (const k in s.toplam) s.toplam[k] = 0; return y.end("{}"); }
  s.acik.bildirim += 1; s.toplam.bildirim += 1;
  i.on("close", () => { s.acik.bildirim -= 1; });   // yanit yazilmaz: cagri asili kalir
}).listen(8103);
createServer((i, y) => {                            // 8100 odunc: kendi ic sinirini uygular
  let vazgecti = false;
  i.on("close", () => { vazgecti = !y.writableEnded; });
  const r = request({ port: 8104, path: "/ucret", agent: false }, (c) => {
    c.resume(); c.on("end", () => { if (vazgecti) s.oksuz += 1; else y.end('{"ucret":1}'); });
  });
  r.setTimeout(Number(new URL(i.url, "http://x").searchParams.get("ic")), () => r.destroy(new Error("ic")));
  r.on("error", () => { if (!vazgecti) y.end('{"hata":"ic"}'); });
  r.end();
}).listen(8100);
```

Çağıran taraf üç dizinde üç kez yazılır. `yok/` sınırsız hâldir; `a/` sınırı **çağrı yerine** koyar,
her isteğin kendi `setTimeout` satırı ve kendi sayı değeri vardır; `b/` sınırı **istemci
sarmalayıcısına** koyar. Üçü de aynı yedi çağrı yerinden üretilir, böylece fark yalnız yerleşimden
gelir.

```js
// kur.mjs — ayni yedi cagri yerini uc yerlesimde uretir: sinirsiz, cagri yerinde, sarmalayicida.
import { mkdirSync, writeFileSync } from "node:fs";
const ATLANAN = "bildirimYinele";                   // DY4: sarmalayiciya tasinmasi unutulan cagri yeri
const C = [["odunc", "kitapAra", 8101, "/ara", 200], ["odunc", "kitapDetay", 8101, "/kitap", 200],
  ["odunc", "uyeDogrula", 8102, "/uye", 200], ["odunc", "uyeCezasi", 8102, "/ceza", 200],
  ["odunc", "gecikmeUcreti", 8104, "/ucret", 800],
  ["bildirim", "bildirimGonder", 8103, "/gonder", 800],
  ["bildirim", ATLANAN, 8103, "/yinele", 800]];
const ham = ([, ad, port, yol, sinir], koy) => `export function ${ad}() {
  return new Promise((coz, at) => {
    const r = request({ port: ${port}, path: "${yol}" }, (y) => { y.resume(); y.on("end", coz); });${
  koy ? `\n    r.setTimeout(${sinir}, () => r.destroy(new Error("zaman asimi")));` : ""}
    r.on("error", at); r.end();
  });
}`;
const sarili = ([, ad, port, yol]) => `export const ${ad} = () => istek(${port}, "${yol}", "${yol.slice(1)}");`;
for (const [d, koy, sarmala] of [["yok", false, false], ["a", true, false], ["b", false, true]]) {
  mkdirSync(d, { recursive: true });
  for (const m of ["odunc", "bildirim"]) {
    const g = C.filter((x) => x[0] === m);
    const s = sarmala ? g.filter((x) => x[1] !== ATLANAN) : [], h = g.filter((x) => !s.includes(x));
    writeFileSync(`${d}/${m}.mjs`, `// ${d}/${m}.mjs\n` +
      (h.length ? 'import { request } from "node:http";\n' : "") +
      (s.length ? 'import { istek } from "../istek.mjs";\n' : "") +
      [...s.map(sarili), ...h.map((x) => ham(x, koy))].join("\n") + "\n");
  }
}
```

`ATLANAN` bu dersin çekirdeğidir: bildirim modülündeki yineleme yolu sarmalayıcıya taşınmadı.
Sınır bir yere konduğunda, oraya uğramayan çağrı yeri kaynakta hiçbir eksiklik göstermez.

```js
// istek.mjs — istemci sarmalayicisi: her dis cagri buradan gecer, sure siniri burada uygulanir.
import { request } from "node:http";
export const VARSAYILAN = { sure: 800 };            // DY3: tek varsayilan, cagri yerinde gecilebilir
export function istek(port, yol, ad, ayar = {}) {
  const sure = ayar.sure ?? VARSAYILAN.sure;
  return new Promise((coz, at) => {
    const r = request({ port, path: yol }, (y) => { y.resume(); y.on("end", () => coz(ad)); });
    r.setTimeout(sure, () => r.destroy(new Error(`zaman asimi ${sure} ms`)));
    r.on("error", at); r.end();
  });
}
```

#### Yerleşimin Kaynakta ve Koşumdaki Bedeli

Ölçüm betiği üç dizini tarar, üç sürümü canlı servislere karşı koşturup **bağımlılıkta kaç isteğin
açık kaldığını bağımlılığın kendi sayacından okur**, sonra dış ve iç sınırı dört çiftle dener.

```js
// olc.mjs — kapsanan/atlanan cagri yeri, asili cagri ve ic ice sinirlarin celismesi.
import { readdirSync, readFileSync } from "node:fs";
import { request } from "node:http";
const TUR = 4, PENCERE = 1500, N = 10, BAGIMLILIK = 500;   // ucretlendirmenin sabit gecikmesi 500 ms
const V = { yok: "sinir yok", a: "cagri yerinde", b: "istemci sarmalayicisinda" };
const say = (m, d) => (m.match(d) || []).length;
const yaz = (g, a, ...r) => console.log(String(a).padEnd(26) + r.map((x) => String(x).padStart(g)).join(""));
const bekle = (ms) => new Promise((c) => setTimeout(c, ms));
const sayac = (yol = "/sayac") => new Promise((c) => request({ port: 8103, path: yol }, (r) => {
  let g = ""; r.on("data", (p) => (g += p)); r.on("end", () => c(g === "{}" ? null : JSON.parse(g)));
}).end());
const tara = (d) => readdirSync(d).map((a) => readFileSync(`${d}/${a}`, "utf8"))   // [dosya, satir, ham, sarmali, sinir]
  .reduce((a, m) => [a[0] + 1, a[1] + m.split("\n").filter((x) => x.trim()).length,
    a[2] + say(m, /\brequest\(\{/g), a[3] + say(m, /\bistek\(/g), a[4] + say(m, /\.setTimeout\(/g)], [0, 0, 0, 0, 0]);

async function kosum(d) {                           // yedi cagri yerini canli servislere karsi kosar
  const m = { ...(await import(`./${d}/odunc.mjs`)), ...(await import(`./${d}/bildirim.mjs`)) };
  const c = { biten: 0, hata: 0 }, ad = Object.keys(m), once = (await sayac()).acik.bildirim;
  for (let t = 0; t < TUR; t += 1) for (const a of ad) m[a]().then(() => { c.biten += 1; }, () => { c.hata += 1; });
  await bekle(PENCERE);
  return { n: ad.length * TUR, ...c, acik: (await sayac()).acik.bildirim - once };
}
const gecit = (dis, ic) => new Promise((coz) => {   // ag gecidi -> odunc:8100 -> ucretlendirme:8104
  const t0 = process.hrtime.bigint();
  const bit = (s) => coz({ s, ms: Number(process.hrtime.bigint() - t0) / 1e6 });
  const r = request({ port: 8100, path: `/ucret?ic=${ic}`, agent: false }, (y) => {
    let g = ""; y.on("data", (p) => (g += p)); y.on("end", () => bit(g.includes("hata") ? "ic hata" : "yanit"));
  });
  r.setTimeout(dis, () => r.destroy(new Error("dis")));
  r.on("error", () => bit("dis zaman asimi")); r.end();
});

const sar = readFileSync("istek.mjs", "utf8").split("\n").filter((x) => x.trim()).length;
yaz(13, "yerlesim", "dosya", "satir", "cagri yeri", "sarmalanan", "atlanan", "sinir satiri");
for (const [d, ad] of Object.entries(V)) {
  const [dos, sat, ham, srm, snr] = tara(d), b = d === "b";
  yaz(13, ad, dos + b, sat + (b ? sar : 0), ham + srm, d === "a" ? snr : srm, d === "a" ? 0 : ham, snr + b);
}
const sayi = ["a/odunc.mjs", "a/bildirim.mjs"].flatMap((f) => readFileSync(f, "utf8").match(/setTimeout\((\d+)/g));
console.log(`yapilandirma yuzeyi: cagri yerinde ${sayi.length} sayi, ${new Set(sayi).size} ayri deger ` +
  `(${[...new Set(sayi)].map((x) => x.slice(11)).join(" / ")} ms); sarmalayicida 1 varsayilan, 0 gecersiz kilma`);

console.log(`\n${TUR} tur x 7 cagri yeri; gozlem penceresi ${PENCERE} ms`);
yaz(13, "yerlesim", "cagri", "yanit", "zaman asimi", "asili kalan", "acik istek");
for (const [d, ad] of Object.entries(V)) {
  const r = await kosum(d);
  yaz(13, ad, r.n, r.biten, r.hata, r.n - r.biten - r.hata, r.acik);
}

console.log(`\nic ice sinirlar: bagimliligin sabit gecikmesi ${BAGIMLILIK} ms, her cift icin ${N} istek;` +
  `\n  olculen ms bu kosumun ortalamasidir, 100'e yuvarlandi`);
yaz(18, "dis sinir / ic sinir", "sonuc", "olculen ms", "bagimliliga cagri", "oksuz tamamlanma");
for (const [dis, ic] of [[300, 800], [800, 300], [800, 800], [800, 100]]) {
  await sayac("/sifirla");
  const r = await Promise.all(Array.from({ length: N }, () => gecit(dis, ic)));
  await bekle(BAGIMLILIK + 300);                    // bagimliligin isini bitirmesi beklenir
  const s = await sayac();
  yaz(18, `${dis} / ${ic}`, [...new Set(r.map((x) => x.s))].join("+"),
    Math.round(r.reduce((a, x) => a + x.ms, 0) / N / 100) * 100, s.toplam.ucretlendirme, s.oksuz);
}
process.exit(0);
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1; node kur.mjs && node olc.mjs; kill $SP
```

```
yerlesim                          dosya        satir   cagri yeri   sarmalanan      atlanan sinir satiri
sinir yok                             2           46            7            0            7            0
cagri yerinde                         2           53            7            7            0            7
istemci sarmalayicisinda              3           28            7            6            1            1
yapilandirma yuzeyi: cagri yerinde 7 sayi, 2 ayri deger (200 / 800 ms); sarmalayicida 1 varsayilan, 0 gecersiz kilma

4 tur x 7 cagri yeri; gozlem penceresi 1500 ms
yerlesim                          cagri        yanit  zaman asimi  asili kalan   acik istek
sinir yok                            28           20            0            8            8
cagri yerinde                        28           20            8            0            0
istemci sarmalayicisinda             28           20            4            4            4

ic ice sinirlar: bagimliligin sabit gecikmesi 500 ms, her cift icin 10 istek;
  olculen ms bu kosumun ortalamasidir, 100'e yuvarlandi
dis sinir / ic sinir                   sonuc        olculen ms bagimliliga cagri  oksuz tamamlanma
300 / 800                    dis zaman asimi               300                10                10
800 / 300                            ic hata               300                10                 0
800 / 800                              yanit               500                10                 0
800 / 100                            ic hata               100                10                 0
```

Sınırsız satır, istemcinin **varsayılan bir zaman aşımı olmadığını** söylüyor: 28 çağrının 20'si
yanıt aldı, 8'i ne yanıt ne hata aldı, pencere kapandığında bağımlılıkta hâlâ 8 istek açıktı. Bu 8
koşum süresinden bağımsızdır — pencere 15 dakika olsaydı da 8 olurdu, çünkü o çağrıları bitirecek
bir mekanizma yok.

Çağrı yerine konan sınır kaynağı 46 satırdan 53 satıra çıkarıyor — çağrı yeri başına tam bir satır —
ve **yapılandırma yüzeyini yedi ayrı sayıya dağıtıyor**; bu yedi sayının yalnız iki ayrı değeri var,
yani beşi kopya. Buna karşılık kapsam tamdır: 7 sarmalanan, 0 atlanan çağrı yeri, 0 asılı çağrı.
Sınır çağrı yerindeyken **hiçbir çağrı yeri unutulamaz**, çünkü unutulan sınır kaynakta
yazılmamış bir satır olarak görünür.

Sarmalayıcı sürümü ters yöndedir. Toplamı **daha kısadır** — iki modül ve sarmalayıcı birlikte 28
satır, sınırsız hâlin bile altında — çünkü sarmalayıcı yalnız sınırı değil, istek kurmanın tekrarını
da soğuruyor. Kaybedilen şey bağımlılık başına ayardır: çağrı yerinde katalog 200 ms'ye
ayarlıydı, sarmalayıcı sürümünde o da 800 ms'dir.

#### Atlanan Çağrı Yeri

Sarmalayıcı satırının son iki sütunu bu dersin asıl ölçüsüdür: **6 sarmalanan, 1 atlanan çağrı
yeri**. Bedeli koşumda görünüyor: 28 çağrının 4'ü asılı kalıyor, sınırsız hâlin tam yarısı.

Fark yalnız sayıda değil, bilgi durumundadır: sınırsız hâlde her çağrı yeri şüphelidir ve yazan
bunu bilir; sarmalayıcılı hâlde altı çağrı yeri güvenlidir ve bir tanesi yalan söyler. Buradan
çıkan kural şudur: **sarmalayıcı yerleşimi kapsam denetimi olmadan eksiktir.** `tara` işlevinin
yaptığı — ham çağrı kurucusunu aramak — sürekli koşması gereken bir denetimdir.

#### İç İçe Sınırların Çelişmesi

Üçüncü tablo aynı işi üç katmandan geçiriyor ve her katmanın sınırı ötekini bilmiyor.

İlk satır yanlış ayarın adıdır: **iç sınır dış sınırdan büyük.** Ağ geçidi 300 ms'de vazgeçiyor,
ödünç servisi beklemeyi sürdürüyor, bağımlılık 500 ms'de işini bitiriyor ve yanıt **kimseye
gitmiyor**: 10 öksüz tamamlanma. Hiçbir gösterge bunu hatalı saymaz; ödünç servisi
kendi açısından başarılı bir çağrı yapmıştır.

İkinci satırda iç sınır dıştakinden küçüktür: ödünç servisi önce vazgeçiyor, ağ geçidine adı konmuş
bir hata dönüyor, öksüz tamamlanma sıfıra iniyor. **Sınırların içten dışa büyümesi bir tercih değil,
bir sıralama kuralıdır.** Dördüncü satır kuralın öbür ucudur: iç sınır bağımlılığın gecikmesinin
altına indiğinde (100 ms'ye karşı 500 ms) bağımlılıkta hiçbir sorun yokken her çağrı başarısız
oluyor.

Üç satırın ortak sütunu asıl dersi veriyor: **bağımlılığa giden çağrı her ayarda 10.** Zaman aşımı
bağımlılığın yükünü azaltmaz, yalnız bekleyeni serbest bırakır.

#### Sınanabilirlik

Sınırın yeri, davranışının testte nasıl tetiklendiğini de belirler.

```js
// test/sinir.test.mjs — ayni davranis iki yerlesimde nasil tetiklenir.
import { test } from "node:test";
import assert from "node:assert/strict";
import { VARSAYILAN } from "../istek.mjs";
import * as A from "../a/bildirim.mjs";
import * as B from "../b/bildirim.mjs";
const kova = (g) => (g < 100 ? "100 ms altinda" : `~${Math.round(g / 100) * 100} ms`);
const sure = async (f) => { const t = Date.now(); await assert.rejects(f()); return Date.now() - t; };

test("cagri yerindeki sinir bir sabittir: test o kadar bekler", async () => {
  const g = await sure(A.bildirimGonder);
  assert.ok(g >= 700, `sinir 800 ms; olculen ${g}`);
  console.log(`  cagri yerinde: red ${kova(g)}`);
});
test("sarmalayicidaki sinir tek yerden dusurulur", async () => {
  VARSAYILAN.sure = 20;
  const g = await sure(B.bildirimGonder);
  assert.ok(g < 200, `sinir 20 ms; olculen ${g}`);
  console.log(`  sarmalayicida: red ${kova(g)}`);
});
test("atlanan cagri yeri sarmalayicinin ayarindan etkilenmez", async () => {
  assert.equal(await Promise.race([B.bildirimYinele().then(() => "yanit", () => "red"),
    new Promise((c) => setTimeout(() => c("hâlâ asili"), 300))]), "hâlâ asili");
});
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1
node --test --test-force-exit --test-reporter=tap test/sinir.test.mjs |
  grep -E "^(ok|not ok|#   |# (tests|pass|fail) )"
kill $SP
```

```
#   cagri yerinde: red ~800 ms
ok 1 - cagri yerindeki sinir bir sabittir: test o kadar bekler
#   sarmalayicida: red 100 ms altinda
ok 2 - sarmalayicidaki sinir tek yerden dusurulur
ok 3 - atlanan cagri yeri sarmalayicinin ayarindan etkilenmez
# tests 3
# pass 3
# fail 0
```

Çağrı yerindeki sınır bir sabittir; testin onu değiştirecek tutamağı yoktur ve gerçek süreyi
beklemek zorundadır. Sarmalayıcıdaki sınır bir değişkendir; test varsayılanı 20 ms'ye indirip aynı
dalı 100 ms'nin altında doğruluyor. Koşumdan bağımsız nicelik sınırların oranıdır: kırk kat.

Üçüncü test bu dersin ölçüsünü teste çeviriyor: atlanan çağrı yeri, varsayılan 20 ms'ye indirilmiş
olmasına rağmen 300 ms sonunda hâlâ asılıdır. **Kapsam bir sayı olarak ölçülebiliyorsa testte de
doğrulanabilir.** Çağrı yerinde yerleşimde böyle bir test yazılamaz, çünkü doğrulanacak bir kapsam
kavramı yoktur.

#### Özet

- Zaman aşımının etkisi M19/K05 Dayanıklılık ve Güvenilirlik kursunda ölçüldü; burada ölçülen sınırın
  kodda nereye yerleştiğidir. İstemcinin varsayılan sınırı yoktur: sınırsız sürümde 28 çağrının 8'i
  hiç sonuçlanmadı ve bağımlılıkta 8 istek açık kaldı.
- Çağrı yerinde yerleşim 46 satırı 53 satıra çıkarır ve yapılandırma yüzeyini yedi ayrı sayıya
  dağıtır (iki ayrı değer, beş kopya), ama kapsamı tamdır: 7 sarmalanan, 0 atlanan çağrı yeri.
- Sarmalayıcı yerleşimi daha kısadır (28 satır) ve yapılandırma yüzeyi tektir, ama kapsamı garanti
  etmez: 6 sarmalanan, 1 atlanan çağrı yeri, 4 asılı çağrı. Atlanan çağrı yeri kaynakta bir eksiklik
  olarak görünmez, ancak taramayla bulunur.
- İç sınır dış sınırdan büyükse dış katman vazgeçer, iç katman çalışmayı sürdürür: 10 isteğin
  10'unda öksüz tamamlanma. Sınırların içten dışa büyümesi bir sıralama kuralıdır.
- Bağımlılığa giden çağrı sayısı dört ayarın hepsinde 10'dur; zaman aşımı bekleyeni serbest bırakır,
  bağımlılığın yükünü azaltmaz. Sınırın değişken olması sınanabilirliği belirler: atlanan çağrı
  yeri doğrudan test edilebilir.

#### Sonraki Adım

Sınır kondu ve bekleyen serbest kaldı, ama elinde bir hata var ve o hatayla ne yapılacağı
söylenmedi. En yakın karşılık yeniden denemektir. Bu kararın nereye konduğu da bir yerleşim
sorusudur ve zaman aşımından daha tehlikelidir: zaman aşımı bağımlılığın yükünü değiştirmezken
yeniden deneme onu doğrudan çoğaltır. Sonraki ders yeniden denemenin **hangi katmanda durduğunu**
ölçer — aynı kural üç ayrı katmana kurulur, sonra üçü aynı anda açılır ve tek bir kullanıcı
isteğinin bağımlılıkta kaç çağrıya dönüştüğü sayılır.

### Yeniden Deneme ve Geri Çekilme

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/yeniden-deneme-ve-geri-cekilme

Yeniden denemenin hangi katmanda durduğunun ölçülmesi: aynı kuralın istemci sarmalayıcısında, ara katmanda ve ağ geçidinde ayrı ayrı kurulması, sağlıklı çağrı yerinin yeniden denenip denenmediği, üst üste binen üç yerleşimin çarpımı ve tekrarlanan yan etkinin teslim cinsinden sayılması.

Bir önceki ders süre sınırını koydu ve çağıranı serbest bıraktı, ama elinde bir hata kaldı. En
yakın karşılık yeniden denemektir: bağımlılık geçici olarak düşmüşse ikinci çağrı başarılı olabilir.
Bu kararın da bir yeri vardır ve zaman aşımından daha tehlikelidir — zaman aşımı bağımlılığın
yükünü değiştirmiyordu, yeniden deneme onu doğrudan çarpar.

Üstel geri çekilme, sarsıntı, tavan ve deneme bütçesi aritmetiği **M14/K06 Uygulama Mimarisi**,
**M16/K05** ve **M19/K05 Dayanıklılık ve Güvenilirlik** kurslarında kuruldu; kalıbın sistem
üzerindeki etkisi de M19/K05'te ölçüldü. Burada tekrarlanmaz. Bu dersin sorusu tektir: **yeniden
deneme hangi katmanda duruyor** ve o katman değiştiğinde bağımlılığa giden çağrı ne oluyor.

Aynı kural üç yere kurulabilir. **İstemci sarmalayıcısı** tek bir dış çağrıyı yeniden dener. **Ara
katman** isteğin gövdesini bütünüyle yeniden çalıştırır. **Ağ geçidi** servisin tamamını yeniden
çağırır ve servisin kodunda tek satırı yoktur. Üçü de aynı sözü verir; üçü de farklı bir şeyi
tekrarlar.

#### Düzenek

Bildirim bağımlılığı bu dersin ölçüsünü taşır. **DY5 — bildirim teslimi gerçekten yapar, kaybolan
şey onaydır:** her çağrıda teslim sayacı artar, ama ilk `ariza` çağrıda 503 döner. Böylece yeniden
denemenin tekrarladığı şey yalnız çağrı değil, **yan etkidir**. Üyelik sağlıklı bir bağımlılıktır
ve her zaman yanıt verir.

```js
// servisler.mjs — 8202 bildirim (arizali, teslimi sayar), 8203 uyelik (saglikli),
// 8201 odunc: iki yeniden deneme yerlesimini de tasir. Sayaclar 8202/sayac ucundadir.
import { createServer, request } from "node:http";
const s = { odunc: 0, uyelik: 0, bildirim: 0, teslim: 0, kalan: 0 };  // teslim: bildirimin gercekten gonderdigi
createServer((i, y) => {                            // 8202 bildirim
  const q = new URL(i.url, "http://x").searchParams;
  if (i.url === "/sayac") return y.end(JSON.stringify(s));
  if (i.url.startsWith("/sifirla")) {
    for (const k of ["odunc", "uyelik", "bildirim", "teslim"]) s[k] = 0;
    s.kalan = Number(q.get("ariza")); return y.end("{}");
  }
  s.bildirim += 1; s.teslim += 1;                   // teslim yapilir; onay ilk `kalan` cagride kaybolur
  if (s.kalan > 0) { s.kalan -= 1; y.statusCode = 503; }
  y.end("{}");
}).listen(8202);
createServer((i, y) => { s.uyelik += 1; y.end("{}"); }).listen(8203);   // saglikli bagimlilik

const cagir = (port, yol) => new Promise((coz) => {
  const r = request({ port, path: yol, agent: false }, (y) => { y.resume(); y.on("end", () => coz(y.statusCode)); });
  r.on("error", () => coz(0)); r.end();
});
// >>> yerlesim: istemci sarmalayicisi — yalniz sardigi cagri yerini yeniden dener
const istek = async (port, yol, deneme) => {
  for (let n = 1; ; n += 1) {
    const k = await cagir(port, yol);
    if (k < 500 || n >= deneme) return k;
  }
};
// <<<
const isle = async (ic) => {                        // odunc isteginin govdesi: iki dis cagri yeri
  await istek(8203, "/uye", 1);                     // saglikli bagimlilik, yeniden denenmez
  return istek(8202, "/gonder", ic);
};
createServer(async (i, y) => {                      // 8201 odunc
  const q = new URL(i.url, "http://x").searchParams;
  s.odunc += 1;
  // >>> yerlesim: ara katman — istegin govdesini butunuyle yeniden calistirir
  let k = 0;
  for (let n = 1; n <= Number(q.get("ara")); n += 1) { k = await isle(Number(q.get("ic"))); if (k < 500) break; }
  // <<<
  y.statusCode = k; y.end("{}");
}).listen(8201);
```

Üçüncü yerleşim ölçüm betiğindedir, çünkü ağ geçidi servisin dışındadır. **DY6 — her katmanın
deneme sayısı 3'tür**; kıyas ancak sayı sabitken anlamlıdır. `>>> yerlesim:` işaretleri kod bedelini
saymak içindir, betik onları kaynaktan okur.

```js
// olc.mjs — yeniden denemenin hangi katmanda durdugu, ust uste binen denemenin carpimi, kod bedeli.
import { readFileSync } from "node:fs";
import { request } from "node:http";
const cagir = (port, yol) => new Promise((coz) => {
  const r = request({ port, path: yol, agent: false }, (y) => { y.resume(); y.on("end", () => coz(y.statusCode)); });
  r.on("error", () => coz(0)); r.end();
});
const oku = (yol) => new Promise((c) => request({ port: 8202, path: yol }, (r) => {
  let g = ""; r.on("data", (p) => (g += p)); r.on("end", () => c(g === "{}" ? null : JSON.parse(g)));
}).end());
const yaz = (g, a, ...r) => console.log(String(a).padEnd(24) + r.map((x) => String(x).padStart(g)).join(""));
// >>> yerlesim: ag gecidi — servisin kodunda tek satir yoktur
const gecit = async (dis, ara, ic) => {
  for (let n = 1; ; n += 1) {
    const k = await cagir(8201, `/odunc?ara=${ara}&ic=${ic}`);
    if (k < 500 || n >= dis) return k;
  }
};
// <<<
const dene = async (ariza, dis, ara, ic) => {
  await oku(`/sifirla?ariza=${ariza}`);
  const k = await gecit(dis, ara, ic);
  return { k, ...(await oku("/sayac")) };
};

console.log("gecici ariza: bildirim ilk 2 cagride teslim eder ama onay kaybolur; tek kullanici istegi");
yaz(15, "yerlesim (dis/ara/ic)", "sonuc", "odunc cagri", "uyelik cagri", "bildirim cagri", "teslim");
for (const [ad, d, a, i] of [["yok (1/1/1)", 1, 1, 1], ["sarmalayici (1/1/3)", 1, 1, 3],
  ["ara katman (1/3/1)", 1, 3, 1], ["ag gecidi (3/1/1)", 3, 1, 1]]) {
  const r = await dene(2, d, a, i);
  yaz(15, ad, r.k, r.odunc, r.uyelik, r.bildirim, r.teslim);
}

console.log("\nkalici ariza: ust uste binen yeniden deneme");
yaz(15, "dis / ara / ic", "carpim", "bildirim cagri", "teslim", "uyelik cagri");
for (const [d, a, i] of [[1, 1, 1], [3, 1, 1], [3, 3, 1], [3, 3, 3], [5, 3, 3]]) {
  const r = await dene(999, d, a, i);
  yaz(15, `${d} / ${a} / ${i}`, d * a * i, r.bildirim, r.teslim, r.uyelik);
}

const kod = (dosya) => [...readFileSync(dosya, "utf8").matchAll(/\/\/ >>> yerlesim: ([^\n—]+)—[^\n]*\n([\s\S]*?)\/\/ <<</g)]
  .map(([, ad, g]) => [ad.trim(), dosya, g.trim().split("\n").length]);
const cy = [...readFileSync("servisler.mjs", "utf8").matchAll(/istek\(\d+, "[^"]+", ([^)]+)\)/g)].map((m) => m[1]);
console.log("\nkod bedeli ve kapsam");
yaz(15, "yerlesim", "dosya", "satir");
for (const [ad, d, n] of [...kod("servisler.mjs"), ...kod("olc.mjs")]) yaz(15, ad, d, n);
console.log(`odunc govdesinde ${cy.length} dis cagri yeri var; sarmalayici ${cy.filter((x) => x !== "1").length} ` +
  `tanesini yeniden deniyor, ara katman ve ag gecidi ${cy.length} tanesini de.`);
console.log("yapilandirma yuzeyi: 3 deneme sayisi, 3 ayri yerde; hicbiri otekinin degerini gormuyor.");
process.exit(0);
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1; node olc.mjs; kill $SP
```

```
gecici ariza: bildirim ilk 2 cagride teslim eder ama onay kaybolur; tek kullanici istegi
yerlesim (dis/ara/ic)             sonuc    odunc cagri   uyelik cagri bildirim cagri         teslim
yok (1/1/1)                         503              1              1              1              1
sarmalayici (1/1/3)                 200              1              1              3              3
ara katman (1/3/1)                  200              1              3              3              3
ag gecidi (3/1/1)                   200              3              3              3              3

kalici ariza: ust uste binen yeniden deneme
dis / ara / ic                   carpim bildirim cagri         teslim   uyelik cagri
1 / 1 / 1                             1              1              1              1
3 / 1 / 1                             3              3              3              3
3 / 3 / 1                             9              9              9              9
3 / 3 / 3                            27             27             27              9
5 / 3 / 3                            45             45             45             15

kod bedeli ve kapsam
yerlesim                          dosya          satir
istemci sarmalayicisi     servisler.mjs              6
ara katman                servisler.mjs              2
ag gecidi                       olc.mjs              6
odunc govdesinde 2 dis cagri yeri var; sarmalayici 1 tanesini yeniden deniyor, ara katman ve ag gecidi 2 tanesini de.
yapilandirma yuzeyi: 3 deneme sayisi, 3 ayri yerde; hicbiri otekinin degerini gormuyor.
```

#### Üç Yerleşim Aynı Arızayı Nasıl Kapatıyor

İlk tablo geçici bir arızayı üç yerleşimle karşılıyor ve **sonuç sütunu üçünde de aynı**: 503 olan
istek 200'e dönüyor. Yeniden deneme sözünü tutuyor. Fark öteki sütunlardadır.

**Bağımlılığa giden çağrı üçünde de 3.** Bu şaşırtıcı değildir; arıza iki çağrı sürüyor, üçüncüde
geçiyor. Yeniden denemenin bağımlılık üzerindeki yükü, yerleşimden değil deneme sayısından gelir.

**Sağlıklı bağımlılığa giden çağrı ise 1, 3 ve 3.** Ayrım buradadır. İstemci sarmalayıcısı yalnız
sardığı çağrı yerini yeniden dener; üyelik çağrısı bir kez yapılır ve arızayla hiç ilgisi olmayan
o bağımlılık hiç zorlanmaz. Ara katman isteğin gövdesini baştan çalıştırır, dolayısıyla **arızasız
çağrı yerini de** tekrarlar. Ağ geçidi aynısını yapar ve üstüne ödünç servisini de üç kez çağırır —
tabloda `odunc cagri` sütununda 1, 1, 3 olarak görünür.

Bundan çıkan kural bir seçim ölçütüdür: **dıştaki yerleşim ayrım yapamaz.** Ağ geçidi hangi çağrının
başarısız olduğunu bilmez, isteğin tamamını tekrarlar; ne kadar dışarı çıkılırsa tekrarlanan iş o
kadar büyür. İçteki yerleşim ayrım yapar ama yalnız kendisinden geçen çağrı yerini kapsar — bir
önceki dersin atlanan çağrı yeri sorunu burada da aynen geçerlidir.

**Teslim sütunu üç yerleşimde de 3.** Tek bir kullanıcı isteği için bildirim üç kez gönderilmiştir.
Yerleşimi değiştirmek bunu düzeltmiyor; bu yan etki yeniden denemenin kendisinden geliyor. Bir çağrı
yerinin tekrarlanabilir olup olmadığı yerleşimin değil, çağrı yerinin özelliğidir ve düzeltmesi de
oradadır: o çağrı yeri deneme sayısını 1'de tutmalıdır.

#### Üst Üste Binen Yeniden Deneme

İkinci tablo üç yerleşimi aynı anda açıyor. Kalıcı arızada her katman kendi payını sonuna kadar
harcıyor ve bağımlılığa giden çağrı **çarpım sütununu birebir izliyor**: 1, 3, 9, 27, 45. Tek bir
kullanıcı isteği bağımlılıkta 27 çağrıya dönüşüyor ve tek bir bildirim 27 kez teslim ediliyor.

Bu tablonun asıl bilgisi çarpımın kendisi değil, **hiçbir katmanın onu görmemesidir**. Ağ geçidini
yazan üç deneme yazdı, ara katmanı yazan üç deneme yazdı, sarmalayıcıyı yazan üç deneme yazdı;
hiçbiri yanlış bir sayı seçmedi. Yapılandırma yüzeyi üç ayrı yerdedir ve hiçbiri ötekinin değerini
göremez. Son satır bunun ne kadar hızlı büyüdüğünü söylüyor: dıştaki katman üçten beşe çıktığında
toplam 27'den 45'e gidiyor, yani **tek bir sayının tek bir yerde değişmesi bağımlılıkta on sekiz
çağrı ekliyor**.

`uyelik cagri` sütunu aynı satırlarda 9'dur, 27 değil. Sağlıklı bağımlılık yalnız dış iki katmanın
çarpımı kadar çağrılıyor, çünkü en içteki yerleşim onu tekrarlamıyor. Çarpımın hangi çarpanlarının
hangi çağrı yerine uygulandığı, yerleşimin doğrudan sonucudur.

#### Kod Bedeli ve Kapsam

Üçüncü tablo yerleşimlerin koddaki büyüklüğünü veriyor: istemci sarmalayıcısı 6 satır, ara katman 2
satır, ağ geçidi 6 satır — ve ağ geçidinin altı satırı **servisin kodunda değildir**. Bu, dıştaki
yerleşimi çekici kılan şeydir: servis hiç değişmeden bütün çağrı yerleri kapsanır, hatta henüz
yazılmamış olanlar bile. Bedeli de tam olarak budur; kapsamın seçici olmaması bir eksiklik değil,
o yerleşimin tanımıdır.

İçteki yerleşim tersidir: ödünç gövdesindeki iki dış çağrı yerinden birini yeniden deniyor, ötekini
bilerek bırakıyor. Seçicilik ancak her çağrı yerine tek tek karar verilerek elde edilir ve bu karar
bir kez verilmez — yeni bir çağrı yeri eklendiğinde yeniden verilir.

#### Sınanabilirlik

Yerleşim, davranışın testte nereye bağlanabileceğini de belirler.

```js
// test/deneme.test.mjs — yerlesimin sinanabilirligi: iddia nereye baglaniyor.
import { test } from "node:test";
import assert from "node:assert/strict";
import { request } from "node:http";
const cagir = (port, yol) => new Promise((coz) => {
  const r = request({ port, path: yol, agent: false }, (y) => { y.resume(); y.on("end", () => coz(y.statusCode)); });
  r.on("error", () => coz(0)); r.end();
});
const oku = (yol) => new Promise((c) => request({ port: 8202, path: yol }, (r) => {
  let g = ""; r.on("data", (p) => (g += p)); r.on("end", () => c(g === "{}" ? null : JSON.parse(g)));
}).end());
const kos = async (ariza, dis, ara, ic) => {
  await oku(`/sifirla?ariza=${ariza}`);
  for (let n = 1; n <= dis; n += 1) if (await cagir(8201, `/odunc?ara=${ara}&ic=${ic}`) < 500) break;
  return oku("/sayac");
};

test("sarmalayici yerlesimi: iddia tek cagri yerine baglanir", async () => {
  assert.equal((await kos(2, 1, 1, 3)).bildirim, 3);
  assert.equal((await kos(2, 1, 1, 3)).uyelik, 1);   // saglikli cagri yeri yeniden denenmez
});
test("ag gecidi yerlesimi: ayni iddia ic ayarla degisir", async () => {
  assert.equal((await kos(999, 3, 1, 1)).bildirim, 3);
  assert.equal((await kos(999, 3, 1, 3)).bildirim, 9);   // ag gecidi ayni, sonuc uc kati
});
test("yeniden deneme teslimi cogaltir", async () => {
  const r = await kos(2, 1, 1, 3);
  assert.equal(r.teslim, 3);                        // tek kullanici istegi, uc teslim
});
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1
node --test --test-force-exit --test-reporter=tap test/deneme.test.mjs |
  grep -E "^(ok|not ok|# (tests|pass|fail) )"
kill $SP
```

```
ok 1 - sarmalayici yerlesimi: iddia tek cagri yerine baglanir
ok 2 - ag gecidi yerlesimi: ayni iddia ic ayarla degisir
ok 3 - yeniden deneme teslimi cogaltir
# tests 3
# pass 3
# fail 0
```

İlk test sarmalayıcı yerleşiminin sınanabilirliğini gösteriyor: iddia tek bir çağrı yerine
bağlanır, sayı yereldir ve başka hiçbir ayar onu değiştiremez. İkinci test ağ geçidi yerleşiminin
sorununu **testin kendisiyle** kanıtlıyor: ağ geçidinin ayarı iki koşumda da 3'tür, ama beklenen
çağrı sayısı 3'ten 9'a çıkar, çünkü içeride başka bir sayı değişmiştir. Dıştaki yerleşimin davranışı
hakkında yazılan bir iddia, kendisinin görmediği bir ayara bağımlıdır ve o ayar değiştiğinde test
kırılır. Üçüncü test yan etkiyi doğrudan sabitliyor: bir kullanıcı isteği, üç teslim.

#### Özet

- Yeniden denemenin aritmetiği ve sistem üzerindeki etkisi M14/K06, M16/K05 ve M19/K05
  Dayanıklılık ve Güvenilirlik kurslarında ölçüldü; burada ölçülen hangi katmanda durduğudur.
- Geçici arızada üç yerleşim de isteği kurtarır ve bağımlılığa giden çağrı üçünde de 3'tür; fark
  sağlıklı bağımlılığa giden çağrıdadır: sarmalayıcıda 1, ara katmanda 3, ağ geçidinde 3. Ağ geçidi
  ayrıca ödünç servisini 1 yerine 3 kez çağırır.
- Dıştaki yerleşim ayrım yapamaz: hangi çağrının başarısız olduğunu bilmediği için isteğin tamamını
  tekrarlar. İçteki yerleşim ayrım yapar ama yalnız kendisinden geçen çağrı yerini kapsar.
- Üç yerleşim aynı anda açıldığında bağımlılığa giden çağrı çarpımı birebir izler: 1, 3, 9, 27, 45.
  Hiçbir katman ötekinin sayısını görmez; dıştaki sayının üçten beşe çıkması bağımlılıkta on sekiz
  çağrı ekler. Sağlıklı bağımlılık aynı satırlarda 9 çağrı alır, 27 değil.
- Teslim sayacı üç yerleşimde de 3'tür: tekrarlanan yan etki yerleşimin değil, çağrı yerinin
  sorunudur ve çözümü o çağrı yerinin deneme sayısını 1'de tutmaktır.
- Kod bedeli 6 / 2 / 6 satırdır ve ağ geçidinin altı satırı servisin kodunda değildir. Testte
  sarmalayıcı yerleşiminin iddiası yereldir; ağ geçidi yerleşiminin iddiası görmediği bir ayara
  bağlıdır ve o ayar değişince kırılır.

#### Sonraki Adım

Yeniden deneme geçici arızayı kapatıyor ama kalıcı arızada durumu ağırlaştırıyor: 27 çağrının 27'si
de başarısız oldu, hiçbiri bir şey kurtarmadı ve bağımlılık zaten düşmüşken üzerine yirmi yedi kat
yük bindi. Eksik olan şey bir sayı değil, bir **karardır** — bağımlılığın düştüğünü anlayıp çağrıyı
hiç yapmamak. Sonraki ders devre kesiciyi koda yerleştirir ve şunu ölçer: kesicinin durumu bağımlılık
başına mı yoksa çağrı yeri başına mı tutuluyor, aynı arızada iki yerleşim ne zaman açılıyor,
yapılandırma yüzeyi kaç parametre ve yanlış eşik hangi davranışı üretiyor.

### Devre Kesici

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/devre-kesici

Kesicinin kodda nereye yerleştiğinin ölçülmesi: durumun bağımlılık başına mı çağrı yeri başına mı tutulduğu, iki yerleşimde açılmaya kadar bağımlılığa giden çağrı, aynı eşiğin iki yerleşimde iki ayrı anlam taşıması ve seyrek çağrılan bir yolun kesicisinin hiç açılmaması.

Bir önceki ders yeniden denemenin kalıcı arızada durumu ağırlaştırdığını ölçtü: 27 çağrının 27'si
başarısız oldu ve düşmüş bir bağımlılığın üzerine yirmi yedi kat yük bindi. Eksik olan bir sayı
değil, bir karardır — bağımlılığın düştüğünü anlayıp çağrıyı hiç yapmamak.

Devre kesicinin ne olduğu, kapalı, açık ve yarı açık durumları **M14/K06 Uygulama Mimarisi**
kursunda kuruldu; kalıbın sistem üzerindeki etkisi, arızasız günün bedeli ve yanlış açılmanın
maliyeti **M19/K05 Dayanıklılık ve Güvenilirlik** kursunda ölçüldü. İkisi de burada tekrarlanmaz.
Bu dersin sorusu şudur: kesicinin **durumu nerede tutuluyor**. Bir kesici bir sayı ve bir zaman
damgasından ibarettir; o sayının nereye konduğu kalıbın ne yaptığını baştan belirler.

İki yerleşim vardır ve aralarındaki fark koddaki tek bir simgedir: durum **bağımlılık adıyla** mı
saklanıyor, yoksa **çağrı yeri adıyla** mı. Ödünç servisi aynı bildirim bağımlılığına üç ayrı çağrı
yerinden gidiyor; birinci yerleşimde üçü tek bir durumu paylaşır, ikincisinde her biri kendi
durumunu tutar.

#### Düzenek

Bildirim bağımlılığı kalıcı olarak arızalıdır ve her çağrıya 503 döner. Katalog sağlıklıdır ve
kesiciden geçmesine rağmen hiç açılmaz — kesicinin kapsamının nerede bittiğini göstermek için
oradadır. **DY7 — kesik süresi koşumdan uzundur**, böylece açılan kesici kapanmaz ve yarı açık
geçişi ölçüme karışmaz.

```js
// servisler.mjs — 8302 bildirim (surekli arizali), 8303 katalog (saglikli), 8301 odunc.
// Devre kesici iki yerlesimde de ayni koddur; degisen tek sey durumun hangi anahtarla tutuldugudur.
import { createServer, request } from "node:http";
const s = { katalog: 0, gonder: 0, hatirlat: 0, iptal: 0 };
createServer((i, y) => {                            // 8302 bildirim: her cagride 503
  if (i.url === "/sayac") return y.end(JSON.stringify(s));
  if (i.url === "/sifirla") { for (const k in s) s[k] = 0; return y.end("{}"); }
  s[i.url.slice(1)] += 1; y.statusCode = 503; y.end("{}");
}).listen(8302);
createServer((i, y) => { s.katalog += 1; y.end("{}"); }).listen(8303);

const cagir = (port, yol) => new Promise((coz) => {
  const r = request({ port, path: yol, agent: false }, (y) => { y.resume(); y.on("end", () => coz(y.statusCode)); });
  r.on("error", () => coz(0)); r.end();
});
let ESIK = 5, durum = new Map();                    // DY7: kesik suresi kosumdan uzun; yari acik gecis M14/K06'da kuruldu
const KESIK = 3_600_000;
const kesici = (a) => { if (!durum.has(a)) durum.set(a, { ardisik: 0, acikBitis: 0 }); return durum.get(a); };
const istek = async (port, yol, anahtar) => {       // kesici burada; anahtar yerlesimi belirler
  const d = kesici(anahtar);
  if (Date.now() < d.acikBitis) return 0;           // acik: cagri hic yapilmaz
  const k = await cagir(port, yol);
  if (k >= 500) { d.ardisik += 1; if (d.ardisik >= ESIK) d.acikBitis = Date.now() + KESIK; } else d.ardisik = 0;
  return k;
};
createServer(async (i, y) => {                      // 8301 odunc: uc cagri yeri ayni bagimliliga gider
  const q = new URL(i.url, "http://x").searchParams;
  if (i.url.startsWith("/kur")) { ESIK = Number(q.get("esik")); durum = new Map(); return y.end("{}"); }
  if (i.url.startsWith("/durum"))
    return y.end(JSON.stringify([...durum].map(([a, d]) => [a, Date.now() < d.acikBitis])));
  const n = Number(q.get("n")), yer = q.get("yer");
  const an = (bagimlilik, cagriYeri) => (yer === "bagimlilik" ? bagimlilik : cagriYeri);
  await istek(8303, "/kitap", an("katalog", "kitap"));
  await istek(8302, "/gonder", an("bildirim", "gonder"));
  await istek(8302, "/hatirlat", an("bildirim", "hatirlat"));
  if (n % 20 === 0) await istek(8302, "/iptal", an("bildirim", "iptal"));
  y.end("{}");
}).listen(8301);
```

`an` işlevi bu dersin tamamıdır. Kesicinin kodu değişmiyor, eşik değişmiyor, çağrı yerleri
değişmiyor; değişen tek şey `istek` çağrısına geçirilen anahtarın bağımlılığı mı yoksa çağrı yerini
mi adlandırdığı. Üç çağrı yerinden ikisi her istekte, `iptal` yolu **her yirmi istekte bir**
çağrılır; gerçek bir kod tabanında sık ve seyrek yollar hep birlikte bulunur.

```js
// olc.mjs — kesicinin durumu nerede tutuluyor: bagimlilik basina mi, cagri yeri basina mi.
import { request } from "node:http";
const N = 60;                                       // 60 kullanici istegi; iptal yolu her 20'de bir cagrilir
const cagir = (port, yol) => new Promise((coz) => {
  const r = request({ port, path: yol, agent: false }, (y) => {
    let g = ""; y.on("data", (p) => (g += p)); y.on("end", () => coz(g));
  });
  r.on("error", () => coz("")); r.end();
});
const yaz = (a, ...r) => console.log(String(a).padEnd(18) + r.map((x) => String(x).padStart(16)).join(""));

const kos = async (esik, yer) => {
  await cagir(8302, "/sifirla");
  await cagir(8301, `/kur?esik=${esik}`);
  for (let n = 1; n <= N; n += 1) await cagir(8301, `/istek?n=${n}&yer=${yer}`);
  const s = JSON.parse(await cagir(8302, "/sayac"));
  const d = JSON.parse(await cagir(8301, "/durum"));
  return { ...s, nesne: d.length, acik: d.filter(([, a]) => a).length };
};

console.log(`${N} kullanici istegi; gonder ve hatirlat her istekte, iptal her 20 istekte bir cagrilir`);
console.log("bildirim bagimliligi kalici arizalidir: her cagriya 503 doner");
yaz("esik / yerlesim", "durum nesnesi", "acik nesne", "bildirim cagri", "gonder", "hatirlat", "iptal", "katalog");
const t = {};
for (const esik of [3, 5, 10])
  for (const [yer, ad] of [["bagimlilik", "bagimlilik"], ["cagriYeri", "cagri yeri"]]) {
    const r = await kos(esik, yer);
    t[`${esik}${yer}`] = r;
    yaz(`${esik} / ${ad}`, r.nesne, r.acik, r.gonder + r.hatirlat + r.iptal, r.gonder, r.hatirlat, r.iptal, r.katalog);
  }
const top = (r) => r.gonder + r.hatirlat + r.iptal;
console.log(`\nyapilandirma yuzeyi: 2 sayi (esik, kesik suresi) ve 1 karar (durumun anahtari).`);
console.log(`ayni esik, iki yerlesimde iki ayri anlam: esik 5 icin bagimliliga giden cagri ` +
  `${top(t["5bagimlilik"])} -> ${top(t["5cagriYeri"])}; esik 10 icin ${top(t["10bagimlilik"])} -> ${top(t["10cagriYeri"])}.`);
console.log(`cagri yeri basina yerlesimde bagimlilik duzeyindeki etkin esik = esik x acilabilen cagri yeri; ` +
  `esik 10'da iptal yolu ${t["10cagriYeri"].iptal} cagriyla esige hic ulasmadi ve kesicisi kapali kaldi.`);
process.exit(0);
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1; node olc.mjs; kill $SP
```

```
60 kullanici istegi; gonder ve hatirlat her istekte, iptal her 20 istekte bir cagrilir
bildirim bagimliligi kalici arizalidir: her cagriya 503 doner
esik / yerlesim      durum nesnesi      acik nesne  bildirim cagri          gonder        hatirlat           iptal         katalog
3 / bagimlilik                   2               1               3               2               1               0              60
3 / cagri yeri                   4               3               9               3               3               3              60
5 / bagimlilik                   2               1               5               3               2               0              60
5 / cagri yeri                   4               2              13               5               5               3              60
10 / bagimlilik                  2               1              10               5               5               0              60
10 / cagri yeri                  4               2              23              10              10               3              60

yapilandirma yuzeyi: 2 sayi (esik, kesik suresi) ve 1 karar (durumun anahtari).
ayni esik, iki yerlesimde iki ayri anlam: esik 5 icin bagimliliga giden cagri 5 -> 13; esik 10 icin 10 -> 23.
cagri yeri basina yerlesimde bagimlilik duzeyindeki etkin esik = esik x acilabilen cagri yeri; esik 10'da iptal yolu 3 cagriyla esige hic ulasmadi ve kesicisi kapali kaldi.
```

#### Durum Nesnesi Sütunu

En sol sayısal sütun yerleşimin doğrudan tanımıdır: bağımlılık başına yerleşimde **2 durum
nesnesi**, çağrı yeri başına yerleşimde **4**. İkisi de aynı iki bağımlılığa gidiyor; fark, birinde
durumun bağımlılık sayısınca, ötekinde çağrı yeri sayısınca çoğalmasıdır. Bu sayı kod tabanıyla
birlikte büyür: çağrı yeri eklendiğinde bağımlılık başına yerleşimde hiçbir şey değişmez, çağrı yeri
başına yerleşimde bir durum nesnesi daha doğar ve o nesne sıfırdan öğrenmeye başlar.

Öğrenmenin paylaşılıp paylaşılmadığı **bildirim çağrı** sütununda görünüyor. Eşik 5'te bağımlılık
başına yerleşim bozuk bağımlılığa 5 çağrı yapıyor: `gonder` üç kez, `hatirlat` iki kez başarısız
oluyor, beşinci başarısızlıkta ortak kesici açılıyor ve **`iptal` yolu bağımlılığa hiç gitmiyor** —
sütunda 0. Bir çağrı yerinin öğrendiğini öteki de biliyor.

Çağrı yeri başına yerleşimde aynı eşikle 13 çağrı gidiyor: `gonder` 5, `hatirlat` 5, `iptal` 3. Her
çağrı yeri aynı gerçeği **baştan** öğreniyor. Bağımlılık zaten düşmüşken üçüncü çağrı yeri hiçbir
şey bilmiyormuş gibi davranıyor.

#### Aynı Eşiğin İki Ayrı Anlamı

Eşik satırları birlikte okunduğunda yapılandırma yüzeyinin asıl sorunu çıkıyor. Eşik 3, 5 ve 10 için
bağımlılığa giden çağrı bağımlılık başına yerleşimde 3, 5 ve 10; çağrı yeri başına yerleşimde 9, 13
ve 23. **Eşik 5 yazan kişi bir yerleşimde "bağımlılığa en çok beş başarısız çağrı" demiş oluyor,
ötekinde "her çağrı yeri için beş, toplamda on beşe kadar".** Sayı aynıdır, anlamı değildir ve
kodda hangi anlamın geçerli olduğunu söyleyen tek şey bir anahtarın adıdır.

Bağımlılık düzeyindeki etkin eşik, çağrı yeri başına yerleşimde eşik ile açılabilen çağrı yeri
sayısının çarpımıdır. Bu çarpan yapılandırmada hiçbir yerde yazmaz; kaynaktaki çağrı yeri sayısıdır
ve her yeni çağrı yeriyle sessizce büyür.

#### Açılmayan Kesici

Son satır bu dersin en sert ölçüsüdür. Eşik 10'da çağrı yeri başına yerleşimde **4 durum nesnesinin
yalnız 2'si açık** — `gonder` ve `hatirlat` açıldı, `iptal` açılmadı. Nedeni aritmetiktir: `iptal`
yolu 60 istekte yalnız 3 kez çağrılıyor ve 3 başarısızlık 10'luk eşiğe hiç ulaşmıyor.

Sonucu şudur: bağımlılık bozuk olduğu **bilindiği hâlde** o çağrı yeri her çağrıldığında yine dışarı
gidiyor. Kesici oradadır, kodu doğrudur, ayarı ötekilerle aynıdır ve hiçbir zaman işe yaramayacaktır.
Seyrek çağrılan bir çağrı yeri kendi başına bir eşiği hiçbir zaman dolduramaz — **korumasız kalan
tam da en az sınanmış yoldur.** Bağımlılık başına yerleşimde aynı yol sıfır çağrı yapıyor, çünkü sık
çağrılan komşularının öğrendiğini devralıyor.

`katalog` sütunu her satırda 60'tır. Kesici sağlıklı bağımlılığa hiç dokunmuyor ve iki yerleşimde de
aynı davranıyor; durum paylaşımı **bağımlılıklar arasında değil, aynı bağımlılığın çağrı yerleri
arasında** bir sorudur.

#### Sınanabilirlik

```js
// test/kesici.test.mjs — kesicinin durumu nerede tutuluyorsa iddia oraya baglanir.
import { test } from "node:test";
import assert from "node:assert/strict";
import { request } from "node:http";
const cagir = (port, yol) => new Promise((coz) => {
  const r = request({ port, path: yol, agent: false }, (y) => {
    let g = ""; y.on("data", (p) => (g += p)); y.on("end", () => coz(g));
  });
  r.on("error", () => coz("")); r.end();
});
const kos = async (esik, yer, n = 60) => {
  await cagir(8302, "/sifirla");
  await cagir(8301, `/kur?esik=${esik}`);
  for (let i = 1; i <= n; i += 1) await cagir(8301, `/istek?n=${i}&yer=${yer}`);
  return { s: JSON.parse(await cagir(8302, "/sayac")), d: JSON.parse(await cagir(8301, "/durum")) };
};

test("bagimlilik basina: bir cagri yerinin ogrendigini oteki de bilir", async () => {
  const { s, d } = await kos(5, "bagimlilik");
  assert.equal(s.iptal, 0);                         // iptal yolu bagimliliga hic gitmedi
  assert.equal(d.length, 2);                        // tek iddia: iki bagimlilik, iki durum nesnesi
});
test("cagri yeri basina: seyrek cagri yeri esige hic ulasmaz", async () => {
  const { s, d } = await kos(10, "cagriYeri");
  assert.equal(s.iptal, 3);                         // bagimlilik bozukken uc kez daha cagrildi
  assert.deepEqual(d.find(([a]) => a === "iptal"), ["iptal", false]);
});
test("kesicinin kapsami bagimlilikla sinirlidir", async () => {
  for (const yer of ["bagimlilik", "cagriYeri"]) assert.equal((await kos(5, yer)).s.katalog, 60);
});
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1
node --test --test-force-exit --test-reporter=tap test/kesici.test.mjs |
  grep -E "^(ok|not ok|# (tests|pass|fail) )"
kill $SP
```

```
ok 1 - bagimlilik basina: bir cagri yerinin ogrendigini oteki de bilir
ok 2 - cagri yeri basina: seyrek cagri yeri esige hic ulasmaz
ok 3 - kesicinin kapsami bagimlilikla sinirlidir
# tests 3
# pass 3
# fail 0
```

Kesicinin testte tetiklenmesi kolaydır — bağımlılığı bozuk tutmak ve eşik kadar çağrı yapmak yeter.
Zor olan **hangi durumun sınandığıdır.** Bağımlılık başına yerleşimde tek bir nesne vardır ve tek bir
iddia bütün çağrı yerlerini kapsar; birinci testin `d.length` üzerine kurduğu iddia budur. Çağrı
yeri başına yerleşimde her çağrı yerinin kendi durumu ayrı ayrı sınanmalıdır ve ikinci testin
gösterdiği gibi, sınanmayan çağrı yeri sessizce korumasız kalır. Yerleşim yalnız çalışma zamanı
davranışını değil, **testin kaç iddia yazması gerektiğini** de belirliyor: bir bağımlılık için bir,
ya da çağrı yeri sayısınca.

#### Özet

- Devre kesicinin durumları M14/K06 Uygulama Mimarisi kursunda, sistem üzerindeki etkisi M19/K05
  Dayanıklılık ve Güvenilirlik kursunda kuruldu; burada ölçülen durumun kodda nerede tutulduğudur.
  İki yerleşim arasındaki fark tek bir anahtarın adıdır.
- Durum nesnesi sayısı yerleşimin doğrudan sonucudur: bağımlılık başına 2, çağrı yeri başına 4.
  Çağrı yeri eklendiğinde ikincisinde bir durum nesnesi daha doğar ve sıfırdan öğrenmeye başlar.
- Aynı eşikte bozuk bağımlılığa giden çağrı 5'e karşı 13'tür; bağımlılık başına yerleşimde bir çağrı
  yerinin öğrendiğini öteki devralır ve `iptal` yolu bağımlılığa hiç gitmez.
- Aynı eşik iki yerleşimde iki ayrı anlam taşır: 3, 5, 10 eşikleri bağımlılıkta 3, 5, 10 çağrıya
  karşılık gelirken çağrı yeri başına yerleşimde 9, 13, 23 çağrıya karşılık gelir. Bağımlılık
  düzeyindeki etkin eşik, eşik ile çağrı yeri sayısının çarpımıdır ve bu çarpan hiçbir yapılandırmada
  yazmaz.
- Seyrek çağrılan yol kendi eşiğini dolduramaz: eşik 10'da `iptal` yolunun kesicisi 3 çağrıyla kapalı
  kaldı ve bağımlılık bozuk olduğu bilindiği hâlde her çağrıda dışarı gitmeyi sürdürdü.
- Kesici sağlıklı bağımlılığa dokunmaz (katalog her satırda 60); paylaşım sorusu bağımlılıklar
  arasında değil, aynı bağımlılığın çağrı yerleri arasındadır. Testte bağımlılık başına yerleşim tek
  iddiayla kapanır, çağrı yeri başına yerleşim çağrı yeri sayısınca iddia ister.

#### Sonraki Adım

Kesici bir sınır çizdi ama sınırın malzemesi bilgidir: koruma ancak yeterince başarısızlık
biriktikten sonra başlıyor ve seyrek yollarda hiç başlamıyor. Dahası kesici bir bağımlılığın bozuk
olduğunu anlar, o bağımlılığı çağıran işin çağıranın kaynaklarını tükettiğini anlamaz — üç çağrı
yeri de aynı süreçte, aynı havuzdan çalışıyor ve biri yavaşladığında ötekilere kalan pay küçülüyor.
O hâlde bir soru daha vardır: bulaşmayı algılamaya hiç bağlamadan, baştan kesmek mümkün müdür.
Sonraki ders kaynak havuzunu bölerek bunu dener ve havuzun kodda nerede tanımlandığını, bir
bağımlılığı kendi havuzuna almanın kaç çağrı yerine dokunduğunu, havuz sınırının hangi yollardan
atlandığını ve yanlış boyutlandırmanın hangi davranışı ürettiğini ölçer.

### Bölme Kalıbı

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/bolme-kalibi

Kaynak havuzunun kodda nereye yerleştiğinin ölçülmesi: tek paylaşılan havuz ile bağımlılık başına havuzun aynı toplam yuvayla karşılaştırılması, havuzu hiç kullanmayan çağrı yerinin sınırı aşması ve yanlış boyutlandırmanın eş zamanlılık ile süre cinsinden davranışı.

Bir önceki ders devre kesicinin durumunu koda yerleştirdi ve şunu gösterdi: koruma ancak yeterince
başarısızlık biriktikten sonra başlıyor, seyrek çağrılan yollarda hiç başlamıyor. Kesici bir
bağımlılığın bozuk olduğunu anlar; o bağımlılığı bekleyen işin **çağıranın kaynağını tükettiğini**
anlamaz. Üç çağrı yeri de aynı süreçte, aynı yuva havuzundan çalışıyor.

Bölme kalıbı ve sistem üzerindeki etkisi **M19/K05 Dayanıklılık ve Güvenilirlik** kursunda kuruldu
ve orada ölçüldü. Burada tekrarlanmaz. Bu dersin sorusu şudur: **havuz kodda nerede tanımlı**, onu
kullanmak kaç çağrı yerini değiştirir ve hangi yollar sınırın dışında kalır.

#### Düzenek

İki bağımlılık: bildirim 300 ms sürer, katalog 5 ms. Her ikisine de kırk eş zamanlı çağrı yapılır.
Her bağımlılık kendi **en çok eş zamanlı** isteğini kaydeder — havuz sınırının bağımlılık tarafından
görünen karşılığı budur. **DY8 — iki yerleşimde toplam yuva sayısı eşittir:** tek havuzda sekiz,
bağımlılık başına havuzda dört artı dört. Kıyas ancak toplam sabitken yerleşimi ölçer.

```js
// servisler.mjs — 8402 bildirim (yavas), 8403 katalog (hizli), 8401 odunc: havuzlar burada tanimlanir.
import { createServer, request, Agent } from "node:http";
const N = 40;                                       // her akista 40 es zamanli cagri
const s = { bildirim: { acik: 0, enCok: 0 }, katalog: { acik: 0, enCok: 0 } };
const servis = (ad, gecikme, port) => createServer((i, y) => {
  const d = s[ad]; d.acik += 1; d.enCok = Math.max(d.enCok, d.acik);
  setTimeout(() => { d.acik -= 1; y.end("{}"); }, gecikme);
}).listen(port);
servis("bildirim", 300, 8402);
servis("katalog", 5, 8403);

// >>> havuz tanimi: iki yerlesim, ayni toplam yuva
const havuzlar = (yerlesim, b, k) => {
  if (yerlesim === "tek") { const a = new Agent({ maxTotalSockets: b + k }); return () => a; }
  const t = { 8402: new Agent({ maxTotalSockets: b }), 8403: new Agent({ maxTotalSockets: k }) };
  return (port) => t[port];
};
// <<<
const cagir = (port, yol, havuz) => new Promise((coz) => {
  const r = request({ port, path: yol, agent: havuz }, (y) => { y.resume(); y.on("end", coz); });
  r.on("error", coz); r.end();
});

createServer(async (i, y) => {                      // 8401 odunc: bir kosumu bastan sona yurutur
  const q = new URL(i.url, "http://x").searchParams;
  const al = havuzlar(q.get("yerlesim"), Number(q.get("b")), Number(q.get("k")));
  const atla = Number(q.get("atla"));               // havuzu hic kullanmayan cagri yeri sayisi
  for (const d of Object.values(s)) { d.acik = 0; d.enCok = 0; }
  const t0 = Date.now();
  let biten = 0, katalogOnce = null;
  const yavas = Array.from({ length: N }, (_, j) => cagir(8402, "/gonder", j < atla ? false : al(8402)));
  const hizli = Array.from({ length: N }, () => cagir(8403, "/kitap", al(8403)).then(() => { biten += 1; }));
  Promise.race(yavas).then(() => { katalogOnce = biten; });
  await Promise.all([...yavas, ...hizli]);
  y.end(JSON.stringify({ n: N, katalogOnce, bildirim: s.bildirim.enCok, katalog: s.katalog.enCok,
    sure: Math.round((Date.now() - t0) / 500) * 500 }));
}).listen(8401);
```

`katalogOnce` bu dersin ana ölçüsüdür: **ilk bildirim çağrısı tamamlanana kadar kaç katalog çağrısı
bitebildi.** Katalog beş milisaniyede yanıt verdiği için, kaynağı olan bir akış kırk çağrısını
bildirim daha ilk yanıtını vermeden bitirmiş olmalıdır. Bitiremiyorsa yuva bulamamış demektir.

`atla` parametresi havuzu hiç kullanmayan çağrı yerini üretir: `agent: false` ile yapılan istek
kendi yuvasını açar ve havuz tablosuna hiç bakmaz. Kaynakta bu bir eksiklik gibi görünmez; yalnızca
başka bir seçenektir.

```js
// olc.mjs — havuzun nerede tanimlandigi, atlanan yol ve yanlis boyutlandirma.
import { readFileSync } from "node:fs";
import { request } from "node:http";
const cagir = (yol) => new Promise((c) => request({ port: 8401, path: yol, agent: false }, (r) => {
  let g = ""; r.on("data", (p) => (g += p)); r.on("end", () => c(JSON.parse(g)));
}).end());
const yaz = (a, ...r) => console.log(String(a).padEnd(30) + r.map((x) => String(x).padStart(18)).join(""));

console.log("her akista 40 es zamanli cagri: bildirim 300 ms, katalog 5 ms; toplam yuva her yerlesimde 8");
console.log("sure sutunu bu kosumun olcumudur, 500 ms'ye yuvarlandi; beklenen deger ceil(40/yuva) x 300 ms");
yaz("yerlesim", "katalog once biten", "bildirim eszaman", "katalog eszaman", "sure ms");
for (const [ad, y] of [["tek paylasilan havuz", "yerlesim=tek&b=4&k=4&atla=0"],
  ["bagimlilik basina havuz", "yerlesim=ayri&b=4&k=4&atla=0"],
  ["bagimlilik basina + 8 atlanan", "yerlesim=ayri&b=4&k=4&atla=8"]]) {
  const r = await cagir(`/kos?${y}`);
  yaz(ad, `${r.katalogOnce} / ${r.n}`, r.bildirim, r.katalog, r.sure);
}

console.log("\nyanlis boyutlandirma: bagimlilik basina havuzda yuva sayilari");
yaz("bildirim yuva / katalog yuva", "bildirim eszaman", "katalog eszaman", "sure ms");
for (const [b, k] of [[1, 4], [4, 4], [40, 4], [4, 1]]) {
  const r = await cagir(`/kos?yerlesim=ayri&b=${b}&k=${k}&atla=0`);
  yaz(`${b} / ${k}`, r.bildirim, r.katalog, r.sure);
}

const kaynak = readFileSync("servisler.mjs", "utf8");
const blok = kaynak.match(/\/\/ >>> havuz tanimi[^\n]*\n([\s\S]*?)\/\/ <<</)[1].trim().split("\n").length;
const cy = (kaynak.match(/cagir\(\d+, "/g) || []).length;
console.log(`\nhavuz tanimi ${blok} satir, tek yerde; dis cagri yeri ${cy} ve hicbiri degismedi — ` +
  `havuzu cagri yerine tasiyan sey ${(kaynak.match(/al\(\d+\)/g) || []).length} adet al(port) ifadesidir.`);
process.exit(0);
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1; node olc.mjs; kill $SP
```

```
her akista 40 es zamanli cagri: bildirim 300 ms, katalog 5 ms; toplam yuva her yerlesimde 8
sure sutunu bu kosumun olcumudur, 500 ms'ye yuvarlandi; beklenen deger ceil(40/yuva) x 300 ms
yerlesim                      katalog once biten  bildirim eszaman   katalog eszaman           sure ms
tek paylasilan havuz                      0 / 40                 8                 1              2000
bagimlilik basina havuz                  40 / 40                 4                 4              3000
bagimlilik basina + 8 atlanan            40 / 40                12                 4              2500

yanlis boyutlandirma: bagimlilik basina havuzda yuva sayilari
bildirim yuva / katalog yuva    bildirim eszaman   katalog eszaman           sure ms
1 / 4                                          1                 4             12000
4 / 4                                          4                 4              3000
40 / 4                                        40                 4               500
4 / 1                                          4                 1              3000

havuz tanimi 5 satir, tek yerde; dis cagri yeri 2 ve hicbiri degismedi — havuzu cagri yerine tasiyan sey 2 adet al(port) ifadesidir.
```

#### Havuz Kodda Nerede Duruyor

Son satır yerleşimin kod bedelini veriyor: **havuz tanımı beş satır ve tek yerde.** İki dış çağrı
yerinin ikisi de değişmedi; havuzu çağrı yerine taşıyan şey `al(port)` biçiminde iki ifadedir. Bu,
bölme kalıbının önceki üç kalıptan ayrıldığı noktadır. Zaman aşımı bir davranış ekler, yeniden
deneme bir döngü ekler, kesici bir durum tutar — hepsi çağrı yerinin **yaptığı işi** değiştirir.
Havuz ise yalnızca çağrının hangi kaynağı kullanacağını söyler; çağrı yerinin kodu aynı kalır.

Bunun sonucu şudur: **havuz yerleşimi kaynakta neredeyse görünmezdir.** Bir çağrı yerinin doğru
havuzu kullanıp kullanmadığı, o çağrı yerinin kendisine bakılarak anlaşılmaz; hangi ifadenin
geçirildiğine bakmak gerekir. Bir satırda `al(8402)` yerine `false` yazmak sözdizimsel olarak
kusursuzdur.

#### İki Yerleşim, Aynı Sekiz Yuva

İlk tablonun ilk iki satırı aynı sekiz yuvayı iki ayrı biçimde dağıtıyor.

Tek paylaşılan havuzda `katalog once biten` **0 / 40**. İlk bildirim yanıtı gelene kadar tek bir
katalog çağrısı bile bitemedi. Nedeni `bildirim eszaman` sütununda: yavaş bağımlılık sekiz yuvanın
sekizini de tutuyor ve katalog akışı en fazla bir yuva bulabiliyor. Katalog bağımlılığında hiçbir
sorun yok, katalog beş milisaniyede yanıt veriyor; yine de kırk isteğin kırkı bekliyor. Havuz
paylaşıldığında **bir bağımlılığın yavaşlığı, onunla hiç ilgisi olmayan akışın gecikmesine
dönüşüyor.**

Bağımlılık başına havuzda aynı sütun **40 / 40**. Katalog akışı kendi dört yuvasını buluyor ve kırk
çağrısını bildirim daha ilk yanıtını vermeden bitiriyor. Bildirim akışının eş zamanlılığı sekizden
dörde düşüyor ve toplam süre 2000 ms'den 3000 ms'ye çıkıyor — bölmenin bedeli budur ve tabloda
doğrudan görünür. Kalıp bulaşmayı **algılamaya hiç bağlamadan** kesiyor: hiçbir eşik, hiçbir sayaç,
hiçbir öğrenme süresi yok. Sınır baştan konmuştur.

#### Atlanan Yol

Üçüncü satır bu dersin en değerli ölçüsüdür. Havuz yapılandırması değişmedi — bildirim için hâlâ
dört yuva yazıyor. Kırk çağrının sekizi havuzu kullanmıyor, kendi yuvasını açıyor. Sonuç:
**bildirim eş zamanlılığı 4 yerine 12.**

Havuz sınırı dört diyor, bağımlılık on iki görüyor. Fazlalık tam olarak atlanan çağrı
sayısıdır ve yapılandırmanın hiçbir yerinde görünmez: yuva sayısını okuyan biri dört görür,
bağımlılığı izleyen biri on iki. Bir önceki iki dersin kapsam sorunu burada en sinsi hâlindedir,
çünkü havuz yerleşimi çağrı yerinin kodunu değiştirmediği için **eksikliği de değiştirmez** — atlanan
çağrı yeri, sarılmış olanla neredeyse aynı görünür.

Komşu akışın hâlâ kurtulduğuna dikkat etmek gerekir: `katalog once biten` yine 40 / 40. Atlanan yol
bildirim havuzunun sınırını deliyor ama katalog havuzunu delmiyor. **Bölme kalıbının koruması,
kaçağın kendi bölmesinde kalmasıdır**; kaçak yine de o bölmenin sözünü geçersiz kılar.

#### Yanlış Boyutlandırma

İkinci tablo havuz boyutunun iki ucunu gösteriyor ve ikisi de kalıbı işlevsiz bırakıyor, ama farklı
yönlerden.

**Bir yuva** bildirim akışını sıraya diziyor: eş zamanlılık 1, süre 12000 ms. Bağımlılıkta hiçbir
sorun yokken kırk çağrı arka arkaya yapılıyor. Bölme kalıbı burada koruma değil, darboğaz üretiyor;
komşu akış kurtuluyor ama korunan akışın kendisi sistemin en yavaş parçası oluyor.

**Kırk yuva** ters uçtur: eş zamanlılık 40, süre 500 ms. Havuz kodda vardır, tanımlıdır, adı
geçmektedir ve hiçbir şeyi bağlamamaktadır. Bu, yapılandırmanın en tehlikeli hâlidir — kalıbın
olmadığı durumdan ayırt edilemez, ama kaynağa bakan biri bölmenin kurulduğunu görür.

Son satır sınırın komşu tarafta da geçerli olduğunu söylüyor: katalog havuzu bire indirildiğinde
katalog eş zamanlılığı 1 oluyor. **Her havuz kendi akışının tavanıdır**; bölme yalnız yavaş
bağımlılığı sınırlamaz, hızlı olanı da sınırlar ve yanlış boyutlandırma iki yönde de aynı biçimde
bedelini verir.

#### Sınanabilirlik

```js
// test/havuz.test.mjs — havuz siniri gercekten baglayici mi.
import { test } from "node:test";
import assert from "node:assert/strict";
import { request } from "node:http";
const kos = (yol) => new Promise((c) => request({ port: 8401, path: yol, agent: false }, (r) => {
  let g = ""; r.on("data", (p) => (g += p)); r.on("end", () => c(JSON.parse(g)));
}).end());

test("bagimlilik basina havuz komsu akisi kurtarir", async () => {
  assert.equal((await kos("/kos?yerlesim=tek&b=4&k=4&atla=0")).katalogOnce, 0);
  assert.equal((await kos("/kos?yerlesim=ayri&b=4&k=4&atla=0")).katalogOnce, 40);
});
test("atlanan cagri yeri havuz sinirini asar", async () => {
  assert.equal((await kos("/kos?yerlesim=ayri&b=4&k=4&atla=0")).bildirim, 4);
  assert.equal((await kos("/kos?yerlesim=ayri&b=4&k=4&atla=8")).bildirim, 12);   // 4 + 8
});
test("havuz siniri yalniz kendi bagimliligini baglar", async () => {
  for (const b of [1, 4, 40]) assert.equal((await kos(`/kos?yerlesim=ayri&b=${b}&k=4&atla=0`)).katalog, 4);
});
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1
node --test --test-force-exit --test-reporter=tap test/havuz.test.mjs |
  grep -E "^(ok|not ok|# (tests|pass|fail) )"
kill $SP
```

```
ok 1 - bagimlilik basina havuz komsu akisi kurtarir
ok 2 - atlanan cagri yeri havuz sinirini asar
ok 3 - havuz siniri yalniz kendi bagimliligini baglar
# tests 3
# pass 3
# fail 0
```

Bölme kalıbının sınanabilirliği önceki üç kalıptan farklıdır: kalıbın davranışı çağıranın kodunda
değil, **bağımlılığın gördüğü eş zamanlılıkta** ortaya çıkar. Bir zaman aşımı ya da kesici testi
çağıranın aldığı hataya bakarak yazılabilir; havuz testi ancak karşı tarafın kaç bağlantı gördüğünü
sayarak yazılabilir. İkinci test bunu doğrudan kullanıyor: havuz dört diyor, iddia on iki üzerine
kuruluyor ve atlanan çağrı yeri böylece bir sayı hâline geliyor. Üçüncü test sınırın kapsamını
sabitliyor — bildirim havuzu ne olursa olsun katalog dörttedir.

#### Özet

- Bölme kalıbının etkisi M19/K05 Dayanıklılık ve Güvenilirlik kursunda ölçüldü; burada ölçülen
  havuzun kodda nerede tanımlandığıdır. Havuz tanımı beş satır ve tek yerdedir; iki dış çağrı yeri de
  değişmez, havuzu taşıyan şey çağrıya geçirilen `al(port)` ifadesidir.
- Aynı sekiz yuva tek havuzda toplandığında, yavaş bağımlılık yuvaların hepsini tutar ve ilk bildirim
  yanıtına kadar biten katalog çağrısı 0 / 40 olur; katalog eş zamanlılığı 1'e iner.
- Bağımlılık başına havuzda aynı sayı 40 / 40'tır. Bedeli tablodadır: bildirim eş zamanlılığı 8'den
  4'e, süre 2000 ms'den 3000 ms'ye. Koruma bir eşiğe ya da öğrenmeye bağlı değildir, baştan konmuştur.
- Kırk çağrının havuzu kullanmayan sekizi, yapılandırma dört yuva derken bağımlılığa on iki eş
  zamanlı bağlantı ulaştırır. Fazlalık tam olarak atlanan çağrı sayısıdır ve yapılandırmada
  görünmez.
- Havuz boyutunun iki ucu da kalıbı işlevsiz bırakır: bir yuva eş zamanlılığı 1'e indirip süreyi
  12000 ms'ye çıkarır, kırk yuva ise sınırı hiç bağlamaz (eş zamanlılık 40, süre 500 ms) ve kaynakta
  bölme kurulmuş gibi görünür.
- Her havuz kendi akışının tavanıdır: katalog havuzu 1'e indirildiğinde katalog eş zamanlılığı da 1
  olur. Havuz testi ancak bağımlılığın gördüğü eş zamanlılık sayılarak yazılabilir.

#### Sonraki Adım

Dört kalıp da aynı şeyi yaptı: bir sınır koydu ve o sınırın ötesindeki isteği reddetti. Zaman aşımı
bekleyeni bıraktı, yeniden deneme çoğalttı ve vazgeçti, kesici çağrıyı hiç yapmadı, bölme yuva
vermedi. Hepsinde geriye aynı şey kaldı — **karşılanmayan bir istek**. Reddedilen o isteğin nereye
gideceği bu dört kalıbın hiçbirinin sorusu değildi; hepsi arızayı çevreledi, hiçbiri onu
karşılamadı. Bir sonraki adım bu boşluğu doldurur: sınıra çarpan isteğin tam yanıt yerine eksik ama
kullanılabilir bir yanıt alması, yani sistemin bozulurken bile bir şey döndürmesi.

### Zarif Bozulma

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/zarif-bozulma

Zarif bozulmanın koda yerleştirilmesi: çekirdek ve isteğe bağlı alan ayrımının şema nesnesinde toplanması, çağrı yerinde try/catch ile paylaşılan sarmalayıcının 7 ve 12 satır olarak karşılaştırılması, 14 çağrı yerinin 6'sının sarmalanıp 2'sinin atlanması ve yedek değerin gövdeyi gerçek sıfırdan ayırt edilemez kılması.

Bölme kalıbı bozulan bağımlılığı kendi havuzuna kapattı: havuz dolduğunda çağrı hızla başarısız
oluyor ve öteki bağımlılıkların havuzları etkilenmiyor. Kurtarılan şey aşağı akıştır. Yukarı akışta
hiçbir şey değişmedi — çağrı yeri hâlâ bir hata nesnesi alıyor, uç hâlâ o hatayı yukarı fırlatıyor
ve istemci hâlâ boş dönüyor. Panel yanıtının beş alanından dördü elde olsa bile, beşincisi gelmediği
için beşi birden atılıyor.

Zarif bozulma bu boşluğu kapatır. Etkisi **Dayanıklılık ve Güvenilirlik** kursunda ölçüldü: hangi
politikanın kaç isteği kurtardığı, arızasız günün bedeli ve çekirdek bir bağımlılık düştüğünde
kalıbın elinden ne geldiği orada sayıldı. Burada ölçülen başka bir şey: bu kalıp **koda nereye
yerleşiyor**, kaç çağrı yerini kapsıyor ve hangi çağrı yerlerini atlıyor.

#### Şema Nesnesi ve Çağrı Yerleri

Düzenek bölünmüş ödünç sisteminin beş servisidir. Her biri yerel bir `node:http` sunucusudur ve
bayrağı kapatıldığında bağlantıyı reddeder — çağıran taraf için bu, üzerinde denetimi olmayan bir
hata nesnesi demektir.

```js
// bozulma/servisler.mjs — bolunmus odunc sisteminin yukari akis servisleri.
// Her biri yerel bir node:http surecidir; CALISIYOR bayragi kapatilinca baglanti reddedilir.
import http from "node:http";

export const PORT = {};
export const CALISIYOR = { uyelik: 1, odunc: 1, ucretlendirme: 1, bildirim: 1, katalog: 1 };
export const GOVDE = {
  uyelik: { ad: "N.A.", durum: "etkin" },
  odunc: { acik: 3, gecikmis: 1 },
  ucretlendirme: { borc: 12.5 },
  bildirim: { okunmamis: 4 },
  katalog: { oneri: ["K-1180"] },
};
const AYAK = [];

export async function baslat() {
  for (const ad of Object.keys(GOVDE)) {
    const s = http.createServer((req, res) => {
      if (CALISIYOR[ad] === 0) { res.socket.destroy(); return; }
      res.writeHead(200, { "content-type": "application/json" });
      res.end(JSON.stringify(GOVDE[ad]));
    });
    await new Promise((r) => s.listen(0, "127.0.0.1", r));
    PORT[ad] = s.address().port;
    AYAK.push(s);
  }
}
export function durdur() { for (const s of AYAK) s.close(); }

// Yalin cagri yeri: servis dusunce hata firlatir. Sarmalanmamis her cagri boyle davranir.
export async function cagir(ad) {
  const y = await fetch(`http://127.0.0.1:${PORT[ad]}/`);
  if (y.ok === false) throw new Error(`${ad}: HTTP ${y.status}`);
  return y.json();
}
```

**DY10 — panelin alan ayrımı.** Üye panelinin beş alanı vardır. `uye` ile `odunc` çekirdektir: bu
ikisi olmadan sayfa bir şey göstermez. `borc`, `bildirim` ve `oneri` isteğe bağlıdır ve her birinin
yerine konacak bir yedek değeri vardır. **DY11 — makbuz ve özet uçları panelden önce yazılmıştır** ve
kendi çağrılarını yaparlar; bu, bozulma kalıbı sisteme sonradan girdiğinde tipik olan durumdur.

Ayrım kodun içine dağılmaz, tek bir şema nesnesinde durur. İki yerleşim aynı şemayı okur.

```js
// bozulma/panel.mjs — panel yanitinin semasi ve kalibin iki yerlesimi.
import { cagir } from "./servisler.mjs";

// DY10: yanitin cekirdek/istege bagli ayrimi sema nesnesinde durur, kodun icine dagilmaz.
export const SEMA = {
  uye: { servis: "uyelik", cekirdek: 1 },
  odunc: { servis: "odunc", cekirdek: 1 },
  borc: { servis: "ucretlendirme", cekirdek: 0, yedek: { borc: 0 } },
  bildirim: { servis: "bildirim", cekirdek: 0, yedek: { okunmamis: 0 } },
  oneri: { servis: "katalog", cekirdek: 0, yedek: { oneri: [] } },
};

// Yerlesim A — cagri yerinde: her istege bagli cagri kendi try/catch'ini ve kendi yedegini tasir.
export async function panelA() {
  const y = { uye: await cagir("uyelik"), odunc: await cagir("odunc") };
  try { y.borc = await cagir("ucretlendirme"); } catch { y.borc = { borc: 0 }; }
  try { y.bildirim = await cagir("bildirim"); } catch { y.bildirim = { okunmamis: 0 }; }
  try { y.oneri = await cagir("katalog"); } catch { y.oneri = { oneri: [] }; }
  return y;
}

// Yerlesim B — paylasilan sarmalayici: yedek semadan okunur, dusen alan yanitta isaretlenir.
export async function istegeBagli(alan, eksik) {
  const t = SEMA[alan];
  try { return await cagir(t.servis); } catch { eksik.push(alan); return t.yedek; }
}
export async function panelB() {
  const eksik = [];
  const y = { uye: await cagir("uyelik"), odunc: await cagir("odunc") };
  y.borc = await istegeBagli("borc", eksik);
  y.bildirim = await istegeBagli("bildirim", eksik);
  y.oneri = await istegeBagli("oneri", eksik);
  return { ...y, eksik };
}

// Ayni servislere dokunan oteki iki uc. Ikisi de panelden once yazildi.
export async function makbuz() {
  const u = await cagir("uyelik");
  const b = await cagir("ucretlendirme");
  return { uye: u.ad, borc: b.borc };
}
export async function ozet() {
  const o = await cagir("odunc");
  const b = await cagir("bildirim");
  return { acik: o.acik, okunmamis: b.okunmamis };
}
```

#### Kaynak Taraması ve Koşum

Ölçüm iki bölümdür. Önce kaynak metni taranır ve her çağrı yeri sınıflandırılır: çekirdek bir
servise mi gidiyor, isteğe bağlı bir servise gidip sarmalanmış mı, yoksa isteğe bağlı bir servise
gidip sarmalanmamış mı. Üçüncüsü **atlanan çağrı yeridir**. Sonra aynı kod arızalı senaryolarda
koşturulur ve tarama ile koşum karşılaştırılır.

```js
// bozulma/olcum.mjs — once kaynak sayilir, sonra ayni kod arizali senaryolarda kosturulur.
import { readFileSync } from "node:fs";
import { baslat, durdur, CALISIYOR, GOVDE } from "./servisler.mjs";
import { SEMA, panelA, panelB, makbuz, ozet } from "./panel.mjs";

const ISTEGE_BAGLI = new Set(Object.values(SEMA).filter((t) => !t.cekirdek).map((t) => t.servis));
const yer = [], satir = {};              // her cagri yeri: hangi islev, hangi servis, sarmali mi
let simdi = null;
for (const s of readFileSync(new URL("./panel.mjs", import.meta.url), "utf8").split("\n")) {
  const bas = s.match(/^export async function (\w+)/);
  if (bas) simdi = bas[1];
  if (simdi === null) continue;
  if (s.trim() !== "") satir[simdi] = (satir[simdi] ?? 0) + 1;
  for (const m of s.matchAll(/cagir\("(\w+)"\)/g))            // adi yazili yalin cagri yeri
    yer.push({ islev: simdi, servis: m[1], sarmali: s.includes("try {") });
  for (const m of s.matchAll(/istegeBagli\("(\w+)"/g))        // sarmalayiciya verilmis cagri yeri
    yer.push({ islev: simdi, servis: SEMA[m[1]].servis, sarmali: true });
  if (s.startsWith("}")) simdi = null;
}
const sinif = (y) => (ISTEGE_BAGLI.has(y.servis) ? (y.sarmali ? "sarmalanan" : "atlanan") : "cekirdek");

console.log("-- kaynak taramasi --");
console.log(`${"islev".padEnd(13)}${"satir".padStart(6)}${"cagri yeri".padStart(11)}${"cekirdek".padStart(9)}${"sarmalanan".padStart(11)}${"atlanan".padStart(8)}`);
for (const i of [...new Set(yer.map((y) => y.islev))]) {
  const t = yer.filter((y) => y.islev === i);
  const n = (k) => String(t.filter((y) => sinif(y) === k).length);
  console.log(`${i.padEnd(13)}${String(satir[i]).padStart(6)}${String(t.length).padStart(11)}${n("cekirdek").padStart(9)}${n("sarmalanan").padStart(11)}${n("atlanan").padStart(8)}`);
}
console.log(`toplam ${yer.length} cagri yeri; ${yer.filter((y) => sinif(y) === "sarmalanan").length} sarmalandi, ${yer.filter((y) => sinif(y) === "atlanan").length} atlandi`);
console.log(`yerlesim A ${satir.panelA} satir, yerlesim B ${satir.panelB}+${satir.istegeBagli}=${satir.panelB + satir.istegeBagli} satir\n`);

await baslat();
const UC = { panelA, panelB, makbuz, ozet };
const SENARYO = [["hepsi saglam", []], ["ucretlendirme durdu", ["ucretlendirme"]],
  ["ucretlendirme+bildirim+katalog", ["ucretlendirme", "bildirim", "katalog"]], ["uyelik durdu", ["uyelik"]]];
console.log("-- kosum: dusen servise giden cagri yerlerinin akibeti --");
console.log(`${"senaryo".padEnd(31)}${"dokunulan".padStart(10)}${"yedek donen".padStart(12)}${"hata firlatan".padStart(14)}${"yanitlayan uc".padStart(14)}`);
const govde = {};
for (const [ad, dusen] of SENARYO) {
  for (const s of Object.keys(CALISIYOR)) CALISIYOR[s] = dusen.includes(s) ? 0 : 1;
  let yanit = 0;
  for (const [uc, f] of Object.entries(UC)) {
    try { govde[`${ad}|${uc}`] = JSON.stringify(await f()); yanit += 1; } catch { /* uc dustu */ }
  }
  const d = yer.filter((y) => dusen.includes(y.servis));
  const yedek = d.filter((y) => sinif(y) === "sarmalanan").length;
  console.log(`${ad.padEnd(31)}${String(d.length).padStart(10)}${String(yedek).padStart(12)}${String(d.length - yedek).padStart(14)}${`${yanit}/4`.padStart(14)}`);
}

for (const s of Object.keys(CALISIYOR)) CALISIYOR[s] = 1;
GOVDE.ucretlendirme.borc = 0;                        // borcu gercekten olmayan bir uye
const sifirA = JSON.stringify(await panelA()), sifirB = JSON.stringify(await panelB());
const arizaliA = govde["ucretlendirme durdu|panelA"], arizaliB = govde["ucretlendirme durdu|panelB"];
console.log("\n-- yedegin sessiz yanlisi: servis dustu / borc gercekten 0 --");
console.log(`A dustu: ${arizaliA}`);
console.log(`A sifir: ${sifirA}`);
console.log(`ayni govde mi: ${arizaliA === sifirA}`);
console.log(`B dustu: ${arizaliB.slice(arizaliB.indexOf('"borc"'))}`);
console.log(`B sifir: ${sifirB.slice(sifirB.indexOf('"borc"'))}`);
console.log(`ayni govde mi: ${arizaliB === sifirB}; isaretin bedeli ${arizaliB.length - arizaliA.length} bayt, yanitin %${(((arizaliB.length - arizaliA.length) / arizaliA.length) * 100).toFixed(1)}'i`);
durdur();
```

```
-- kaynak taramasi --
islev         satir cagri yeri cekirdek sarmalanan atlanan
panelA            7          5        2          3       0
panelB            8          5        2          3       0
makbuz            5          2        1          0       1
ozet              5          2        1          0       1
toplam 14 cagri yeri; 6 sarmalandi, 2 atlandi
yerlesim A 7 satir, yerlesim B 8+4=12 satir

-- kosum: dusen servise giden cagri yerlerinin akibeti --
senaryo                         dokunulan yedek donen hata firlatan yanitlayan uc
hepsi saglam                            0           0             0           4/4
ucretlendirme durdu                     3           2             1           3/4
ucretlendirme+bildirim+katalog          8           6             2           2/4
uyelik durdu                            3           0             3           1/4

-- yedegin sessiz yanlisi: servis dustu / borc gercekten 0 --
A dustu: {"uye":{"ad":"N.A.","durum":"etkin"},"odunc":{"acik":3,"gecikmis":1},"borc":{"borc":0},"bildirim":{"okunmamis":4},"oneri":{"oneri":["K-1180"]}}
A sifir: {"uye":{"ad":"N.A.","durum":"etkin"},"odunc":{"acik":3,"gecikmis":1},"borc":{"borc":0},"bildirim":{"okunmamis":4},"oneri":{"oneri":["K-1180"]}}
ayni govde mi: true
B dustu: "borc":{"borc":0},"bildirim":{"okunmamis":4},"oneri":{"oneri":["K-1180"]},"eksik":["borc"]}
B sifir: "borc":{"borc":0},"bildirim":{"okunmamis":4},"oneri":{"oneri":["K-1180"]},"eksik":[]}
ayni govde mi: false; isaretin bedeli 17 bayt, yanitin %11.9'i
```

#### İki Yerleşim ve Atlanan Çağrı Yeri

İki yerleşim aynı işi görüyor ve panel ucunda aynı sonucu veriyor: her ikisi de üç isteğe bağlı
çağrıyı sarmalıyor, ikisi de ücretlendirme düştüğünde yanıt üretiyor. Satır bedeli farklı — çağrı
yerindeki yerleşim 7 satır, paylaşılan sarmalayıcılı yerleşim 8 artı 4, toplam 12 satır. İlk bakışta
çağrı yerindeki yerleşim ucuz görünüyor.

Farkı yaratan şey satır sayısı değil, **yedeğin nerede yazıldığı**. Çağrı yerindeki yerleşimde
`{ borc: 0 }` ifadesi `catch` bloğunun içine gömülüdür; şemadaki `yedek` alanı ile aralarında hiçbir
bağ yoktur. İkisi ayrı ayrı değiştirilebilir ve sessizce ayrışabilirler. Paylaşılan sarmalayıcıda
yedek tek yerden, şemadan okunur; bir alanın çekirdek olup olmadığına dair karar da orada durur.

Asıl ölçü ikinci tablodadır. Ücretlendirme servisi düştüğünde o servise giden **üç** çağrı yeri var:
paneldeki iki yerleşim ve makbuz ucundaki çağrı. İkisi yedek değer döndürüyor, biri hâlâ hata
fırlatıyor — makbuz ucu yanıt veremiyor, dört uçtan üçü ayakta. Üç servis birden düştüğünde
dokunulan çağrı yeri sekize çıkıyor, altısı yedek döndürüyor, ikisi fırlatıyor ve özet ucu da
düşüyor.

Bozulma kalıbı panele yerleşti, sisteme yerleşmedi. Şema nesnesi `borc` alanını "isteğe bağlı" ilan
ediyor, ama bu ilan yalnızca o alanı okuyan kod yolunu bağlıyor. Makbuz ucu aynı servisi çağırıyor,
aynı hatayı alıyor ve o hatayı yukarı fırlatmayı sürdürüyor. **Kalıbın kapsamı, kalıbın kurulduğu
yer değil, kalıbın çağrıldığı yerlerin kümesidir.** Kaynak taraması bu kümeyi altı sarmalanan ve iki
atlanan çağrı yeri olarak sayıyor; iki atlanan çağrı yeri koşumda iki düşen uca dönüşüyor.

Son satır ayrımın bir başka yüzünü gösteriyor. Üyelik servisi düştüğünde ona giden üç çağrı yerinin
üçü de çekirdek sınıfındadır ve hiçbiri sarmalanmamıştır; bu bir eksiklik değil, tasarımın kendisidir.
Yine de özet ucu ayakta kalıyor, çünkü üyelik onun için bir bağımlılık değil. **Çekirdeklik alanın
değil, ucun özelliğidir**: aynı servis bir uçta çekirdek, başka bir uçta hiç yoktur.

#### Yedeğin Sessiz Yanlışı

Son bölüm kalıbın kendi ürettiği kusuru ölçüyor. Ücretlendirme servisi düşükken çağrı yerindeki
yerleşimin ürettiği gövde ile borcu gerçekten sıfır olan bir üyenin sağlam gündeki gövdesi
karşılaştırılıyor: **bayt bayt aynı**. `borc: 0` değeri iki anlama geliyor ve istemcinin bu ikisini
ayırt etmesinin hiçbir yolu yok. Üye ekranında "borcunuz yok" yazıyor, gerçekte on iki buçuk liralık
bir borç var ve kimse bir hata görmüyor.

Bu, bozulma kalıbının hata fırlatan koddan **daha kötü** olabildiği tek noktadır. Fırlatılan hata
görünürdür: uç düşer, kayda geçer, sayaca yansır. Yedek değer görünmez, çünkü başarılı bir yanıtın
içinde durur. Sarmalayıcılı yerleşim bunu düzeltiyor: `eksik` listesi hangi alanın yedekle
doldurulduğunu yanıtın kendisinde taşıyor ve iki gövde artık ayrışıyor. Bedeli on yedi bayt, bu
yanıtın yüzde 11,9'u.

Bu on yedi bayt yalnızca bir ek alan değil, kalıbın **gözlemlenebilir** hâlidir. İşareti taşımayan
bir bozulma, bu kursun ilk iki konusundaki bütün araçlar için görünmezdir: günlük kaydı üretilmez,
sayaç artmaz, gösterge kıpırdamaz. Yedek değerin bir işareti yoksa bozulma ölçülemez; ölçülemeyen
bozulma da bir süre sonra normal sanılır.

#### Özet

- Zarif bozulma koda bir şema nesnesi olarak yerleşir: hangi alanın çekirdek olduğu, hangisinin
  isteğe bağlı olduğu ve yedeğinin ne olduğu tek yerde durur. Kalıbın etkisi Dayanıklılık ve
  Güvenilirlik kursunda ölçüldü; buradaki ölçü yerleşimin kendisidir.
- İki yerleşim karşılaştırıldı: çağrı yerinde `try/catch` 7 satır, paylaşılan sarmalayıcı 12 satır.
  Satır farkının karşılığı, yedek değerin `catch` bloğuna gömülmek yerine şemadan okunmasıdır.
- Kaynak taraması 14 çağrı yeri buldu: 6'sı sarmalandı, 2'si atlandı, kalanı çekirdek çağrılardır.
  Atlanan iki çağrı yeri, panelden önce yazılmış makbuz ve özet uçlarındadır.
- Koşum taramayı doğruladı: ücretlendirme düştüğünde 3 çağrı yerinin 2'si yedek döndürdü, 1'i hata
  fırlattı ve dört uçtan biri düştü. Üç servis birden düştüğünde 8 çağrı yerinin 6'sı yedek döndürdü
  ve iki uç düştü.
- İşaretsiz yedek sessizce yanlış veri üretir: servis düşükken üretilen gövde ile borcu gerçekten
  sıfır olan üyenin gövdesi bayt bayt aynıdır. `eksik` listesi bu ikisini ayırır ve yanıta 17 bayt,
  yüzde 11,9 ekler.
- Çekirdeklik alanın değil ucun özelliğidir: üyelik servisi panel ve makbuz için çekirdek, özet ucu
  için hiç yoktur.

#### Sonraki Adım

Buraya kadarki beş kalıp da aynı yöne bakıyor: aşağı akıştaki bir bağımlılık bozulduğunda bu sistemin
ne yapacağı. Zaman aşımı beklemeyi kesiyor, yeniden deneme yeniden soruyor, devre kesici sormayı
bırakıyor, bölme kaynakları ayırıyor, bozulma yanıtı küçültüyor. Hepsinin öznesi aynı: **çağıran
taraf**.

Aynı sistemin öteki yüzü hiç ele alınmadı. Bu servisler yalnız çağırmıyor, çağrılıyor da; ve çağıran
tarafın kaç istek göndereceğine dair hiçbir sınır yok. Bir istemcinin yanlış yazılmış bir döngüsü,
tek bir uca dakikada binlerce istek gönderebilir ve buradaki hiçbir kalıp bunu durduramaz — hepsi
zaten gelmiş bir isteğin başına ne geleceğini düzenler. Sıradaki ders sınırı **kabul tarafına**
koyar ve şunu ölçer: sınırlayıcı kodun hangi katmanında durur, sınır kime uygulanır, hangi yollar
sınırın dışında kalır ve anahtar yanlış seçildiğinde ne olur.

### Hız Sınırlama ve Kısıtlama

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/hiz-sinirlama-ve-kisitlama

Hız sınırlayıcının koddaki yerleşimi ve anahtarı: aynı sınırlayıcının ağ geçidinde 8 yol-erişim çiftinden 2'sini, servis içi ara katmanda 6'sını görmesi, 2 çiftin hiçbir yerleşimin dışında kalması ve tek bir 20 parametresinin anahtar seçimine göre 20 ile 100.000 arasında değişen bir tavana dönüşmesi.

Önceki beş kalıbın öznesi hep çağıran taraftı: bu sistem başka bir servisi çağırdığında ne olacağı
düzenlendi. Panelin isteğe bağlı alanları yedekle dolduruldu, hata fırlatan çağrı yerleri sayıldı ve
kalıbın kapsamadığı iki uç bulundu. Hepsi aşağı akışa bakıyordu.

Yukarı akış hiç düzenlenmedi. Ödünç servisi yalnız çağırmıyor, çağrılıyor da ve kaç çağrı alacağına
dair hiçbir sınırı yok. Gecelik bir toplu iş yanlış yazılmış bir döngüye girdiğinde, o servisin
önündeki zaman aşımı, yeniden deneme ve devre kesici hiçbir işe yaramaz — hepsi zaten kabul edilmiş
bir isteğin başına ne geleceğini düzenler. Sınır kabul tarafına konur ve adı **hız sınırlamadır**;
reddetmek yerine geciktiren biçimine **kısıtlama** denir.

Sınırlayıcının sistem üzerindeki etkisi ve algoritmaların karşılaştırması — sabit pencere, kayan
pencere, jeton kovası — **Dayanıklılık ve Güvenilirlik** kursunda ve hız sınırlayıcı tasarımına
ayrılmış vaka çalışmasında ölçüldü. Burada algoritma sabit tutulur ve iki başka soru sorulur:
sınırlayıcı **kodun hangi katında** durur ve sınır **kime** uygulanır.

#### Üç Kat, Bir Sınırlayıcı

Sınırlayıcı tek bir işlevdir; onu üç yere koymak mümkündür. Ağ geçidine konursa bütün servislerin
önünde tek yerde durur. Servis içi bir ara katmana konursa yalnız o servisi korur ama o servisin
her yolunu korur. Çağrı yerine konursa yalnız sarmalandığı çağrıyı düzenler — bu üçüncüsü giden
kısıtlamadır ve kapsamı, bir önceki dersteki sarmalanan çağrı yeri ölçüsüyle aynı biçimde sayılır.
İlk ikisi burada gerçekten kurulur.

```js
// sinir/kat.mjs — sinirlayici ve onu barindirabilecek iki kat: ag gecidi ve servis ici ara katman.
import http from "node:http";

// Sabit pencere. Pencere DY12 geregi kosumdan uzun secildi; algoritma secimi burada olculmuyor.
export function sinirlayici(anahtar, sinir = 20, pencere = 60_000) {
  const kova = new Map();
  let bas = Date.now();
  const s = { gecen: 0, red: 0, uye: new Map(), gorulen: 0 };
  return { s, kova, izin(i) {
    if (Date.now() - bas >= pencere) { kova.clear(); bas = Date.now(); }
    s.gorulen += 1;
    const n = (kova.get(anahtar(i)) ?? 0) + 1;
    kova.set(anahtar(i), n);
    const t = s.uye.get(i.uye) ?? { gecen: 0, red: 0 };
    if (n <= sinir) { t.gecen += 1; s.gecen += 1; } else { t.red += 1; s.red += 1; }
    s.uye.set(i.uye, t);
    return n <= sinir;
  } };
}

const istek = (req) => ({ uc: req.url, istemci: req.headers["x-istemci"], uye: req.headers["x-uye"] });
const yaz = (res, kod, g) => { res.writeHead(kod, { "content-type": "application/json" }); res.end(JSON.stringify(g)); };

// Servis: ara katman verilirse butun yollarini kapsar, verilmezse hicbirini.
export function servis(yollar, araKatman) {
  return http.createServer((req, res) => {
    const i = istek(req);
    if (yollar.includes(i.uc) === false) return yaz(res, 404, { yol: i.uc });
    if (araKatman && araKatman.izin(i) === false) return yaz(res, 429, { sinir: 1 });
    yaz(res, 200, { yol: i.uc });
  });
}

// Ag gecidi: sinir yalnizca yazilmis onek esleserse uygulanir. Oteki onekler gecer.
export function agGecidi(limit, onek, hedef) {
  return http.createServer(async (req, res) => {
    const i = istek(req);
    if (i.uc.startsWith(onek) && limit.izin(i) === false) return yaz(res, 429, { sinir: 1 });
    const y = await fetch(`http://127.0.0.1:${hedef(i.uc)}${i.uc}`,
      { headers: { "x-istemci": i.istemci ?? "", "x-uye": i.uye ?? "" } });
    yaz(res, y.status, await y.json());
  });
}

export async function dinle(s) { await new Promise((r) => s.listen(0, "127.0.0.1", r)); return s.address().port; }
export async function gonder(port, uc, istemci, uye) {
  const y = await fetch(`http://127.0.0.1:${port}${uc}`, { headers: { "x-istemci": istemci, "x-uye": uye } });
  await y.json();
  return y.status;
}
```

Ölçüm iki bölümdür. Önce kapsam: dört yol, iki erişim yolu ve iki yerleşim kurulur; her istek için
hangi sınırlayıcının o isteği **gördüğü** sayılır. Sonra anahtar: kapsamı geniş olan yerleşim sabit
tutulur ve dört ayrı anahtar seçimi aynı istek akışında koşturulur.

```js
// sinir/olcum.mjs — once kapsam (hangi yol hangi katmanda sayiliyor), sonra anahtar secimi.
import { sinirlayici, servis, agGecidi, dinle, gonder } from "./kat.mjs";

const ODUNC = ["/odunc/al", "/odunc/liste", "/odunc-toplu/liste"];
const KATALOG = ["/katalog/ara"];
const ONEK = "/odunc/";                      // ag gecidine yazilan tek onek

// Iki ayri duzenek: A'da sinir yalniz ag gecidinde, B'de yalniz servis ici ara katmanda.
const gwA = sinirlayici(() => "genel", 1e9);
const araB = sinirlayici(() => "genel", 1e9);
const kur = async (ara) => {
  const o = await dinle(servis(ODUNC, ara)), k = await dinle(servis(KATALOG, null));
  return { o, k, hedef: (u) => (u.startsWith("/katalog") ? k : o) };
};
const A = await kur(null), B = await kur(araB);
const gwPortA = await dinle(agGecidi(gwA, ONEK, A.hedef));
const gwPortB = await dinle(agGecidi(sinirlayici(() => "genel", 1e9), "/hicbiri/", B.hedef));

console.log("-- kapsam: bir istek hangi yerlesimde sayiliyor --");
console.log(`${"yol".padEnd(20)}${"erisim".padEnd(11)}${"ag gecidi".padStart(10)}${"ara katman".padStart(12)}`);
const kapsam = { gecit: 0, ara: 0, hicbiri: 0, toplam: 0 };
for (const yol of [...ODUNC, ...KATALOG]) {
  for (const erisim of ["ag gecidi", "dogrudan"]) {
    const g0 = gwA.s.gorulen, a0 = araB.s.gorulen;
    await gonder(erisim === "dogrudan" ? (yol.startsWith("/katalog") ? A.k : A.o) : gwPortA, yol, "olcum", "u0");
    await gonder(erisim === "dogrudan" ? (yol.startsWith("/katalog") ? B.k : B.o) : gwPortB, yol, "olcum", "u0");
    const g = gwA.s.gorulen > g0, a = araB.s.gorulen > a0;
    kapsam.toplam += 1; if (g) kapsam.gecit += 1; if (a) kapsam.ara += 1; if (!g && !a) kapsam.hicbiri += 1;
    console.log(`${yol.padEnd(20)}${erisim.padEnd(11)}${(g ? "sayildi" : "atlandi").padStart(10)}${(a ? "sayildi" : "atlandi").padStart(12)}`);
  }
}
console.log(`${kapsam.toplam} yol-erisim ciftinden ag gecidi ${kapsam.gecit}, ara katman ${kapsam.ara} tanesini goruyor; ` +
  `${kapsam.hicbiri} cift hicbir yerlesimin disinda\n`);

// DY13: gecelik toplu is once kesintisiz kosar, mobil istemci uyelerini sirayla esitler, web sonra gelir.
const AKIS = [["toplu", "u5", 40], ["mobil", "u1", 12], ["mobil", "u2", 8], ["mobil", "u3", 4], ["web", "u4", 6]];
const PLAN = [];
for (const [istemci, uye, n] of AKIS)
  for (let i = 0; i < n; i += 1) PLAN.push({ istemci, uye, uc: istemci === "mobil" ? ODUNC[i % 3] : ODUNC[1] });

const ANAHTAR = [["sabit", () => "genel"], ["istemci", (i) => i.istemci],
  ["uye", (i) => i.uye], ["istemci+uc", (i) => `${i.istemci} ${i.uc}`]];
const UYE = ["u1", "u2", "u3", "u4", "u5"], SINIR = 20, ETKIN_UYE = 5000;
console.log(`-- anahtar secimi: ${PLAN.length} istek, kova basina sinir ${SINIR}, yerlesim ara katman --`);
console.log(`${"anahtar".padEnd(12)}${"kova".padStart(5)}${"tavan".padStart(7)}${"gecen".padStart(7)}${"red".padStart(5)}` +
  UYE.map((u) => `${u} g/r`.padStart(11)).join(""));
for (const [ad, f] of ANAHTAR) {
  const lim = sinirlayici(f, SINIR);
  const port = await dinle(servis(ODUNC, lim));
  for (const p of PLAN) await gonder(port, p.uc, p.istemci, p.uye);
  console.log(`${ad.padEnd(12)}${String(lim.kova.size).padStart(5)}${String(lim.kova.size * SINIR).padStart(7)}` +
    `${String(lim.s.gecen).padStart(7)}${String(lim.s.red).padStart(5)}` +
    UYE.map((u) => `${lim.s.uye.get(u).gecen}/${lim.s.uye.get(u).red}`.padStart(11)).join(""));
}
console.log(`\nyazilan tek parametre ${SINIR}; gercek tavan kova sayisiyla carpilir. Anahtar uye secilirse kova ` +
  `sayisi uye sayisidir: DY14'un ${ETKIN_UYE} etkin uyesinde ayni yapilandirma ${ETKIN_UYE * SINIR} istek/pencere demektir.`);
process.exit(0);
```

```
-- kapsam: bir istek hangi yerlesimde sayiliyor --
yol                 erisim      ag gecidi  ara katman
/odunc/al           ag gecidi     sayildi     sayildi
/odunc/al           dogrudan      atlandi     sayildi
/odunc/liste        ag gecidi     sayildi     sayildi
/odunc/liste        dogrudan      atlandi     sayildi
/odunc-toplu/liste  ag gecidi     atlandi     sayildi
/odunc-toplu/liste  dogrudan      atlandi     sayildi
/katalog/ara        ag gecidi     atlandi     atlandi
/katalog/ara        dogrudan      atlandi     atlandi
8 yol-erisim ciftinden ag gecidi 2, ara katman 6 tanesini goruyor; 2 cift hicbir yerlesimin disinda

-- anahtar secimi: 70 istek, kova basina sinir 20, yerlesim ara katman --
anahtar      kova  tavan  gecen  red     u1 g/r     u2 g/r     u3 g/r     u4 g/r     u5 g/r
sabit           1     20     20   50       0/12        0/8        0/4        0/6      20/20
istemci         3     60     46   24       12/0        8/0        0/4        6/0      20/20
uye             5    100     50   20       12/0        8/0        4/0        6/0      20/20
istemci+uc      5    100     50   20       12/0        8/0        4/0        6/0      20/20

yazilan tek parametre 20; gercek tavan kova sayisiyla carpilir. Anahtar uye secilirse kova sayisi uye sayisidir: DY14'un 5000 etkin uyesinde ayni yapilandirma 100000 istek/pencere demektir.
```

#### Sınırın Dışında Kalan Yol

Ağ geçidindeki yerleşim sekiz yol-erişim çiftinden **ikisini** görüyor. Bu, konumun kendisiyle
ilgili değil, ağ geçidine yazılan tek satırla ilgilidir: `/odunc/` öneki. Ödünç servisinin üçüncü
yolu `/odunc-toplu/liste` adını taşıyor ve bu ad öneke uymuyor. Yol sonradan eklendi, ağ geçidindeki
önek listesi güncellenmedi ve sınırlayıcı o yolu hiç görmedi. Kod hiçbir yerde hata vermiyor; kural
sessizce eksik kalıyor.

İkinci ve daha ağır boşluk erişim yolundadır. Ağ geçidi yalnız kendi üzerinden geçen isteği görebilir.
Servisin kendi dinleyicisine doğrudan ulaşan her istek — başka bir iç servisin çağrısı, bir bakım
betiği, bir sağlık yoklaması — sınırlayıcıya hiç uğramaz. Tablodaki dört `dogrudan` satırının
dördünde de ağ geçidi sütunu `atlandi` yazıyor. Ağ geçidi bir sınır değil, bir **kapıdır**; kapının
yanından geçilebiliyorsa sınır yoktur.

Servis içi ara katman altı çifti görüyor, çünkü işleyici tablosunun tamamını sarmalıyor: yolun adı
ne olursa olsun, istek nereden gelirse gelsin, o servise ulaşan her istek sayılıyor. Bedeli ise
kapsamın dar olmasıdır — yalnız o süreç. Katalog servisi ayrı bir süreçtir ve kendi ara katmanı
yoktur; `/katalog/ara` iki erişim yolunda da hiçbir sınırlayıcının görüş alanına girmiyor. İki kat
birlikte kurulsa bile bu iki çift açık kalır, çünkü boşluk katmanda değil, **sınırlayıcının hiç
kurulmadığı süreçtedir**.

Kısıtlama biçimi bu tabloyu değiştirmez. Sınırlayıcı 429 döndürmek yerine isteği bir kuyruğa alıp
geciktirse de, kuyruğa alabildiği istekler yine yalnız gördüğü isteklerdir. Reddetmek ile geciktirmek
arasındaki seçim isteğin akıbetiyle ilgilidir; kapsam sorusu ondan bağımsızdır ve önce sorulur.

#### Anahtar Kimi Sınırlıyor

İkinci tablo aynı yerleşimi, aynı sınırı ve aynı yetmiş isteklik akışı dört ayrı anahtarla
koşturuyor. Akış DY13'tür: gecelik toplu iş kırk istekle başlıyor, mobil istemci üç üyesini sırayla
eşitliyor, web istemcisi en sonda geliyor.

**Sabit anahtar** — yani anahtar işlevinin her istek için aynı değeri döndürmesi — bütün istemcileri
tek kovada topluyor. Toplu iş ilk yirmi isteğiyle kovayı bitiriyor ve geri kalan elli istek
reddediliyor: dört üyenin dördü de sıfır geçen istekle kalıyor. Mobil istemcinin on iki isteği, web
istemcisinin altı isteği, hiçbiri sisteme ulaşmıyor. Bu, sınırlayıcı olmayan bir sistemden **daha
kötüdür**: sınırlayıcı olmasaydı toplu işin yükü servisi yavaşlatırdı, sınırlayıcı varken toplu iş
öteki herkesi tamamen dışarıda bırakıyor. Yanlış anahtar, koruma aracını bir yayılma aracına
çeviriyor.

**İstemci anahtarı** kovayı üçe bölüyor ve toplu işi kendi kotasına hapsediyor; geçen istek sayısı
yirmiden kırk altıya çıkıyor. Ama mobil istemcinin arkasında üç ayrı üye var ve üçü aynı kovayı
paylaşıyor. Mobil kotasını ilk iki üye tüketiyor, üçüncü üyenin dört isteğinin dördü de reddediliyor.
Bu üye dört istek gönderdi ve hiçbiri geçmedi; sınırı aşan davranış başkasınındı. İstemci anahtarı
paylaşılan bir istemcinin arkasındaki kullanıcıları birbirinin kotasından yedirir.

**Üye anahtarı** bunu düzeltiyor: mobil üç üyenin üçü de bütün isteklerini geçiriyor ve yalnız toplu
iş sınırlanıyor. Ölçülen akışta **istemci+uç** anahtarı da aynı sonucu veriyor, çünkü mobil istemcinin
yirmi dört isteği üç uca dağılıyor ve hiçbir kova sınıra ulaşmıyor.

#### Gerçek Tavan

Son iki sütun yapılandırma yüzeyinin en sinsi yanını gösteriyor. Yapılandırmada yazılan tek sayı
`20`. Sistemin gerçekte kabul ettiği üst sınır ise `20 × kova sayısı`: sabit anahtarda 20, istemci
anahtarında 60, üye anahtarında 100.

Kova sayısı bir yapılandırma değeri değildir; veriden gelir. Üye anahtarı seçildiğinde kova sayısı
etkin üye sayısına eşittir. DY14 bu sistemde beş bin etkin üye varsayar; aynı `20` değeri o zaman
pencere başına yüz bin istek demektir. Yapılandırma dosyasına bakan biri servisin yirmi istekle
sınırlandığını sanır. **Anahtar seçimi, sınır değerinin kendisinden daha belirleyicidir** ve ikisi
yapılandırmanın iki ayrı yerinde durduğu için aralarındaki çarpım hiçbir yerde yazılı değildir.

Bu, sınırlayıcının neden tek başına yeterli olmadığını da söylüyor. Üye başına sınır tek bir üyeyi
frenler; servisin toplam kapasitesini korumaz. Toplam kapasiteyi koruyan sınır sabit anahtarlı
olandır ve onun da ilk tabloda görülen bedeli vardır. İki anahtarın birlikte kurulması gerekir ve
bu, iki ayrı kova kümesi, iki ayrı sınır değeri ve iki ayrı reddetme nedeni demektir.

#### Özet

- Sınırlayıcının etkisi ve algoritma seçimi başka yerde ölçüldü; buradaki ölçü yerleşim ve
  anahtardır. Aynı sınırlayıcı işlevi ağ geçidine ve servis içi ara katmana kuruldu.
- Ağ geçidi 8 yol-erişim çiftinden 2'sini, servis içi ara katman 6'sını görüyor. Fark iki nedenden
  gelir: ağ geçidine yazılan `/odunc/` öneki sonradan eklenen `/odunc-toplu/liste` yolunu tutmuyor ve
  servise doğrudan ulaşan hiçbir istek ağ geçidinden geçmiyor.
- 2 çift hiçbir yerleşimin dışındadır: katalog ayrı bir süreçtir ve orada sınırlayıcı hiç kurulmadı.
  Boşluk katman seçiminde değil, kalıbın kurulmadığı süreçtedir.
- Sabit anahtar bütün istemcileri tek kovada topluyor: toplu iş yirmi isteğiyle kovayı bitiriyor ve
  öteki dört üyenin toplam otuz isteğinin hepsi reddediliyor. Bu sonuç sınırlayıcısız durumdan kötüdür.
- İstemci anahtarı paylaşılan istemcinin arkasındaki üyeleri birbirinin kotasından yedirir: dört
  istek gönderen üye dördünde de reddedildi. Üye anahtarında aynı üye dört isteğini de geçirdi.
- Yazılan tek parametre `20`, gerçek tavan `20 × kova sayısı`: sabit anahtarda 20, istemcide 60,
  üyede 100 ve DY14'ün beş bin üyesinde 100.000. Kova sayısı yapılandırmadan değil veriden gelir.

#### Sonraki Adım

Bu konuda altı kalıp kuruldu ve her biri belirli bir bozulma biçimine karşı yazıldı: yanıt gelmiyor,
yanıt gecikiyor, bağımlılık sürekli düşüyor, kaynaklar tükeniyor, bir alan eksik, çağrı çok fazla.
Her kalıbın kodda bir yeri, bir kapsamı ve o kapsamın dışında kalan çağrı yerleri var.

Kalıpların hangi bozulma biçimine karşılık geldiği ise hiçbir yerde tek tek yazılmadı. Bir
bağımlılık bağlantıyı reddettiğinde, yavaş yanıt verdiğinde, boş gövde döndürdüğünde ya da geçerli
görünen yanlış bir değer döndürdüğünde uygulama kodu bunları **farklı biçimlerde** görür — kimi bir
hata nesnesi, kimi bir zaman aşımı, kimi hiçbir belirti. Sıradaki ders bu görünümleri tek tek
üretir, her birinin çağrı yerinde nasıl göründüğünü yazar, hangi kalıbın hangisini yakaladığını
eşleştirir ve geriye kalanı sayar: hiçbir kalıbın karşılamadığı bozulma biçimleri.

### Arıza Kipleri

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/ariza-kipleri

Arıza kiplerinin uygulama kodundaki karşılığı: aynı ucun sekiz ayrı biçimde bozulması, çağrı yerinde beş farklı hata kimliği üretmesi, kalıp yığınının bunlardan beşini görüp üçünü hiç görmemesi ve altı kipte çağrı yerine aynı sıfır değerinin ulaşması.

Bu konuda altı kalıp kuruldu. Her biri bir bozulma biçimine karşı yazıldı, her birinin kodda bir
yeri var ve her birinin kapsamadığı çağrı yerleri sayıldı. Kalıpların ne yaptığı belli; hangi arızaya
karşılık geldikleri ise tek tek yazılmadı.

Arıza kipi sözlüğü — bir bağımlılığın kaç ayrı biçimde bozulabileceği ve her birinin sistem
üzerindeki etkisi — **Dayanıklılık ve Güvenilirlik** kursunda kuruldu. Burada sorulan soru başkadır:
bu kiplerin her biri **uygulama kodunda ne olarak görünür**. Çağrı yeri bir hata nesnesi mi alır, bir
zaman aşımı mı, boş bir gövde mi, yoksa hiçbir belirti göstermeyen geçerli bir yanıt mı. Bu, konunun
kapanışıdır ve kalıpların toplam kapsamını sayar.

#### Sekiz Kipin Kodda Üretilmesi

**DY15 — sekiz kip.** Ücretlendirme servisinin tek bir ucu, sekiz ayrı biçimde bozulur: bağlantı
reddi, geciken yanıt, sunucu hatası, boş gövde, ortasından kesilen gövde, doğru yapıda ama yanlış
tipte alan, geçerli ve doğru tipte ama yanlış değer ve zaman aşımının hemen altında kalan yavaşlık.
Son üçü HTTP 200 ve geçerli JSON döndürür. Gerçek borç 12,5'tir ve gecikme ücreti hesabı bu değere
beş ekler.

**DY16 — yığının ayarları.** Zaman aşımı 120 ms, deneme sayısı 2, devre kesici eşiği 3 ardışık
başarısızlık, kip başına 5 çağrı. Bu değerler önceki derslerde ölçüldü; burada sabit tutulurlar.

```js
// kip/kip.mjs — bagimliligin sekiz bozulma bicimi ve uzerine yiginlanmis kaliplar.
import http from "node:http";

export const GERCEK = 12.5, ZAMAN_ASIMI = 120, DENEME = 2, ESIK = 3, CAGRI = 5;
const gonder = (res, g) => { res.writeHead(200, { "content-type": "application/json" }); res.end(JSON.stringify(g)); };

// Her kip ayni ucu farkli bir bicimde bozar. Sonuncu ikisi HTTP 200 ve gecerli JSON dondurur.
export const KIP = {
  "baglanti-reddi": null,                                     // dinleyici yok
  "zaman-asimi": (res) => setTimeout(() => gonder(res, { borc: GERCEK }), 400),
  "sunucu-hatasi": (res) => { res.writeHead(500, { "content-type": "application/json" }); res.end("{}"); },
  "bos-govde": (res) => { res.writeHead(200, { "content-type": "application/json" }); res.end(""); },
  "kesik-govde": (res) => {
    res.writeHead(200, { "content-type": "application/json", "content-length": "20" });
    res.write('{"borc":'); res.socket.destroy();
  },
  "yanlis-tip": (res) => gonder(res, { borc: String(GERCEK.toFixed(2)) }),
  "sessiz-yanlis": (res) => gonder(res, { borc: 0 }),
  "sinir-alti-yavaslik": (res) => setTimeout(() => gonder(res, { borc: GERCEK }), 90),
};

export function kesici() {
  let ard = 0;
  return { acik: () => ard >= ESIK, basari() { ard = 0; }, hata() { ard += 1; } };
}

// Zaman asimi + yeniden deneme + devre kesici tek sarmalayicida. Hangisinin tetiklendigi iz'e yazilir.
export async function korumali(port, k, iz) {
  if (k.acik()) { iz.add("kesici"); throw new Error("devre acik"); }
  let son;
  for (let d = 1; d <= DENEME; d += 1) {
    try {
      const y = await fetch(`http://127.0.0.1:${port}/`, { signal: AbortSignal.timeout(ZAMAN_ASIMI) });
      if (y.ok === false) { const e = new Error(`HTTP ${y.status}`); e.name = `HTTP ${y.status}`; throw e; }
      const g = await y.json();
      k.basari();
      return g;
    } catch (e) {
      son = e;
      iz.add(e.name === "TimeoutError" ? "asim" : "hata");
      if (d < DENEME) iz.add("deneme");
    }
  }
  k.hata();
  throw son;
}

export const belirti = (e) => (e.cause?.code ? `${e.name}/${e.cause.code}` : e.name);

// Cagri yeri: bozulma yedegi burada. Yedek deger, sessiz-yanlis kipinin dondurdugu degerle ayni.
export async function cagriYeri(port, k, iz) {
  try { return { g: await korumali(port, k, iz), yedek: 0, b: "HTTP 200" }; }
  catch (e) { iz.add("yedek"); return { g: { borc: 0 }, yedek: 1, b: belirti(e) }; }
}
```

#### Kalıp Yığını ve Ölçüm

Ölçüm her kipi sırayla üretir ve beş çağrı yapar. Kaydedilen dört şey var: çağrı yerinde görünen
hata kimliği, tetiklenen kalıplar, yedeğe düşmeden geçen çağrı sayısı ve çağrı yerine ulaşan `borc`
değeriyle onun üzerinden hesaplanan gecikme ücreti.

```js
// kip/olcum.mjs — her kipi tek tek uretir, cagri yerinde ne gorundugunu ve hangi kalibin tetiklendigini yazar.
import http from "node:http";
import { KIP, GERCEK, ZAMAN_ASIMI, DENEME, ESIK, CAGRI, kesici, cagriYeri } from "./kip.mjs";

let simdi = "sessiz-yanlis";
const s = http.createServer((req, res) => KIP[simdi](res));
await new Promise((r) => s.listen(0, "127.0.0.1", r));
const PORT = s.address().port;
const olu = http.createServer(() => {});            // dinleyicisi kapatilmis port
await new Promise((r) => olu.listen(0, "127.0.0.1", r));
const OLU_PORT = olu.address().port;
await new Promise((r) => olu.close(r));

console.log(`kosumdan bagimsiz: zaman asimi ${ZAMAN_ASIMI} ms, deneme ${DENEME}, kesici esigi ${ESIK}, kip basina ${CAGRI} cagri`);
console.log(`gercek borc ${GERCEK}; gecikme ucreti hesabi borc + 5 = ${GERCEK + 5} olmali\n`);
console.log(`${"kip".padEnd(20)}${"cagri yerinde gorunen".padEnd(25)}${"tetiklenen kalip".padEnd(25)}${"gecen".padStart(6)}${"borc".padStart(8)}${"borc+5".padStart(9)}`);

const satir = [];
for (const kip of Object.keys(KIP)) {
  simdi = kip;
  const k = kesici(), iz = new Set();
  let gecen = 0, ilk = null;
  for (let c = 0; c < CAGRI; c += 1) {
    const y = await cagriYeri(kip === "baglanti-reddi" ? OLU_PORT : PORT, k, iz);
    if (y.yedek === 0) gecen += 1;
    ilk ??= y;                                     // belirti ilk cagridan okunur
  }
  const gorulen = iz.size > 0, dogru = ilk.g.borc === GERCEK;
  satir.push({ kip, gorulen, dogru, iz });
  console.log(`${kip.padEnd(20)}${ilk.b.padEnd(25)}${([...iz].join("+") || "hicbiri").padEnd(25)}` +
    `${`${gecen}/${CAGRI}`.padStart(6)}${String(ilk.g.borc).padStart(8)}${String(ilk.g.borc + 5).padStart(9)}`);
}
s.close();

const gormeyen = satir.filter((x) => x.gorulen === false);
console.log(`\n${satir.length} kipten ${satir.length - gormeyen.length}'i en az bir kalibi tetikledi, ` +
  `${gormeyen.length}'i hicbir kalibi tetiklemedi: ${gormeyen.map((x) => x.kip).join(", ")}`);
const yanlis = satir.filter((x) => x.dogru === false);
console.log(`gercek deger ${yanlis.length}/${satir.length} kipte cagri yerine ulasmadi: ${yanlis.filter((x) => x.gorulen).length} kipte bir kalip bunu bildirdi, ` +
  `${yanlis.filter((x) => !x.gorulen).length} kipte hicbir bildirim yok`);
console.log("\nkalip basina kapsam: " + ["asim", "hata", "deneme", "kesici", "yedek"]
  .map((kl) => `${kl} ${satir.filter((x) => x.iz.has(kl)).length}/${satir.length}`).join(", "));
console.log(`kosumdan bagimsiz ust sinir: basarisiz bir cagri en cok ${DENEME * ZAMAN_ASIMI} ms bekler, devre acildiktan sonra 0 ms`);
```

```
kosumdan bagimsiz: zaman asimi 120 ms, deneme 2, kesici esigi 3, kip basina 5 cagri
gercek borc 12.5; gecikme ucreti hesabi borc + 5 = 17.5 olmali

kip                 cagri yerinde gorunen    tetiklenen kalip          gecen    borc   borc+5
baglanti-reddi      TypeError/ECONNREFUSED   hata+deneme+yedek+kesici    0/5       0        5
zaman-asimi         TimeoutError             asim+deneme+yedek+kesici    0/5       0        5
sunucu-hatasi       HTTP 500                 hata+deneme+yedek+kesici    0/5       0        5
bos-govde           SyntaxError              hata+deneme+yedek+kesici    0/5       0        5
kesik-govde         TypeError/UND_ERR_SOCKET hata+deneme+yedek+kesici    0/5       0        5
yanlis-tip          HTTP 200                 hicbiri                     5/5   12.50   12.505
sessiz-yanlis       HTTP 200                 hicbiri                     5/5       0        5
sinir-alti-yavaslik HTTP 200                 hicbiri                     5/5    12.5     17.5

8 kipten 5'i en az bir kalibi tetikledi, 3'i hicbir kalibi tetiklemedi: yanlis-tip, sessiz-yanlis, sinir-alti-yavaslik
gercek deger 7/8 kipte cagri yerine ulasmadi: 5 kipte bir kalip bunu bildirdi, 2 kipte hicbir bildirim yok

kalip basina kapsam: asim 1/8, hata 4/8, deneme 5/8, kesici 5/8, yedek 5/8
kosumdan bagimsiz ust sinir: basarisiz bir cagri en cok 240 ms bekler, devre acildiktan sonra 0 ms
```

#### Çağrı Yerinde Beş Kimlik, İki Yol

İkinci sütun sekiz kipin uygulama tarafındaki kimliklerini veriyor ve bunlar birbirine benzemiyor.
Bağlantı reddi bir `TypeError` üretiyor ve gerçek neden ancak `cause.code` alanında görünüyor. Zaman
aşımı ayrı bir hata sınıfı, `TimeoutError`. Sunucu hatası hiç hata fırlatmıyor — çözülen bir yanıt
nesnesi geliyor ve durum kodunun denetlenmesi çağıran tarafa kalıyor; denetim yazılmazsa 500 yanıtı
başarılı sayılır. Boş gövde bir `SyntaxError`, yani bir **ayrıştırma** hatası; ağ katmanında hiçbir
sorun yok, gövde geçersiz. Kesilen gövde yine `TypeError`, ama başka bir `cause.code` ile.

Beş ayrı kimlik, beş ayrı yerden okunuyor: hata sınıfı, hata nedeni, durum kodu, ayrıştırma sonucu.
Bir arıza kipini kodda **tanımak**, bu dört kaynağın hepsine aynı anda bakmayı gerektiriyor.

Buna karşılık son sütunlardaki davranış tek tip. Beş kipin beşinde de aynı dört kalıp tetikleniyor
ve beş çağrının beşi de yedeğe düşüyor. Kalıp yığını kipleri **ayırt etmiyor**; hepsini tek bir
kategoriye indiriyor: "çağrı başarısız oldu." Bu, kalıpların bir kusuru değil, kapsamlarının
sınırıdır — ama sonucu şudur ki bağlantı reddiyle boş gövde arasındaki fark, ki biri servisin
düştüğünü biri servisin bozuk yanıt ürettiğini söyler, kalıp yığınından geçtikten sonra kaybolur.
Bu farkı taşıyacak tek şey, o hata kimliğini kaydeden bir günlük kaydıdır.

Kalıp başına kapsam satırı bunu sayıya döküyor. Zaman aşımı sekiz kipin yalnız **birinde**
tetikleniyor: geciken yanıtta. Kalan dört başarısız kip zaten kendiliğinden hata fırlatıyor ve zaman
aşımına hiç ihtiyaç duymuyor. Yeniden deneme, devre kesici ve bozulma yedeği beşer kipte
tetikleniyor. Yığındaki en pahalı kalıp — devre kesici — en yaygın tetiklenen kalıp aynı zamanda; en
dar kapsamlı olan zaman aşımı ise onsuz kalındığında çağrının süresiz asılı kalmasına yol açan tek
kalıptır. Kapsam genişliği ile önem aynı şey değildir.

#### Hiçbir Kalıbın Karşılamadığı Kipler

Alt üç satır bu dersin asıl sonucudur. Yanlış tipte alan, sessiz yanlış değer ve sınır altı yavaşlık
kiplerinde `tetiklenen kalip` sütunu `hicbiri` yazıyor ve beş çağrının beşi de geçiyor. Kalıp yığını
bu kipleri **görmüyor**, çünkü yığındaki her kalıp taşıma katmanındaki bir belirtiyle tetikleniyor:
bağlantı kurulamadı, süre doldu, durum kodu hatalı, gövde ayrıştırılamadı. Bu üç kipte taşıma katmanı
kusursuz çalışıyor. Bağlantı kuruldu, yanıt zamanında geldi, durum kodu 200, gövde geçerli JSON.

Sonuçları ise kusursuz değil. Yanlış tipte alan geldiğinde gecikme ücreti hesabı `12.505` üretiyor:
sayı beklenen yere metin geldiği için toplama işlemi birleştirmeye dönüşüyor. Ortaya çıkan değer bir
hata değil, yanlış bir ücrettir ve üyenin hesabına yazılır. Sessiz yanlış değerde ücret `5` çıkıyor —
gerçek olması gereken 17,5 yerine. Sınır altı yavaşlıkta değer doğru, tek bedel harcanan süredir;
kalıp yığını için bu kip sağlam bir çağrıdan ayırt edilemez.

Kalıpların bu kipleri karşılamamasının nedeni tasarım hatası değil, **girdi eksikliğidir**. Bir
kalıbın tetiklenmesi için bir belirti gerekir; bu üç kipte belirti taşıma katmanında değil, verinin
kendisindedir. Onu görecek şey bir dayanıklılık kalıbı değil, yanıtın şemasını ve değerini denetleyen
bir doğrulamadır — ve o doğrulama bu konuda hiç yazılmadı.

#### Sıfırın Üç Anlamı

Son bir ölçü, çağrı yerine ulaşan değer sütununda duruyor. Sekiz kipin **altısında** `borc` değeri
sıfır. Bunlardan beşinde sıfır bozulma yedeğidir: bir kalıp tetiklendi, arıza görüldü ve yerine
konan değer sıfır oldu. Altıncısında ise sıfır, servisin gerçekten döndürdüğü değerdir ve arkasında
hiçbir kalıp, hiçbir kayıt, hiçbir belirti yok.

Bir önceki derste yedek değerin gerçek değerden ayırt edilemediği ölçülmüştü; buradaki tablo aynı
sorunun bir basamak daha derinini gösteriyor. Aynı sayı üç ayrı şey anlatıyor: gerçek veri, kalıbın
koyduğu yedek ve bağımlılığın sessizce ürettiği yanlış. Yığının çıktısına bakan kod bu üçünü ayırt
edemez. Ayırt etmenin tek yolu değerin yanına **nereden geldiğinin** yazılmasıdır: yedek işareti bunu
beş kipte sağlıyor, altıncısında sağlayacak bir şey yok.

Konu buraya kadar kalıpların koda nereye yerleştiğini saydı: sarmalanan ve atlanan çağrı yerleri,
sınırın dışında kalan yollar, yanlış anahtarın ürettiği davranış. Bu son ölçüm kapsamı bir bütün
olarak veriyor. Sekiz arıza kipinden beşi görülüyor, üçü görülmüyor ve görülmeyen üçünün ikisi çağrı
yerine yanlış bir değer taşıyor. **Kalıplar arızayı görünür kılmaz; yalnızca kendilerini tetikleyen
belirtiyi görünür kılar.** Belirtisi olmayan arıza, kalıplı bir sistemde de kalıpsız bir sistemde de
aynı biçimde görünmezdir.

#### Özet

- Arıza kipi sözlüğü Dayanıklılık ve Güvenilirlik kursunda kuruldu; buradaki ölçü her kipin uygulama
  kodunda hangi kimlikle göründüğüdür.
- Sekiz kip çağrı yerinde beş ayrı kimlik üretiyor ve bu kimlikler dört ayrı yerden okunuyor: hata
  sınıfı (`TimeoutError`), hata nedeni (`ECONNREFUSED`, `UND_ERR_SOCKET`), durum kodu (HTTP 500) ve
  ayrıştırma sonucu (`SyntaxError`).
- Kalıp yığını bu beş kimliği ayırt etmiyor; beşinde de aynı dört kalıp tetikleniyor ve beş çağrının
  beşi yedeğe düşüyor. Kipler arasındaki fark yığından geçtikten sonra kaybolur.
- Kalıp başına kapsam eşit değil: zaman aşımı 8 kipten 1'inde, yeniden deneme, devre kesici ve
  bozulma yedeği 5'inde tetikleniyor. Kapsam genişliği önemle aynı şey değildir.
- 3 kip hiçbir kalıbı tetiklemiyor — yanlış tip, sessiz yanlış değer ve sınır altı yavaşlık — çünkü
  üçünde de taşıma katmanı kusursuz çalışıyor. İkisi çağrı yerine yanlış değer taşıyor: gecikme
  ücreti `17.5` yerine `12.505` ve `5` çıkıyor.
- 8 kipin 6'sında çağrı yerine sıfır ulaşıyor; 5'inde bu bir yedek değerdir ve işareti vardır,
  1'inde bağımlılığın sessiz yanlışıdır ve hiçbir işareti yoktur.

#### Sonraki Adım

Bu konu kalıpları kurdu, kodda nereye yerleştiklerini saydı ve kapsamlarının dışında kalanı ölçtü.
Ölçülen her sayı bir sistemi tarif ediyor: on dört çağrı yerinin ikisi sarmalanmamış, sekiz
yol-erişim çiftinin ikisi hiçbir sınırın dışında, sekiz arıza kipinin üçü hiçbir kalıp tarafından
görülmüyor. Bu boşlukların tamamı yazıldıkları anda değil, bir arıza onlara dokunduğunda öğrenildi.

Öğrenmenin kendisi ise henüz bir düzenek değil. Bir kesinti yaşandığında zaman çizelgesinin nasıl
kurulacağı, ilk üç konuda üretilen sinyallerden hangilerinin o çizelgeyi doldurduğu, çıkan sonucun
kime ne söylediği ve çıkarılan dersin koda nasıl döneceği — hiçbiri tasarlanmadı. Bir sonraki ders
bu düzeneği ele alır ve şunu sorar: bir olayın ardından yazılan metin, bu konudaki sayılardan
hangilerini değiştirebilir.

### Olay Sonrası Çözümleme

Source: https://academia.sh/tr/kurslar/gozlemlenebilirlik/olay-sonrasi-cozumleme

Bir kesintinin bu kursta üretilen sinyallerden yeniden kurulması: altı sorunun hangi sinyalle ve kaç adımda yanıtlandığı, hiçbir sinyalin yanıtlayamadığı soruların sayılması, eylemlerin koda dönmesi ve kişi arayan çözümlemenin ürettiği eylemlerin kaçının bir sonraki olayda bir soruyu yanıtlanır kıldığı.

Bir önceki ders şunu bıraktı: ölçülen boşlukların tamamı yazıldıkları anda değil, bir arıza onlara
dokunduğunda öğrenildi. Öğrenmenin kendisi ise henüz bir düzenek değil.

Kök neden çözümlemesi ve neden zinciri modeli **M21/K05 Otomasyon ve Altyapı** kursunda kuruldu ve
burada tekrarlanmaz. Buranın konusu daha dardır: **olayın telemetriden yeniden kurulması.** Bu ders
altı soruyu gerçek bir olaya sorar, her birinin hangi sinyalle ve kaç adımda yanıtlandığını sayar,
sonra çıkan eylemleri koda uygulayıp aynı soruları yeniden sorar. Adım birimi bu kursta tanımlandı:
bir sinyal deposu üzerinde bir geçiş ya da iki depoyu bir anahtar üzerinden eşleştiren birleştirme.

#### Olay

Bildirim servisi 80. istekten 140. isteğe kadar 5 ms yerine 200 ms sürüyor. **DY17 — bildirim çağrı
yerinin ne yazdığı üç bayrakla değişir**; her bayrak bu olaydan çıkacak bir eylemin karşılığıdır.
Bayraklar kapalıyken sistem `telemetri` konusunda ölçülen durumdadır: kimliği taşımayan çağrı yeri
bildirim servisini haritanın dışında bırakır.

```js
// servisler.mjs — 8501 odunc, 8502 bildirim, 8503 uyelik. Uc sinyal uygulama kodunda uretilir.
// DY17: bildirim cagri yerinin ne yazdigi uc bayrakla degisir; her bayrak bir olay sonrasi eylemdir.
import { createServer, request } from "node:http";
const S = { gunluk: [], olcut: [], iz: [] }, A = { aralik: 0, kimlik: 0, teslim: 0 };
const OLAY = [80, 140], TIK = 20, YAVAS = 200, NORMAL = 5;  // olay 80. istekte baslar, 140'ta biter
const olcut = (tik, servis, sure) => {               // sayac + sure toplami, kimlik tasimaz
  const o = S.olcut.find((x) => x.tik === tik && x.servis === servis)
    ?? S.olcut[S.olcut.push({ tik, servis, istek: 0, toplam: 0 }) - 1];
  o.istek += 1; o.toplam += sure;
};
const cagir = (port, yol, kimlik) => new Promise((coz) => {
  const t0 = Date.now();
  const r = request({ port, path: yol, agent: false, headers: kimlik ? { "x-kimlik": kimlik } : {} },
    (y) => { y.resume(); y.on("end", () => coz(Date.now() - t0)); });
  r.on("error", () => coz(Date.now() - t0)); r.end();
});
createServer((i, y) => { setTimeout(() => y.end("{}"), NORMAL); }).listen(8503);   // uyelik: hep hizli
createServer((i, y) => {                             // 8502 bildirim: olay penceresinde yavaslar
  const n = Number(new URL(i.url, "http://x").searchParams.get("n"));
  const gec = n >= OLAY[0] && n < OLAY[1], sure = gec ? YAVAS : NORMAL;
  setTimeout(() => {
    const kimlik = i.headers["x-kimlik"] ?? null;    // eylem 2: cagri yeri kimligi tasiyor mu
    const k = { t: n, servis: "bildirim", kimlik, sure };
    if (A.teslim) k.teslim = !gec;                   // eylem 3: teslim sonucu sinyale yaziliyor mu
    S.gunluk.push(k);
    if (A.aralik) S.iz.push({ kimlik, servis: "bildirim", sure });   // eylem 1: aralik yaziliyor mu
    olcut(Math.floor(n / TIK), "bildirim", sure);
    y.end("{}");
  }, sure);
}).listen(8502);
createServer(async (i, y) => {                       // 8501 odunc
  const q = new URL(i.url, "http://x").searchParams;
  if (i.url === "/sinyal") return y.end(JSON.stringify(S));
  if (i.url.startsWith("/kur")) {
    for (const k of Object.keys(A)) A[k] = Number(q.get(k));
    for (const d of Object.keys(S)) S[d] = [];
    return y.end("{}");
  }
  const n = Number(q.get("n")), kimlik = `odunc-${n}`, t0 = Date.now();
  S.iz.push({ kimlik, servis: "uyelik", sure: await cagir(8503, "/uye", kimlik) });
  if (n % 3 === 0) await cagir(8502, `/gonder?n=${n}`, A.kimlik ? kimlik : null);
  const sure = Date.now() - t0;
  S.gunluk.push({ t: n, servis: "odunc", kimlik, uye: `u${n % 25}`, sure });
  S.iz.push({ kimlik, servis: "odunc", sure });
  olcut(Math.floor(n / TIK), "odunc", sure);
  y.end("{}");
}).listen(8501);
```

#### Çözümleme

Altı soru altı işlevdir. Her biri gerçek sinyal depoları üzerinde çalışır ve her geçişte adım
sayacını artırır; sayı iddia edilmez, koşumdan çıkar. Gereken alan ya da birleştirecek anahtar yoksa
soru **yanıtsız** kalır.

```js
// cozumle.mjs — olayin telemetriden yeniden kurulmasi: her soru hangi sinyalle ve kac adimda yanitlaniyor.
import { request } from "node:http";
const N = 200, ESIK = 40, AGIR = 100;                // DY18: tik ortalamasi ESIK'i, tek cagri AGIR'i asarsa olay
const cagir = (yol) => new Promise((c) => request({ port: 8501, path: yol, agent: false }, (r) => {
  let g = ""; r.on("data", (p) => (g += p)); r.on("end", () => c(g === "{}" ? null : JSON.parse(g)));
}).end());
let adim = 0;
const tara = (k, f) => { adim += 1; return k.filter(f); };   // bir depoda bir gecis
const birlestir = (a, b) => {                                // iki depoyu kimlik uzerinden eslestirme
  adim += 1; const m = new Map(b.map((x) => [x.kimlik, x])); return a.map((x) => m.get(x.kimlik));
};
const yavas = (x) => x.sure > AGIR;
const bildirim = (S) => tara(S.gunluk, (x) => x.servis === "bildirim" && yavas(x));
const olayTik = (S) => tara(S.olcut, (x) => x.servis === "odunc" && x.toplam / x.istek > ESIK).map((x) => x.tik);
const SORU = [
  ["olay hangi tikte basladi", "olcut", (S) => { const t = olayTik(S); return t.length ? `tik ${Math.min(...t)}` : null; }],
  ["olay hangi tikte bitti", "olcut", (S) => { const t = olayTik(S); return t.length ? `tik ${Math.max(...t) + 1}` : null; }],
  ["hangi bagimlilik yavasladi", "iz", (S) => {
    const a = tara(S.iz, (x) => x.servis !== "odunc" && yavas(x));
    return a.length ? [...new Set(a.map((x) => x.servis))].join("+") : null;
  }],
  ["kac istek etkilendi", "gunluk", (S) => {
    const b = bildirim(S);
    if (b.length && b[0].kimlik !== null) return `${new Set(b.map((x) => x.kimlik)).size} istek`;
    const o = tara(S.gunluk, (x) => x.servis === "odunc" && yavas(x));   // kimliksiz: depolar ayri ayri taranir
    return tara(S.olcut, (x) => x.servis === "bildirim").length ? `${o.length} istek` : null;
  }],
  ["hangi uyeler etkilendi", "gunluk", (S) => {
    const b = bildirim(S);
    if (!b.length || b[0].kimlik === null) return null;                 // birlestirecek anahtar yok
    const u = birlestir(b, tara(S.gunluk, (x) => x.servis === "odunc"));
    return `${new Set(u.map((x) => x.uye)).size} uye`;
  }],
  ["bildirim teslim edildi mi", "gunluk", (S) => {
    const b = tara(S.gunluk, (x) => x.servis === "bildirim" && x.teslim !== undefined);
    return b.length ? `${b.filter((x) => x.teslim === false).length} teslim yok` : null;
  }],
];
const kos = async (bayrak) => {
  await cagir(`/kur?${bayrak}`);
  for (let n = 0; n < N; n += 1) await cagir(`/istek?n=${n}`);
  const S = await cagir("/sinyal");
  return SORU.map(([ad, sinyal, f]) => { adim = 0; const y = f(S); return { ad, sinyal, y, adim: y === null ? null : adim }; });
};

const YAP = [["olay anindaki hâl", "aralik=0&kimlik=0&teslim=0"], ["+ iz araligi", "aralik=1&kimlik=0&teslim=0"],
  ["+ bagintili kimlik", "aralik=1&kimlik=1&teslim=0"], ["+ teslim sinyali", "aralik=1&kimlik=1&teslim=1"]];
const yaz = (a, ...r) => console.log(String(a).padEnd(28) + r.map((x) => String(x).padStart(16)).join(""));
const t = {};
console.log(`olay: ${N} istek, bildirim 80.-139. isteklerde 5 ms yerine 200 ms suruyor; tik = 20 istek`);
yaz("yapilandirma", "yanitlanan", "yanitsiz", "adim");
for (const [ad, b] of YAP) {
  const r = (t[ad] = await kos(b)), v = r.filter((x) => x.y !== null);
  yaz(ad, v.length, r.length - v.length, v.reduce((a, x) => a + x.adim, 0));
}
const g = (x) => (x.y === null ? "-" : `${x.y} (${x.adim} adim)`);
console.log(`\n${"soru".padEnd(28)}${"sinyal".padEnd(9)}${"olay anindaki hâl".padEnd(24)}eylemlerden sonra`);
t["olay anindaki hâl"].forEach((x, i) =>
  console.log(x.ad.padEnd(28) + x.sinyal.padEnd(9) + g(x).padEnd(24) + g(t["+ teslim sinyali"][i])));

const EYLEM = { kisi: ["gelistiriciyle konusulur", "nobetciye egitim verilir", "rapora sorumlu yazilir"],
  sistem: ["iz araligi yazilir", "bagintili kimlik eklenir", "teslim sonucu yazilir"] };
console.log(`\n${"cozumleme".padEnd(28)}${"eylem".padStart(16)}${"koda donen".padStart(16)}${"yanitlanan soru".padStart(16)}`);
for (const [tur, ad, yer] of [["kisi", "kisi arayan", "olay anindaki hâl"], ["sistem", "sistem arayan", "+ teslim sinyali"]])
  yaz(ad, EYLEM[tur].length, tur === "sistem" ? EYLEM[tur].length : 0, t[yer].filter((x) => x.y !== null).length);
process.exit(0);
```

```bash
node servisler.mjs > /dev/null 2>&1 & SP=$!
sleep 1; node cozumle.mjs; kill $SP
```

```
olay: 200 istek, bildirim 80.-139. isteklerde 5 ms yerine 200 ms suruyor; tik = 20 istek
yapilandirma                      yanitlanan        yanitsiz            adim
olay anindaki hâl                          3               3               5
+ iz araligi                               4               2               6
+ bagintili kimlik                         5               1               7
+ teslim sinyali                           6               0               8

soru                        sinyal   olay anindaki hâl       eylemlerden sonra
olay hangi tikte basladi    olcut    tik 4 (1 adim)          tik 4 (1 adim)
olay hangi tikte bitti      olcut    tik 7 (1 adim)          tik 7 (1 adim)
hangi bagimlilik yavasladi  iz       -                       bildirim (1 adim)
kac istek etkilendi         gunluk   20 istek (3 adim)       20 istek (1 adim)
hangi uyeler etkilendi      gunluk   -                       20 uye (3 adim)
bildirim teslim edildi mi   gunluk   -                       20 teslim yok (1 adim)

cozumleme                              eylem      koda donen yanitlanan soru
kisi arayan                                3               0               3
sistem arayan                              3               3               6
```

#### Çizelge Kaç Adımda Kuruluyor

Olay anındaki sinyallerle **altı sorunun üçü** yanıtlanabiliyor: başlangıç ve bitiş ölçütten birer
adımda, kaç isteğin etkilendiği günlükten üç adımda. Kalan üçü boş.

"Kaç istek etkilendi" satırı bağıntı kimliğinin ne yaptığını gösteriyor: aynı yanıt, aynı 20 sayısı,
**3 adım yerine 1 adım**. Kimlik yokken bildirim kaydı hangi isteğe ait olduğunu söyleyemiyor, bu
yüzden ödünç günlüğü ve ölçüt ayrı ayrı taranıyor ve tarama sayısı servis sayısıyla büyür. Zaman
sorularının maliyeti ise hiç değişmiyor, çünkü ölçüt birleştirmeye ihtiyaç duymuyor. **Çizelgenin
iskeleti en ucuz sinyalden kuruluyor, ayrıntısı en pahalısından.**

#### Telemetri Boşluğu

Üç soru olay anında hiçbir sinyalle yanıtlanamıyor ve nedenleri ayrıdır.

"Hangi bağımlılık yavaşladı" yanıtsız, çünkü bildirim çağrı yeri **iz aralığı yazmıyor**. İz
deposunda ödünç aralığı yavaş görünüyor ama o yalnızca beklediğini söylüyor; bir servisin kendi
yavaşlığı ile bağımlılığı beklemesi çocuk aralık olmadan ayrılamaz.

"Hangi üyeler etkilendi" yanıtsız, çünkü üye alanı yalnız ödünç kaydında, yavaşlık yalnız bildirim
kaydında. İki depo da var, iki alan da var; **eksik olan yalnızca ortak anahtardır** — veri
toplanmıştır, birleştirilemez.

"Bildirim teslim edildi mi" yanıtsız, çünkü teslim sonucu hiçbir yere yazılmıyor. Üç boşluk da birer
**yazma kararının** sonucudur ve okuma zamanında değil, yazma zamanında kapatılabilir.

#### Suçsuzluk Bir Ölçüye Bağlanır

Suçsuzluk bir görgü kuralı değil, eylemin türüyle ilgili bir ölçüttür. Son tablo aynı olayın iki
çözümlemesini karşılaştırıyor. Kişi arayan çözümleme üç eylem üretti; **üçünün de koda dönüşü yok**
ve bir sonraki benzer olayda aynı üç soru yine yanıtsız kalır. Sistem arayanın üç eylemi ise birer
yazma kararıdır, üçü de koda döndü ve altı sorunun altısı yanıtlanır oldu.

Ölçü şudur: **bir eylem, bir sonraki benzer olayda bir sorunun yanıtını değiştiriyorsa koda
dönmüştür.** Bedeli ilk tablodadır: adım 5'ten 8'e çıktı, yani kapatılan soru başına bir adım.

#### Özet

- Olayın çizelgesi bu kursta üretilen üç sinyalden kuruldu; kök neden çözümlemesi ve neden zinciri
  modeli M21/K05'te kuruldu ve burada tekrarlanmadı.
- Olay anındaki sinyallerle altı sorunun üçü yanıtlandı (5 adım), üçü yanıtsız kaldı; eylemlerden
  sonra altısı da yanıtlandı (8 adım). Zaman soruları her yapılandırmada 1 adım, bağıntı kimliği ise
  yanıtı değil maliyeti değiştirdi: aynı 20 sayısı 3 adım yerine 1 adımda çıktı.
- Üç boşluğun üçü de yazma kararıdır: yazılmayan iz aralığı bağımlılığı gizledi, taşınmayan kimlik
  iki deponun birleşmesini engelledi, kaydedilmeyen teslim sonucu soruyu kapattı.
- Kişi arayan çözümlemenin 3 eyleminin 0'ı koda döndü ve yanıtlanan soru 3'te kaldı; sistem arayanın
  3 eyleminin 3'ü döndü ve yanıt 6'ya çıktı. Bedeli üç adımdır.

#### Kurs Kapanışı

| Ders | Kodda nereye yerleşti | Sarmalanan / atlanan çağrı yeri | Yanlış ayarın bedeli |
|---|---|---|---|
| telemetri/01 Üç Sinyal | üçü aynı istekten aynı süreçte | ölçüt 6, günlük 11–12 adım; 9 denemenin 5'i yanıtsız | ölçüt kimlik sorusunu sonsuza kapatır |
| telemetri/02 Yapılandırılmış Günlükleme | alan tabanlı kayıt; kimlik istemci yardımcısında | 11 → 7 adım; kimlikle önceki yanıtsız soru 8 adımda | kimliği taşıyan çağrı yeri 4'te 3; bir servis harita dışı |
| telemetri/03 Ölçüt Türleri | sayaç, ölçer ve histogram çağrı yerinde | üç tip üç ayrı soru; hiçbiri ötekini yanıtlamaz | ölçer 2000 gözlemin 20'sini gördü, tepe 293 yerine 199 |
| telemetri/04 Dağıtık İzleme | iz bağlamı tek başlıkta, süreç sınırından geçer | 100 istek → 100 tam iz, 400 bağlı aralık | bağlamsız tek çağrı yeri 100 kopuk iz; en düşük oranda 40 sorunlu isteğin 0'ı |
| telemetri/05 Enstrümantasyon Standartları | toplayıcı arayüzün arkasında | toplayıcı adı 5 uygulama dosyasında → 0'ında | bağlı yerleşimde geçiş 5 dosyaya, arayüzlüde 1 dosyaya dokunur |
| telemetri/06 Bağıntılandırma | üç sinyal aynı kimlikle tek olayda | 20 adım / 3 sinyal → 2 adım / 1 sinyal | oran 0,50'de 100 olayın 51'i izsiz kalır |
| servis-duzeyi/01 Gösterge, Hedef ve Anlaşma | hariç tutma listesi süzgeç dizisi olarak | 1.188.000 kayıttan üç tanım ve dört süzgeçle 12 sayı | süzgeç dizisi anlaşma metninden ayrışırsa ölçülen ile taahhüt ayrışır |
| servis-duzeyi/02 Altın Sinyaller | doygunluk kuyruk derinliği olarak istek kaydında | 6 olay sınıfı; tek sinyal en çok 3'ünü yakalar | trafik çöküşünde hata oranı 0; 162 boş tetiğin 136'sı düşük trafikte |
| servis-duzeyi/03 Hata Bütçesi | yayın kapısı tek satırlık koşul | 30 günde 3.060 kötü isteklik bütçe; %20,6'sını taban yer | 10 yayın bütçenin %180,5'ini yedi; kapı aşımı 2.463 → 471 |
| servis-duzeyi/04 Uyarı Tasarımı | kural nedenden mi belirtiden mi okuyor | 12 kural 81 uyarı, 4 kural 9 uyarı; ikisi de 4/6 olay | neden tabanlı kümede 67 uyarı boşa, olay başına 3,5 uyarı |
| servis-duzeyi/05 Sağlık Uçları | canlılık ile hazır olma tek koşul teriminde ayrılır | 25 tiklik kesinti: 0'a karşı 4 yeniden başlatma | yanlış sınıflandırma 48 ilgisiz isteği düşürdü, toparlanma 0 → 4 tik |
| dayaniklilik/01 Zaman Aşımları | çağrı yerinde 53 satır, sarmalayıcıda 28 satır | 7 / 0 ve 6 / 1 | iç sınır dıştan büyükse 10 isteğin 10'unda öksüz tamamlanma |
| dayaniklilik/02 Yeniden Deneme ve Geri Çekilme | sarmalayıcı 6, ara katman 2, ağ geçidi 6 satır | gövdedeki 2 çağrı yerinin 1'i yeniden denenir | üç katman birden açıkken 1 istek 27 çağrı ve 27 teslim |
| dayaniklilik/03 Devre Kesici | durum anahtarı: bağımlılık başına 2, çağrı yeri başına 4 | 3 çağrı yeri; eşik 10'da 1'inin kesicisi hiç açılmaz | aynı eşikte bağımlılığa çağrı 5 → 13; seyrek yol korumasız |
| dayaniklilik/04 Bölme Kalıbı | havuz tanımı 5 satır, tek yerde | 2 çağrı yeri değişmedi; 40 çağrının 8'i havuzu atlar | havuz 4 derken bağımlılık 12 görür; 40 yuva sınırı hiç bağlamaz |
| dayaniklilik/05 Zarif Bozulma | şema nesnesinde; çağrı yerinde 7, sarmalayıcıda 12 satır | 14 çağrı yerinin 6'sı sarılı, 2'si atlanmış | işaretsiz yedek, borcu gerçekten sıfır olan üyeden ayırt edilemez |
| dayaniklilik/06 Hız Sınırlama ve Kısıtlama | ağ geçidinde ve servis içi ara katmanda | 8 yol-erişim çiftinin 2'si / 6'sı; 2'si hiçbirinde | sabit anahtarda tavan 20, üye anahtarında 100.000 |
| dayaniklilik/07 Arıza Kipleri | hata kimliği dört ayrı yerden okunur | 8 kipin 5'i kalıp yığınında, 3'ü hiç görülmez | 8 kipin 6'sında çağrı yerine sıfır ulaşır, 1'i işaretsiz |
| dayaniklilik/08 Olay Sonrası Çözümleme | eylem yeni sinyal, kimlik ve aralık olarak koda döner | 6 sorunun 3'ü yanıtsız → 0'ı | kişi arayan çözümlemenin 3 eyleminin 0'ı koda döner |

Tablo tek bir kuralın on dokuz kez uygulanmasıdır. **Bir kalıbın etkisi Dayanıklılık ve Güvenilirlik
kursunda ölçüldü; burada ölçülen, o kalıbın kodda nereye yerleştiği ve neyi atladığıdır.** Aynı kural
sinyaller için de geçerliydi: izleme kategorileri başka yerde kuruldu, burada sinyalin kodda nasıl
üretildiği ve hangi soruyu kaç adımda yanıtladığı sayıldı. Üçüncü sütun neredeyse her satırda aynı
şeyi söylüyor — kapsanmayan yer yapılandırmada değil, yalnız kaynakta görünür.

Sistem artık görünür durumda ve arızaya dayanıyor: bir isteğin nerede yavaşladığı izlenebiliyor,
göstergeler karara dönüşüyor, kalıplar arızayı çevreliyor ve kesintiden sonra çizelge kurulabiliyor.
Yine de hiçbir ölçüm tek bir soruyu sormadı: **isteğin kendisi kötü niyetliyse.** Bütün sayılar
çağıranın sistemi yormaya çalışmadığını varsaydı; yayının kendisi de hiç ele alınmadı. **M16/K08
Sunucu Güvenliği ve Üretime Alma** bu iki soruyla başlar.

