---
title: 'Kaos Deneyleri'
source: 'https://academia.sh/tr/kurslar/islevsel-olmayan-test/kaos-deneyleri'
course: 'İşlevsel Olmayan Test'
language: tr
updated: '2026-08-17T18:10:52+00:00'
license: 'CC BY-SA 4.0'
---

# Kaos Deneyleri

Üretim benzeri ortamda kontrollü bozmanın tasarımı: kararlı durum önermesinin kontrol koşumundan ölçülmesi, deney etiketiyle sınırlanan bulaşma kapsamının yığınlama yüzünden dört katına çıkması, pay marjının taranarak yanlış kalma ile geç algılamanın sayılması ve durdurma ölçütünün ardışık pencere sayısına göre biriken hasarın ölçülmesi.

Önceki ders enjeksiyonu bir test düzeneği olarak kullandı: kapsam tek bir sınırdı, ortam yalnız
o ders için ayağa kalktı ve bozulan şeyin kimseye zararı yoktu. Aynı enjeksiyon gerçek trafiğin
aktığı bir ortamda yapıldığında üç karar öne geçer: sistemin **normal davranışı** nedir, deney
kaç isteği etkileyecektir ve deney hangi sayıya bakılarak durdurulacaktır.

**Kaos deneyi**, üretim benzeri bir ortamda kontrollü bir bozma yapıp sistemin normal
davranışından sapıp sapmadığını ölçmektir. Bu ders deneyin **tasarımını** ele alır; enjekte
edilen arızanın karşısındaki kalıplar Dayanıklılık ve Güvenilirlik kursunda kuruldu ve burada
yeniden anlatılmaz. Üç tasarım kararının her biri bir sayıyla verilir ve her birinin
yanlış verilmesinin bir bedeli ölçülür.

## Kararlı Durum Önermesi

**Kararlı durum önermesi**, sistemin arızasız davranışı hakkında ölçülebilir bir iddiadır ve
deneyden önce yazılır. "Sistem sağlıklı" bir önerme değildir; "her 40 isteklik pencerede tam
yanıt payı şu eşiğin üstündedir" bir önermedir. Eşiğin kaynağı burada bir gereksinim değil, bir
**ölçümdür**: deney açılmadan aynı yük bir kez koşturulur ve pencere ortalaması taban kabul
edilir.

Ölçüm düzeneği bir **modeldir** ve kendi yazılmıştır. Katalog servisi yerel bir `node:http`
sürecidir; enjeksiyon yalnız **deney etiketi** taşıyan isteklere uygulanır. Ödünç servisi künye
aramalarını dörtlü yığınlar hâlinde gönderir, çünkü bir ödünç listesi tek ekranda birden çok
künye ister.

**IO17 — yığın boyu 4, enjekte edilen gecikme 240 ms, ödünç servisinin zaman aşımı 100 ms.**
**IO18 — etiketsiz isteklerin 0,03'ü 150 ms duraklar.** İkisi de varsayımdır; gecikmenin zaman
aşımından büyük seçilmesi enjeksiyonun etkisini deterministik kılar.

