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

# Hata Enjeksiyonu

Arıza senaryolarının bilinçli tetiklenmesi: katalog servisine gecikme, hata ve kayıp enjekte edilerek ödünç akışının kalıbının gerçekten devreye girip girmediğinin ölçülmesi, düşme oranı eşiğinin taranarak yanlış geçme ile yanlış kalmanın sayılması, enjeksiyon katmanının kod yolu ve varsayılan kapalılık denetimi cinsinden bedeli ve enjeksiyonun göremediği arıza sınıfı.

Önceki konu güvenlik testini kapattı ve bir sınır bıraktı. Yük de saldırı da **dışarıdan**
gelen bir baskıydı: birinde çok sayıda geçerli istek, ötekinde bilerek bozulmuş bir istek.
İkisinde de sistemin parçaları çalışıyordu. Sistemin kendi bileşenlerinden biri bozulduğunda
ne olacağı hiçbir derste sınanmadı.

**Hata enjeksiyonu**, bir arıza senaryosunu çalışan sistemde bilerek tetiklemek ve verilen
yanıtı ölçmektir. Ödünç servisi katalog servisine bağlıdır ve o bağlantıya Dayanıklılık ve
Güvenilirlik kursunda kurulan iki kalıp konmuştur: zaman aşımı ve yedek yolla zarif bozulma.
Kalıpların ne yaptığı orada anlatıldı; buradaki soru başkadır — **kalıp gerçekten devreye
giriyor mu ve bunu hangi eşikle söyleyebiliriz.**

## Enjeksiyon Düzeneği

Düzenek bir **modeldir** ve kendi yazılmıştır; gerçek bir kaos aracı ya da küme kurulmaz.
Katalog servisi yerel bir `node:http` sürecidir ve önüne bir enjeksiyon katmanı konmuştur.
Katman üç arıza türü üretir: **gecikme** (yanıt geciktirilir), **hata** (503 döner), **kayıp**
(yanıt hiç yazılmaz). Hangi isteğin bozulacağı istek sırasından tohumlu bir karma ile türetilir;
aynı tohum aynı istek kümesini bozar.

**IO14 — sağlıklı katalog isteklerinin 0,03'ü 150 ms duraklar.** Varsayımdır: gerçek bir servis
arızasız günde de tam düz değildir ve bu duraklama, eşiğin tabanını belirleyecektir.

```js
// katalog.mjs — katalog servisi ve enjeksiyon katmani. Enjeksiyon plani disaridan verilir,
// varsayilani kapalidir; hangi istegin bozulacagi istek sirasindan deterministik turetilir.
import { createServer } from 'node:http';

export const KAPALI = { tur: 'kapali', oran: 0, sure: 0 };
export const TOHUM = { enjeksiyon: 20260731, gurultu: 19870415 };
export const GURULTU = { oran: 0.03, sure: 150 };      // IO14: arka plan duraklamasi

export function karma(tohum, i) {                      // tohumlu, sira basina deterministik deger
  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;
}

const KUNYE = { 'K-903': { baslik: 'kitap K-903', yazar: 'Sabahattin Ali' } };

export function katalogKur(plan = KAPALI) {
  const olcu = { istek: 0, enjekte: 0, duraklama: 0 };
  const sunucu = createServer((istek, yanit) => {
    const adres = new URL(istek.url, 'http://yerel');
    const sira = Number(adres.searchParams.get('sira') ?? 0);
    const no = adres.searchParams.get('no') ?? 'K-903';
    olcu.istek += 1;
    const enjekte = plan.tur !== 'kapali' && karma(TOHUM.enjeksiyon, sira) < plan.oran;
    if (enjekte) olcu.enjekte += 1;
    const gecikmeli = enjekte && plan.tur === 'gecikme';
    const duraklama = gecikmeli === false && karma(TOHUM.gurultu, sira) < GURULTU.oran;
    if (duraklama) olcu.duraklama += 1;
    if (enjekte && plan.tur === 'kayip') return;             // yanit hic yazilmaz
    const gonder = () => {
      if (enjekte && plan.tur === 'hata') {
        yanit.writeHead(503, { 'content-type': 'application/json' });
        return yanit.end('{"hata":"katalog yanit veremiyor"}');
      }
      yanit.writeHead(200, { 'content-type': 'application/json' });
      yanit.end(JSON.stringify({ no, ...KUNYE[no] }));
    };
    const bekle = gecikmeli ? plan.sure : (duraklama ? GURULTU.sure : 0);
    if (bekle === 0) gonder(); else setTimeout(gonder, bekle);
  });
  sunucu.listen(0, '127.0.0.1');
  return { sunucu, olcu };
}
```