```js
// kaos.mjs — deney kapsamina alinmis katalog servisi ve yigin gonderen odunc istemcisi.
// MODELDIR: gercek kaos araci ya da kume kurulmaz; enjeksiyon yalniz deney etiketi tasiyan
// yigina uygulanir ve hangi kullanicinin etiketlendigi tohumlu karmadan gelir.
import { createServer } from 'node:http';

export const YIGIN = 4, GECIKME = 240, ZAMAN_ASIMI = 100;    // IO17
export const GURULTU = { oran: 0.03, sure: 150 };            // IO18

export function karma(tohum, i) {
  let x = (tohum ^ Math.imul(i + 1, 0x9e3779b1)) >>> 0;
  x = Math.imul(x ^ (x >>> 16), 0x85ebca6b) >>> 0;
  x = Math.imul(x ^ (x >>> 13), 0xc2b2ae35) >>> 0;
  return ((x ^ (x >>> 16)) >>> 0) / 4294967296;
}

export function katalogKur() {
  const olcu = { yigin: 0, etiketli: 0, duraklama: 0 };
  const sunucu = createServer((istek, yanit) => {
    const a = new URL(istek.url, 'http://yerel');
    const etiket = a.searchParams.get('deney') === '1';
    const no = Number(a.searchParams.get('yigin'));
    olcu.yigin += 1;
    if (etiket) olcu.etiketli += 1;
    const duraklama = etiket === false && karma(31337, no) < GURULTU.oran;
    if (duraklama) olcu.duraklama += 1;
    const bekle = etiket ? GECIKME : (duraklama ? GURULTU.sure : 0);
    const gonder = () => {
      yanit.writeHead(200, { 'content-type': 'application/json' });
      yanit.end('{"kunye":"tam"}');
    };
    if (bekle === 0) gonder(); else setTimeout(gonder, bekle);
  });
  sunucu.listen(0, '127.0.0.1');
  return { sunucu, olcu };
}

export function yiginGondericisi(taban) {
  return async (no, etiketli) => {
    try {
      await fetch(`${taban}/kunye?yigin=${no}&deney=${etiketli ? 1 : 0}`,
        { signal: AbortSignal.timeout(ZAMAN_ASIMI) });
      return 'tam';
    } catch { return 'bozulmus'; }
  };
}
```

## Bulaşma Kapsamının Sınırlanması

**Bulaşma kapsamı**, deneyin etkileyebileceği en geniş alandır ve deney başlamadan **sayıyla**
yazılır. Buradaki plan şudur: kullanıcıların 0,05'i etiketlenir ve enjeksiyon yalnız onlara
uygulanır. Plan bir niyettir; ölçülmesi gereken şey etiketin gerçekten o kadar isteği
kapsayıp kapsamadığıdır.

**IO19 — koşum 400 istek, pencere 40 istek, deney 200. istekten sonra açık, etiketli kullanıcı
payı 0,05.** Varsayımdır; deneyin koşumun ortasında açılması, önceki pencerelerin kontrol
görevi görmesini sağlar.

```js
// deney.mjs — kontrol kosumu ve deney kosumu gercek istekle yapilir; her istegin kaydi
// gunluk.json'a yazilir. Sayimlar deterministiktir, yalniz duvar suresi kosuma baglidir.
import { writeFileSync } from 'node:fs';
import { katalogKur, yiginGondericisi, karma, YIGIN } from './kaos.mjs';

const N = 400, PENCERE = 40, DENEY_BASI = 200, PAY = 0.05, ES = 4;   // IO19
const etiketliKullanici = (i) => karma(20260731, i) < PAY;

async function kosum(deneyAcik) {
  const { sunucu, olcu } = katalogKur();
  await new Promise((c) => sunucu.once('listening', c));
  const gonder = yiginGondericisi(`http://127.0.0.1:${sunucu.address().port}`);
  const kayit = [];
  let sira = 0;
  const isci = async () => {
    while (sira < N / YIGIN) {
      const y = sira; sira += 1;
      const uyeler = Array.from({ length: YIGIN }, (_, k) => y * YIGIN + k);
      const etiketli = deneyAcik && uyeler[0] >= DENEY_BASI && uyeler.some(etiketliKullanici);
      const sonuc = await gonder(y, etiketli);
      for (const u of uyeler) kayit.push({ istek: u, etiketli, sonuc });
    }
  };
  await Promise.all(Array.from({ length: ES }, isci));
  sunucu.closeAllConnections();
  await new Promise((c) => sunucu.close(c));
  return { kayit: kayit.sort((a, b) => a.istek - b.istek), olcu };
}

const kontrol = await kosum(false);
const deney = await kosum(true);
writeFileSync('gunluk.json', `${JSON.stringify({ N, PENCERE, DENEY_BASI,
  kontrol: kontrol.kayit, deney: deney.kayit }, null, 0)}\n`);

const pencereIci = deney.kayit.filter((k) => k.istek >= DENEY_BASI);
const planlanan = pencereIci.filter((k) => etiketliKullanici(k.istek)).length;
const etkilenen = pencereIci.filter((k) => k.etiketli).length;
const bozulan = pencereIci.filter((k) => k.sonuc === 'bozulmus').length;
const y = (a, b) => `${a}/${b} (%${((100 * a) / b).toFixed(2)})`;
console.log(`${N} istek, ${YIGIN}'luk yigin, deney ${DENEY_BASI}. istekten sonra acik`);
console.log(`planlanan bulasma kapsami : ${y(planlanan, pencereIci.length)} etiketli kullanici`);
console.log(`olculen bulasma kapsami   : ${y(etkilenen, pencereIci.length)} etiketli yigin uyesi`);
console.log(`kapsam carpani            : ${(etkilenen / planlanan).toFixed(2)} kat`);
console.log(`deney penceresinde bozulan: ${y(bozulan, pencereIci.length)}`);
console.log(`kontrol kosumunda bozulan : ${y(kontrol.kayit.filter((k) => k.sonuc === 'bozulmus').length, N)}`);
```

```
400 istek, 4'luk yigin, deney 200. istekten sonra acik
planlanan bulasma kapsami : 11/200 (%5.50) etiketli kullanici
olculen bulasma kapsami   : 44/200 (%22.00) etiketli yigin uyesi
kapsam carpani            : 4.00 kat
deney penceresinde bozulan: 52/200 (%26.00)
kontrol kosumunda bozulan : 16/400 (%4.00)
```

Plan 11 kullanıcı diyordu, ölçüm 44 istek buldu: **kapsam çarpanı 4,00 kat**. Kaynağı
yığınlamadır. Etiket kullanıcıya konuyor, enjeksiyon ise yığına uygulanıyor; bir yığında tek bir
etiketli üye bulunması yığının tamamını deneyin içine alıyor. Çarpanın üst sınırı yığın boyudur
ve bu koşumda üst sınıra çarpılmıştır, çünkü on bir etiketli kullanıcının hepsi ayrı yığınlara
düşmüştür. **Bulaşma kapsamı etiketin taşındığı kadar dardır**: etiketi taşımayan her toplama,
kuyruklama ve önbellekleme adımı kapsamı sessizce genişletir.

Bu, bir deney tasarımı kusurudur ve yalnız ölçülerek görülür. Kapsamı gerçekten daraltmanın yolu
etiketi yığın oluşturma kuralına da taşımaktır; bunun bedeli, etiketi taşımak zorunda kalan çağrı
yeri sayısıdır ve o sayı deney başlamadan bilinmelidir.

## Önerme, Yanlışlanma ve Durdurma Ölçütü

Deney koşumu bittiğinde elde bir istek günlüğü vardır. Önermenin eşiği, durdurma ölçütünün
duyarlılığı ve bu ikisinin bedeli aynı günlük üzerinde taranır; deney yeniden koşturulmaz.

```js
// tarama.mjs — gunluk.json uzerinde iki tarama: kararli durum onermesinin pay marji ve
// durdurma olcutunun ardisik pencere sayisi. Deney tekrar kosturulmaz, kaydi okunur.
import { readFileSync } from 'node:fs';

const g = JSON.parse(readFileSync('gunluk.json', 'utf8'));
const pencereler = (kayit) => {
  const p = [];
  for (let b = 0; b < g.N; b += g.PENCERE) {
    const d = kayit.slice(b, b + g.PENCERE);
    p.push({ tamPay: d.filter((k) => k.sonuc === 'tam').length / d.length,
      hasar: d.filter((k) => k.etiketli && k.sonuc === 'bozulmus').length });
  }
  return p;
};
const K = pencereler(g.kontrol), D = pencereler(g.deney);
const TABAN = K.reduce((a, x) => a + x.tamPay, 0) / K.length;   // esigin kaynagi: kontrol kosumu
const yanlisla = (p, m) => p.tamPay < TABAN - m;
const p = (x, n) => String(x).padStart(n);

console.log(`kararli durum onermesi: her pencerede tam yanit payi >= taban - m; taban kontrol ` +
  `kosumunun pencere ortalamasi = %${(100 * TABAN).toFixed(2)} (${K.length} pencere, ` +
  `pencere basina ${g.PENCERE} istek)`);