Ödünç akışı üç sonuçtan birini üretir: **tam** yanıt, **zarif bozulmuş** yanıt (künye alanları
boş) ya da **düşen** istek; yedek yol kapatıldığında ikinci sonuç kaybolur.

```js
// odunc.mjs — odunc akisi katalogdan kunye ister. Zaman asimi ve yedek yol Dayaniklilik ve
// Guvenilirlik kursunda kurulan kaliplardir; burada sinanan devreye girip girmedikleridir.
export function oduncAkisi({ taban, zamanAsimi, yedekYol }) {
  const s = { tam: 0, bozulmus: 0, dusen: 0, zamanAsimiSayisi: 0, hataSayisi: 0 };
  return {
    olcu: () => ({ ...s }),
    async oduncVer(sira, no = 'K-903') {
      try {
        const y = await fetch(`${taban}/kunye?no=${no}&sira=${sira}`,
          { signal: AbortSignal.timeout(zamanAsimi) });
        if (y.ok === false) { await y.text(); throw new Error(`durum ${y.status}`); }
        const g = await y.json();
        s.tam += 1;
        return { no, baslik: g.baslik, durum: 'tam' };
      } catch (h) {
        if (h.name === 'TimeoutError' || h.name === 'AbortError') s.zamanAsimiSayisi += 1;
        else s.hataSayisi += 1;
        if (yedekYol === false) { s.dusen += 1; return null; }
        s.bozulmus += 1;                       // zarif bozulma: kunye alanlari olmadan yanit
        return { no, baslik: null, durum: 'bozulmus' };
      }
    },
  };
}
```

## Arıza Türü Başına Verilen Yanıt

Enjeksiyon üç yapılandırmada koşturulur. **Sağlam** yapılandırma sınanan sistemdir; **gevşek**
ile **yedeksiz** bilerek bozulmuş iki sürümdür ve testin onları ayırt edip edemediğini ölçer.

**IO13 — koşum 200 istek, 8 eş zamanlı istemci.** **IO15 — enjeksiyon oranı 0,20, gecikme 240 ms.**
**IO16 — zaman aşımı sağlamda 100 ms, gevşekte 400 ms.** Üçü de varsayımdır; IO16'nın gerekçesi
şudur: 100 ms hem 150 ms'lik duraklamayı hem 240 ms'lik gecikmeyi keser, 400 ms ikisini de yutar.

```js
// kosum.mjs — uc yapilandirma dort ariza turunde kosturulur ve olcum olcum.json'a yazilir.
// Sayimlar deterministiktir; yalniz duvar suresi kosuma baglidir.
import { writeFileSync } from 'node:fs';
import { katalogKur, KAPALI, GURULTU } from './katalog.mjs';
import { oduncAkisi } from './odunc.mjs';

const N = 200, ESZAMAN = 8;                     // IO13
const PLAN = {
  kapali: KAPALI,
  gecikme: { tur: 'gecikme', oran: 0.20, sure: 240 },   // IO15
  hata: { tur: 'hata', oran: 0.20, sure: 0 },
  kayip: { tur: 'kayip', oran: 0.20, sure: 0 },
};
const YAPI = {
  saglam: { zamanAsimi: 100, yedekYol: true },           // IO16
  gevsek: { zamanAsimi: 400, yedekYol: true },
  yedeksiz: { zamanAsimi: 100, yedekYol: false },
};

async function kosum(yapiAdi, planAdi) {
  const { sunucu, olcu } = katalogKur(PLAN[planAdi]);
  await new Promise((c) => sunucu.once('listening', c));
  const taban = `http://127.0.0.1:${sunucu.address().port}`;
  const akis = oduncAkisi({ taban, ...YAPI[yapiAdi] });
  let sira = 0;
  const isci = async () => { while (sira < N) { const i = sira; sira += 1; await akis.oduncVer(i); } };
  await Promise.all(Array.from({ length: ESZAMAN }, isci));
  sunucu.closeAllConnections();
  await new Promise((c) => sunucu.close(c));
  return { ...akis.olcu(), ...olcu };
}