console.log(`\n${'pay marji m'.padEnd(13)}${'kontrolde yanlislanan'.padStart(22)}` +
  `${'deneyde yanlislanan'.padStart(20)}${'ilk yanlislanan pencere'.padStart(24)}`);
for (const m of [0, 0.05, 0.10, 0.20]) {
  const ilk = D.findIndex((x) => yanlisla(x, m));
  console.log(`${(`%${(100 * m).toFixed(0)}`).padEnd(13)}` +
    `${p(`${K.filter((x) => yanlisla(x, m)).length}/${K.length}`, 22)}` +
    `${p(`${D.filter((x) => yanlisla(x, m)).length}/${D.length}`, 20)}` +
    `${p(ilk < 0 ? 'yok' : ilk + 1, 24)}`);
}

const M = 0.10;                            // secilen pay marji: kontrolde yanlislanma yok
const TOPLAM = D.reduce((a, x) => a + x.hasar, 0);
const sonrasi = (w) => D.slice(w).reduce((a, x) => a + x.hasar, 0);
console.log(`\nsecilen pay marji %${100 * M}; deneyin toplam hasari ${TOPLAM} istek`);
console.log(`\n${'ardisik pencere k'.padEnd(19)}${'durdurma penceresi'.padStart(20)}` +
  `${'durdurmaya kadar hasar'.padStart(24)}${'k=1 uzerine maliyet'.padStart(21)}`);
let temel = null;
for (const k of [1, 2, 3, 4]) {
  let ardisik = 0, dur = -1;
  for (let i = 0; i < D.length; i += 1) {
    ardisik = yanlisla(D[i], M) ? ardisik + 1 : 0;
    if (ardisik >= k) { dur = i + 1; break; }
  }
  const olan = dur < 0 ? TOPLAM : TOPLAM - sonrasi(dur);
  if (temel === null) temel = olan;
  console.log(`${String(k).padEnd(19)}${p(dur < 0 ? 'durmadi' : dur, 20)}` +
    `${p(olan, 24)}${p(`+${olan - temel}`, 21)}`);
}
```

```
kararli durum onermesi: her pencerede tam yanit payi >= taban - m; taban kontrol kosumunun pencere ortalamasi = %96.00 (10 pencere, pencere basina 40 istek)

pay marji m   kontrolde yanlislanan deneyde yanlislanan ilk yanlislanan pencere
%0                             4/10                7/10                       2
%5                             4/10                7/10                       2
%10                            0/10                4/10                       6
%20                            0/10                2/10                       8

secilen pay marji %10; deneyin toplam hasari 44 istek