const sonuc = {};
const p = (x, n) => String(x).padStart(n);
console.log(`${N} istek, ${ESZAMAN} es zamanli; enjeksiyon orani ${PLAN.gecikme.oran}, gecikme ` +
  `${PLAN.gecikme.sure} ms, arka plan duraklamasi ${GURULTU.oran} / ${GURULTU.sure} ms`);
console.log(`\n${'yapilandirma'.padEnd(13)}${'ariza'.padEnd(9)}${'enjekte'.padStart(8)}` +
  `${'tam'.padStart(6)}${'bozulmus'.padStart(10)}${'dusen'.padStart(7)}` +
  `${'zaman asimi'.padStart(13)}${'dusme orani'.padStart(13)}`);
for (const yapi of Object.keys(YAPI)) {
  for (const plan of Object.keys(PLAN)) {
    const r = await kosum(yapi, plan);
    sonuc[`${yapi}/${plan}`] = r;
    console.log(`${yapi.padEnd(13)}${plan.padEnd(9)}${p(r.enjekte, 8)}${p(r.tam, 6)}` +
      `${p(r.bozulmus, 10)}${p(r.dusen, 7)}${p(r.zamanAsimiSayisi, 13)}` +
      `${p('%' + ((100 * r.dusen) / N).toFixed(2), 13)}`);
  }
}
writeFileSync('olcum.json', `${JSON.stringify({ N, sonuc }, null, 1)}\n`);
console.log(`\narizasiz kosumda enjeksiyon denetimi ${sonuc['saglam/kapali'].istek} istekte calisti, ` +
  `${sonuc['saglam/kapali'].enjekte} tanesinde enjeksiyon yapti`);
```

```
200 istek, 8 es zamanli; enjeksiyon orani 0.2, gecikme 240 ms, arka plan duraklamasi 0.03 / 150 ms

yapilandirma ariza     enjekte   tam  bozulmus  dusen  zaman asimi  dusme orani
saglam       kapali          0   190        10      0           10        %0.00
saglam       gecikme        45   148        52      0           52        %0.00
saglam       hata           45   148        52      0           10        %0.00
saglam       kayip          45   148        52      0           52        %0.00
gevsek       kapali          0   200         0      0            0        %0.00
gevsek       gecikme        45   200         0      0            0        %0.00
gevsek       hata           45   155        45      0            0        %0.00
gevsek       kayip          45   155        45      0           45        %0.00
yedeksiz     kapali          0   190         0     10           10        %5.00
yedeksiz     gecikme        45   148         0     52           52       %26.00
yedeksiz     hata           45   148         0     52           10       %26.00
yedeksiz     kayip          45   148         0     52           52       %26.00

arizasiz kosumda enjeksiyon denetimi 200 istekte calisti, 0 tanesinde enjeksiyon yapti
```

Sağlam yapılandırmanın üç satırı aynı sonucu veriyor: 45 enjekte edilen istek, artı duraklamış 10
istek, zarif bozulmuş yanıta dönüyor — toplam 52 — ve düşen istek sıfır. Ama kalıbı devreye sokan
yol aynı değil: gecikme ve kayıpta 52 zaman aşımı sayılıyor, hatada yalnız 10. Hata türü zaman
aşımını hiç çalıştırmaz; kalıbın öteki yarısı olan durum kodu denetimi devreye girer. **Aynı
düşme oranı farklı kod yollarından gelebilir**, bu yüzden ölçüm hangi yolun çalıştığını da sayar.

Gevşek yapılandırmanın ikinci satırı dersin en önemli sayısıdır: enjekte edilen 45 isteğin hepsi
**tam** yanıta dönüyor, bozulmuş yanıt sıfır, zaman aşımı sıfır. Kalıp hiç devreye girmedi ve
sistem bunu kusursuz bir sonuç gibi gösterdi — 200 tam yanıt, %0,00 düşme oranı. Bozuk
yapılandırma, sağlam yapılandırmadan **daha temiz** görünüyor.

## Eşik ve Eşiğin Kaynağı

Bir arıza altında "başarılı" demek bir eşik seçmektir; en doğal eşik düşme oranıdır. Aşağıdaki
tarama iki kuralı beş eşikte karşılaştırıyor: her eşikte kaç kusurlu yapılandırma geçiyor
(**yanlış geçme**), kaç sağlam yapılandırma kalıyor (**yanlış kalma**).

```js
// esik.mjs — kosum.mjs'in yazdigi olcum.json uzerinde esik taramasi: her esikte kac kusurlu
// yapilandirma geciyor, kac saglam yapilandirma kaliyor.
import { readFileSync } from 'node:fs';

const { N, sonuc } = JSON.parse(readFileSync('olcum.json', 'utf8'));
const GERCEK = { saglam: 'saglam', gevsek: 'kusurlu', yedeksiz: 'kusurlu' };
const ARIZA = ['gecikme', 'hata', 'kayip'];

function kuralA(yapi, e) {                       // yalniz dusme orani esigi
  const k = sonuc[`${yapi}/kapali`];
  if ((k.bozulmus + k.dusen) / N > e) return false;
  return ARIZA.every((a) => sonuc[`${yapi}/${a}`].dusen / N <= e);
}
function kuralB(yapi, e) {                       // dusme orani + kalibin devreye girme sayisi
  if (kuralA(yapi, e) === false) return false;
  return ARIZA.every((a) => sonuc[`${yapi}/${a}`].bozulmus >= sonuc[`${yapi}/${a}`].enjekte);
}

const say = (kural, e) => {
  let gecme = 0, kalma = 0;
  for (const [yapi, g] of Object.entries(GERCEK)) {
    const gecti = kural(yapi, e);
    if (gecti && g === 'kusurlu') gecme += 1;
    if (gecti === false && g === 'saglam') kalma += 1;
  }
  return { gecme, kalma };
};

const p = (x, n) => String(x).padStart(n);
console.log(`${'esik e'.padEnd(9)}${'kural'.padEnd(9)}${'gecen yapilandirma'.padStart(24)}` +
  `${'yanlis gecme'.padStart(14)}${'yanlis kalma'.padStart(14)}`);
for (const e of [0, 0.02, 0.05, 0.10, 0.30])
  for (const [ad, kural] of [['A', kuralA], ['B', kuralB]]) {
    const r = say(kural, e);
    const gecen = Object.keys(GERCEK).filter((y) => kural(y, e));
    console.log(`${('%' + (100 * e).toFixed(0)).padEnd(9)}${ad.padEnd(9)}` +
      `${(gecen.join(",") || "-").padStart(24)}${p(r.gecme, 14)}${p(r.kalma, 14)}`);
  }
console.log(`\nA: arizasiz kosumda yedek yola giden pay <= e, her arizada dusme orani <= e`);
console.log(`B: A ve her arizada zarif bozulmus yanit >= enjekte edilen istek ` +
  `(${sonuc['saglam/gecikme'].enjekte}; kaynak: duzenegin sayaci)`);
```

```
esik e   kural          gecen yapilandirma  yanlis gecme  yanlis kalma
%0       A                          gevsek             1             1
%0       B                               -             0             1
%2       A                          gevsek             1             1
%2       B                               -             0             1
%5       A                   saglam,gevsek             1             0
%5       B                          saglam             0             0
%10      A                   saglam,gevsek             1             0
%10      B                          saglam             0             0
%30      A          saglam,gevsek,yedeksiz             2             0
%30      B                          saglam             0             0