ardisik pencere k    durdurma penceresi  durdurmaya kadar hasar  k=1 uzerine maliyet
1                                     6                       8                   +0
2                                     9                      28                  +20
3                                    10                      44                  +36
4                               durmadi                      44                  +36
```

İlk tablo pay marjını tarıyor. Marj %5'in altındayken önerme **kontrol koşumunda** on
pencerenin dördünde yanlışlanıyor — deney hiç açılmamışken. Bu bir **yanlış kalmadır** ve
sonucu ağırdır: durdurma ölçütü bu önermeye bağlanırsa deney daha başlamadan, ikinci pencerede
durdurulur. Marj %20'ye çıkarıldığında kontrol temizlenir ama deney yalnız iki pencerede
yanlışlanır ve ilk yanlışlanma 8. pencereye kayar; hasarın çoğu **görülmeden geçer**. Seçilen
eşik ikisinin arasındadır: **m = %10**, kaynağı kontrol koşumunun ölçülen dağılımıdır — kontrolün
en düşük penceresi tabanın 6 puan altındadır, marj o gürültünün üstüne konur.

İkinci tablo durdurma ölçütünü tarıyor. **Durdurma ölçütü**, deneyin kendiliğinden sonlandığı
kuraldır ve burada "önerme k ardışık pencerede yanlışlanırsa dur" biçimindedir. k = 1 deneyi
altıncı pencerede durdurur ve o ana kadar 8 istek hasar görmüştür. k = 2 dokuzuncu pencereye
kadar bekler: 28 istek, yani **20 istek fazladan**. k = 3 ve k = 4 deneyin sonuna kadar hiç
durdurmaz ve hasarın tamamını, 44 isteği, kabul eder. Geç durdurmanın maliyeti koşum süresiyle
değil, hasar gören istek sayısıyla yazılır.

Yedinci pencere ikinci tablonun neden yanıltıcı olabileceğini gösteriyor: deney açıkken bile o
pencerede önerme yanlışlanmadı, çünkü pencere ortalaması eşiğin üstünde kaldı. Deney beş pencere
boyunca sürdü ve önerme dördünde yanlışlandı; **yanlışlanma oranı 4/5'tir, 5/5 değil.** Pencere
ortalaması seyrek hasarı yutar ve k'yı büyütmek bu boşlukları birleştirmeyi zorlaştırır.

Bu testin sonucu bir yayım kararı değildir; deneyin kendisine ait bir karardır. Durdurma ölçütü
kırıldığında deney durdurulur, etiket geri alınır ve yanlışlanan pencere bir kayıt olarak açılır.

## Deneyin Yakalamadığı Sınıf

Deney yalnız önermenin ölçtüğü şeyi yanlışlayabilir. Önerme tam yanıt payı üzerine kurulduğu için
**gecikmeye dönüşen hasar** görünmez: zaman aşımı 400 ms olsaydı istekler tam yanıt sayılır ve
önerme sessiz kalırdı — önceki dersteki gevşek yapılandırmanın aynısı. İkinci sınıf **pencere
dışına taşan hasardır**: deney durdurulduktan sonra kuyrukta bekleyen iş ya da yeniden denenen
istek günlüğe girmez. Üçüncüsü **kapsamın dışındaki bağımlılıklardır**: bu deney yalnız katalog
sınırını bozdu; bildirim kanalı hiç sınanmadı ve tablo yine yeşil çıkardı.

## Özet

- Kaos deneyi üç kararla tanımlanır: kararlı durum önermesi, bulaşma kapsamı ve durdurma ölçütü;
  üçü de deney başlamadan sayıyla yazılır.
- Önermenin eşiği ölçümden gelir: kontrol koşumunun pencere ortalaması %96,00 taban kabul edildi
  ve pay marjı bu tabanın altına konan bir paydır.
- Planlanan bulaşma kapsamı 11 kullanıcıydı, ölçülen 44 istek çıktı: kapsam çarpanı 4,00 kat ve
  üst sınırı yığın boyudur; kapsam yalnız etiketin taşındığı kadar dardır.
- Pay marjı %5'in altındayken önerme kontrol koşumunda 4/10 pencerede yanlışlandı (yanlış kalma);
  %20'de deneyde yalnız 2/10 pencere yanlışlandı ve ilk yanlışlanma 8. pencereye kaydı.
- Seçilen marj %10'da durdurma ölçütü k = 1 iken hasar 8 istek, k = 2 iken 28, k = 3 ve
  sonrasında 44'tür; geç durdurmanın maliyeti hasar gören istek sayısıyla ölçülür.
- Deney beş pencere sürdü ve önerme dördünde yanlışlandı; pencere ortalaması seyrek hasarı yutar.

## Sonraki Adım

Bu deney sistemin **çalışmaya devam eden** bir parçasını bozdu: katalog yavaşladı, ödünç akışı
zarif bozuldu, deney durduruldu ve her şey eski hâline döndü. Geri dönüş kendiliğinden oldu,
çünkü kaybolan hiçbir şey yoktu. Bozulan şey veri olduğunda bu varsayım çöker: bir kopya
kaybolduğunda, bir kayıt bozulduğunda ya da bir birim tümüyle düştüğünde sistemi geri getiren şey
deneyin durdurulması değil, **yedeğin geri yüklenmesidir**. Yedek her gün alınmakta ve alındığı
her gün başarılı sayılmaktadır — ama bu bir iddiadır ve bu konuda hiçbir yerde sınanmadı. Sonraki
ders o iddiayı sınar: yedeğin var olması geri yüklenebilir olduğunu göstermez, planlanan kurtarma
adımları ile gerçekten gereken adımlar aynı değildir ve doğrulama kümesinin neyi denetlediği
kadar neyi denetlemediği de sayılabilir.