A: arizasiz kosumda yedek yola giden pay <= e, her arizada dusme orani <= e
B: A ve her arizada zarif bozulmus yanit >= enjekte edilen istek (45; kaynak: duzenegin sayaci)
```

Kural A hiçbir eşikte iki hatayı birden sıfıra indiremiyor. Eşik %2'nin altındayken sağlam
yapılandırma kalıyor — arka plan duraklaması arızasız koşumda bile 10 isteği yedek yola
gönderiyor; bu bir **yanlış kalmadır** ve kaynağı sistemin kusuru değil, ortamın gürültüsüdür.
Eşik %30'da yedeksiz yapılandırma da geçiyor: iki **yanlış geçme**. Arada kalan %5–%10
aralığında yanlış kalma sıfırdır ama gevşek yapılandırma her eşikte geçer; kusurun etkisi düşme
değil, gecikmeye dönüşmedir.

Kural B ikinci bir eşik ekliyor ve o eşiğin kaynağı enjeksiyonun kendisidir: düzenek 45 isteği
bozduğunu **sayıyor**, dolayısıyla kalıptan tam 45 zarif bozulmuş yanıt istenebilir —
**enjeksiyon, eşiğin kaynağını da üretir**. Seçilen eşik budur: e = %5 (kaynağı ölçümdür,
arızasız koşumda yedek yola giden 10/200 payı) ve enjekte başına bir zarif bozulmuş yanıt
(kaynağı düzeneğin sayacıdır). Bu ikilide yanlış geçme ve yanlış kalma sıfırdır.

Kural B'nin sınırı da ölçüldü: gevşek yapılandırmayı yalnız **gecikme** arızası ele veriyor;
hata ve kayıp satırlarında bozulmuş yanıt 45'tir ve kural geçer. Kusuru görünür kılan şey eşik
değil, enjekte edilen arıza türüdür. Sonuç bir yayım kararıdır: kural B kırıldığında dayanıklılık
yapılandırması değişmiş demektir ve yayım durur.

## Enjeksiyonun Kendi Bedeli

Enjeksiyon katmanı üretim kodunun içinde durur ve arızasız günde de her istekte çalışır.

```js
// bedel.mjs — enjeksiyon duzeneginin kendi bedeli: kod yolu, varsayilan denetimi ve plani
// acabilen yol sayisi. olcum.json'u kosum.mjs uretir.
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { katalogKur } from './katalog.mjs';

const kod = readFileSync('katalog.mjs', 'utf8').split('\n')
  .filter((s) => s.trim() !== '' && s.trim().startsWith('//') === false);
const enj = /plan|enjekte|gecikmeli|GURULTU|duraklama|karma|TOHUM/;
const dal = kod.filter((s) => /if |\? /.test(s) && enj.test(s));

const { sunucu, olcu } = katalogKur();               // plan verilmedi: varsayilan kapali
await new Promise((c) => sunucu.once('listening', c));
const taban = `http://127.0.0.1:${sunucu.address().port}`;
for (let i = 0; i < 50; i += 1) await (await fetch(`${taban}/kunye?no=K-903&sira=${i}`)).json();
sunucu.closeAllConnections();
await new Promise((c) => sunucu.close(c));
assert.equal(olcu.enjekte, 0, 'varsayilan plan enjeksiyon yapmamali');

const arizasiz = JSON.parse(readFileSync('olcum.json', 'utf8')).sonuc['saglam/kapali'];
const es = kod.filter((s) => enj.test(s)).length;
console.log(`katalog.mjs: ${kod.length} satir kod, ${es} tanesi enjeksiyon katmani ` +
  `(%${((100 * es) / kod.length).toFixed(1)}); istek yolunda ${dal.length} karar noktasi`);
console.log(`arizasiz kosumda enjeksiyon denetimi ${arizasiz.istek}/${arizasiz.istek} istekte ` +
  `calisti, ${arizasiz.enjekte} enjeksiyon yapti`);
console.log(`plani acabilen yol: 1 (katalogKur'un plan parametresi); varsayilan denetimi ` +
  `50 istekte ${olcu.enjekte} enjeksiyon - gecti`);
```

```
katalog.mjs: 38 satir kod, 13 tanesi enjeksiyon katmani (%34.2); istek yolunda 5 karar noktasi
arizasiz kosumda enjeksiyon denetimi 200/200 istekte calisti, 0 enjeksiyon yapti
plani acabilen yol: 1 (katalogKur'un plan parametresi); varsayilan denetimi 50 istekte 0 enjeksiyon - gecti
```

Bedel koşumdan bağımsız niceliklerle yazılıdır: 38 satırlık katalog kodunun 13'ü (%34,2)
enjeksiyon katmanıdır, istek yolunda 5 karar noktası ekler ve bu denetimler arızasız koşumda
200 isteğin **hepsinde** çalışır. Asıl bedel bu değildir: düzeneğin üretimde yanlışlıkla açık
kalması, ödünç akışının kendi kendini bozması demektir. Bunun karşılığı **planı açabilen yol
sayısıdır**; burada 1'dir ve varsayılanın kapalı olduğu bir iddiayla sınanır. Plan bir çevre
değişkeninden, bir başlıktan ve bir yapılandırma dosyasından da okunsaydı sayı 4 olur, dördü de
ayrı denetlenmek zorunda kalırdı.

## Enjeksiyonun Göremediği Arıza Sınıfı

Enjeksiyon arızanın **belirtisini** üretir, nedenini değil. Bu ayrım üç sınıfı düzeneğin dışında
bırakır. Birincisi **çok bileşenli arızadır**: katman tek bir sınıra konur, oysa katalog ile
bildirim kanalı aynı anda bozulabilir. İkincisi **belirtinin altındaki katmandır**: disk dolması
aynı gecikmeyi verir ama yanında günlük kaydının kesilmesi gibi ikincil etkiler getirir. Üçüncüsü
**katmanın konmadığı çağrı yerleridir**: bir bağımlılığa katman eklenmemişse o yol hiç sınanmaz
ve tablo yine yeşil çıkar. Düzeneğin kapsamı, kapsadığı sınır sayısı kadardır.

## Özet

- Hata enjeksiyonu bir arıza senaryosunu bilerek tetikleyip verilen yanıtı ölçer; üç tür enjekte
  edildi (gecikme, hata, kayıp), düzenek kendi yazıldı ve tohum görünürdür.
- Sağlam yapılandırmada üç arıza türü de aynı sonucu verdi (52 zarif bozulmuş yanıt, 0 düşen)
  ama aynı kod yolundan geçmedi: gecikme ve kayıpta 52 zaman aşımı, hatada 10.
- Zaman aşımı enjekte edilen gecikmeden uzun olan bozuk yapılandırma 200 tam yanıt ve %0,00 düşme
  oranı üretti; düşme oranı eşiği bu kusuru hiçbir değerde yakalamaz.
- Eşik taramasında kural A %2 altında 1 yanlış kalma, %30'da 2 yanlış geçme verdi; enjekte edilen
  istek sayısını eşik yapan kural B ikisini de sıfırladı.
- Seçilen eşik e = %5 (kaynağı ölçüm: arızasız koşumda yedek yola giden 10/200) ve enjekte başına
  bir zarif bozulmuş yanıt (kaynağı düzeneğin sayacı); kural kırıldığında yayım durur.
- Bedel 38 satırın 13'ü, istek yolunda 5 karar noktası ve 200/200 denetimdir; asıl risk planın
  üretimde açık kalmasıdır ve açma yolu sayısı 1 tutulup varsayılan bir iddiayla sınanır.

## Sonraki Adım

Bu derste enjeksiyon bir **test düzeneğiydi**: kapsam tek bir sınırdı, ortam yalnız bu ders için
ayağa kalktı ve bozulan şeyin kimseye zararı yoktu. Aynı enjeksiyonun gerçek trafiğin aktığı bir
ortamda yapılması başka bir sorudur ve o soru kalıbın çalışıp çalışmadığından önce gelir:
**bozmaya ne zaman izin verilir ve ne zaman durdurulur.** Sistemin normal davranışı yazılmadan
bir sapma ölçülemez; deneyin kaç isteği etkileyeceği önceden sınırlanmadan bulaşma denetlenemez;
işler beklenenden kötü giderse deneyi hangi sayıya bakarak durduracağı belli olmalıdır. Sonraki
ders bu üç kararı — kararlı durum önermesi, bulaşma kapsamının sınırlanması ve durdurma ölçütü —
kurar ve her birinin geç ya da yanlış kurulmasının bedelini sayar.
