---
title: 'Paralel Çalıştırma'
source: 'https://academia.sh/tr/kurslar/tumlesim-testi/paralel-calistirma'
course: 'Tümleşim, Sözleşme ve Uçtan Uca Test'
language: tr
updated: '2026-08-17T18:10:51+00:00'
license: 'CC BY-SA 4.0'
---

# Paralel Çalıştırma

Uçtan uca takımın işçilere bölünmesiyle kazanılan sürenin ve kaybedilen yalıtımın ölçülmesi; seri payın hızlanmaya koyduğu tavan, paylaşılan düzeneğin ürettiği çakışmalar, yalnız paralelde görünen yarış kusuru ve yalıtımın hem bedeli hem kör noktası.

Önceki dersin bütün testleri tek tek koştu ve karantina tartışmasının arkasında hep aynı
sayı duruyordu: koşum süresi. Buradaki soru artık hangi testin yazılacağı değil, testlerin
sırayla koşmasının bir zorunluluk mu yoksa bir seçim mi olduğudur.

Seçimdir, ve bedeli vardır. Bir takımı **işçilere** bölmek süreyi kısaltır; aynı işlem
**test yalıtımını** (test isolation) da zorunlu kılar, çünkü sırayla koşarken zararsız olan
her paylaşım artık aynı anda kullanılır. Test yalıtımı, İleri SQL kursundaki yalıtım
düzeyiyle karıştırılmamalıdır: orada eşzamanlı işlemlerin birbirinin verisini görme
kuralı, burada iki testin birbirinin düzeneğine dokunmaması söz konusudur. Bu ders iki
tarafı da sayar: kazanılan süreyi ve yalıtım için ödenen bedeli.

## Süre ve Seri Pay

Toplam süre yalnız işin bölünmesiyle değil, bölünemeyen parçayla da belirlenir: takım
kurulumu, derleme ve raporlama her koşumda bir kez ve tek parça çalışır. Bu, Sistem
Tasarımına Giriş kursundaki Yatay ve Dikey Ölçekleme dersinde kurulan **seri paydır**;
aritmetiği burada tekrarlanmaz, yalnız uçtan uca takıma uygulanır.

**PC1 — kırk test: yirmisi üç birim, on ikisi altı birim, sekizi on beş birim; seri pay on
iki birim.** Süreler model birimidir, makineye bağlı değildir. Dağıtım en uzun testten
başlayarak en boş işçiye yerleştirir.

```js
// zamanlama.mjs — kirk testin isci sayisina gore toplam suresi (model birimi)
const sureler = [
  ...Array(20).fill(3), ...Array(12).fill(6), ...Array(8).fill(15),
];
const SERI = 12;

const dagit = (isciSayisi) => {
  const yuk = Array(isciSayisi).fill(0);
  for (const sure of [...sureler].sort((a, b) => b - a)) {
    const enAz = yuk.indexOf(Math.min(...yuk));
    yuk[enAz] += sure;
  }
  return Math.max(...yuk);
};

const toplamIs = sureler.reduce((a, b) => a + b, 0);
const enUzun = Math.max(...sureler);
const tek = SERI + dagit(1);
const s = (n, g) => String(n).padStart(g);
console.log(`${'isci'.padEnd(6)}${s('paralel', 9)}${s('toplam', 8)}${s('hizlanma', 10)}${s('seri pay', 10)}`);
for (const isci of [1, 2, 4, 8, 16, 40]) {
  const paralel = dagit(isci);
  const toplam = SERI + paralel;
  console.log(`${String(isci).padEnd(6)}${s(paralel, 9)}${s(toplam, 8)}`
    + `${s(`${(tek / toplam).toFixed(2)}x`, 10)}${s(`%${((SERI / toplam) * 100).toFixed(1)}`, 10)}`);
}
console.log(`is ${toplamIs} birim, seri ${SERI} birim, en uzun test ${enUzun} birim, taban ${SERI + enUzun} birim`);
```

```
isci    paralel  toplam  hizlanma  seri pay
1           252     264     1.00x      %4.5
2           126     138     1.91x      %8.7
4            63      75     3.52x     %16.0
8            33      45     5.87x     %26.7
16           18      30     8.80x     %40.0
40           15      27     9.78x     %44.4
is 252 birim, seri 12 birim, en uzun test 15 birim, taban 27 birim
```

İlk iki satır beklendiği gibidir: iki işçi süreyi neredeyse yarıya indirir. Sonrası öyle
değil. Sekiz işçide hızlanma beş virgül seksen yedi, kırk işçide dokuz virgül yetmiş sekiz
kalır — test sayısı kadar işçi verilse bile onda birin altına inilemez. İki sınır çalışıyor:
seri pay toplamın yüzde kırk dörde kadar çıkan bir parçasını tutuyor, ve hiçbir dağıtım en
uzun testten kısa süremez. Yirmi yedi birimlik taban budur.

Buradan çıkan iki iş vardır ve ikisi de işçi eklemekten önce gelir: seri payı küçültmek ve
en uzun testi bölmek. İşçi sayısını artırmak, bu ikisi yapılmadan, ödenen bedeli
karşılamaz.

## Paylaşılan Düzenek

Ödenen bedel yalıtımdır. Aşağıdaki koşum dört testi işçilere dağıtır ve her testi
adımlarına ayırıp işçiler arasında sırayla ilerletir; paralel koşumun testleri iç içe
geçirmesi burada gerçekten olur. Her test bir üye kaydı kurar, açık ödünç sayısını okur,
ödünç yazar, sınırın aşılmadığını doğrular ve düzeneğini yıkar.

Üç seçenek argümanla verilir: kaç işçi, ödünç kararının **iki adımda** mı (önce oku, sonra
yaz) yoksa **tek adımda** mı verildiği, ve düzeneğin paylaşılıp paylaşılmadığı.

```js
// kosum.mjs — dort testin isci sayisi, servis kipi ve yalitim kipine gore kosumu
import { DatabaseSync } from 'node:sqlite';

const [isciSayisi = '1', servis = 'iki-adim', kip = 'paylasilan'] = process.argv.slice(2);
const ISCI = Number(isciSayisi);
const TEST = 4;
const SINIR = 1;
const olcum = { adim: 0, kurulumSorgusu: 0, cakisma: 0, dusen: [] };

const semaKur = () => {
  const db = new DatabaseSync(':memory:');
  db.exec(`CREATE TABLE uye (uye_no TEXT PRIMARY KEY);
           CREATE TABLE odunc (uye_no TEXT, kitap_no TEXT UNIQUE)`);
  olcum.kurulumSorgusu += 2;
  return db;
};

const veritabanlari = kip === 'ayri-sema'
  ? Array.from({ length: ISCI }, semaKur)
  : Array(ISCI).fill(semaKur());

const say = (db, uyeNo) => db.prepare('SELECT COUNT(*) AS n FROM odunc WHERE uye_no = ?').all(uyeNo)[0].n;

const testYap = (no, isci) => {
  const db = veritabanlari[isci];
  const uyeNo = kip === 'paylasilan' ? 'U-17' : `U-${no}`;
  const kitapNo = `K-${no}`;
  const yerel = {};
  const yaz = () => db.prepare('INSERT INTO odunc VALUES (?, ?)').run(uyeNo, kitapNo);
  const adimlar = [
    () => {
      olcum.kurulumSorgusu += 1;
      try { db.prepare('INSERT INTO uye VALUES (?)').run(uyeNo); } catch { olcum.cakisma += 1; }
    },
    ...(servis === 'iki-adim'
      ? [() => { yerel.n = say(db, uyeNo); }, () => { if (yerel.n < SINIR) yaz(); }]
      : [() => { if (say(db, uyeNo) < SINIR) yaz(); }]),
    () => {
      const n = say(db, uyeNo);
      if (n > SINIR) olcum.dusen.push(`test ${no}: acik odunc ${n}, sinir ${SINIR}`);
    },
    () => {
      db.prepare('DELETE FROM odunc WHERE kitap_no = ?').run(kitapNo);
      db.prepare('DELETE FROM uye WHERE uye_no = ?').run(uyeNo);
    },
  ];
  return { adimlar, i: 0 };
};

const kuyruklar = Array.from({ length: ISCI }, () => []);
for (let no = 1; no <= TEST; no += 1) kuyruklar[(no - 1) % ISCI].push(testYap(no, (no - 1) % ISCI));

while (kuyruklar.some((k) => k.length > 0)) {
  for (const kuyruk of kuyruklar) {
    const t = kuyruk[0];
    if (t === undefined) continue;
    t.adimlar[t.i]();
    olcum.adim += 1;
    t.i += 1;
    if (t.i === t.adimlar.length) kuyruk.shift();
  }
}

console.log(`isci ${ISCI}  servis ${servis}  kip ${kip}`);
console.log(`adim ${olcum.adim}  kurulum sorgusu ${olcum.kurulumSorgusu}`
  + `  kurulum cakismasi ${olcum.cakisma}  dusen test ${olcum.dusen.length}/${TEST}`);
for (const satir of olcum.dusen) console.log(satir);
```

Tek işçiyle, yani sırayla, takım temizdir.

```bash
node kosum.mjs 1 iki-adim paylasilan
```

```
isci 1  servis iki-adim  kip paylasilan
adim 20  kurulum sorgusu 6  kurulum cakismasi 0  dusen test 0/4
```

## Yalnız Paralelde Düşen Test

Aynı kod, aynı veri, iki işçi.

```bash
node kosum.mjs 2 iki-adim paylasilan
```

```
isci 2  servis iki-adim  kip paylasilan
adim 20  kurulum sorgusu 6  kurulum cakismasi 2  dusen test 4/4
test 1: acik odunc 2, sinir 1
test 2: acik odunc 2, sinir 1
test 3: acik odunc 2, sinir 1
test 4: acik odunc 2, sinir 1
```

İki ayrı olay birlikte görünüyor. Birincisi **kurulum çakışmasıdır**: iki test aynı üye
kaydını aynı anda kurmaya çalıştı ve ikisinden biri teklik kısıtına takıldı. Bu, testin
kendi düzeneğine ait bir sorundur.

İkincisi bir ürün kusurudur ve adı **kontrol-sonra-yaz yarışıdır**: ödünç kararı iki adımda
veriliyor — önce açık ödünç sayısı okunuyor, sonra kayıt yazılıyor. İki adım arasında başka
bir işçinin yazması, okunan sayıyı geçersiz kılar. İki test de sıfır okudu, ikisi de yazdı,
üyenin bir olan sınırı ikiye çıktı. Sıralı koşumda bu kusur **hiçbir zaman** görünmez;
önceki koşumun sıfır düşüşü onun kanıtıdır. Paralel çalıştırmanın yakaladığı sınıf budur.

Düzeltme, kararı bölünemez tek bir adımda vermektir.

```bash
node kosum.mjs 2 tek-adim paylasilan
```

```
isci 2  servis tek-adim  kip paylasilan
adim 16  kurulum sorgusu 6  kurulum cakismasi 2  dusen test 0/4
```

Testler yeşile döndü, ama kurulum çakışması iki olarak duruyor — ve bu satır ikinci bir
uyarıdır. Çakışmaya uğrayan iki test kendi üyesini kuramadı; ödünç istekleri sınıra takılıp
sessizce reddedildi ve doğrulama yine de geçti. Yeşil oldukları hâlde sınamak istedikleri
şeyi sınamadılar. Düzeneğin paylaşılması, testi düşürmediğinde bile anlamını boşaltır.

## Yalıtımın Bedeli ve Kör Noktası

Yalıtımın iki düzeyi vardır: her teste ayrı kayıt kümesi vermek ya da her işçiye ayrı şema
kurmak. İkisi de aynı kusurla, iki adımlı servisle koşuluyor.

```bash
node kosum.mjs 2 iki-adim ayri-kayit
```

```
isci 2  servis iki-adim  kip ayri-kayit
adim 20  kurulum sorgusu 6  kurulum cakismasi 0  dusen test 0/4
```

```bash
node kosum.mjs 2 iki-adim ayri-sema
```

```
isci 2  servis iki-adim  kip ayri-sema
adim 20  kurulum sorgusu 8  kurulum cakismasi 0  dusen test 0/4
```

Bedel iki sayıda görünür. Ayrı kayıt kümesi çakışmayı sıfıra indirir ve hiçbir şey
eklemez; ayrı şema kurulum sorgusunu altıdan sekize çıkarır, çünkü her işçi kendi
tablolarını yaratır. İşçi sayısı arttıkça bu fark doğrusal büyür: şema kurulumu işçi başına
ödenir, kayıt kümesi ise test başına.

Kaçan sınıf da tam burada. İki koşum da yeşil, oysa **kusur yerinde duruyor** — servis hâlâ
iki adımda karar veriyor. Yalıtım, iki testin aynı üyeye aynı anda dokunmasını engellediği
için yarışın koşulunu ortadan kaldırdı. Yalıtım kırılganlığı giderirken, kırılganlığı
üreten paylaşımın kendisine bağlı kusur sınıfını da görünmez kılar. Gerçek kullanıcılar
kayıtları paylaşır; test takımı paylaşmadığı sürece o yarış yalnız üretimde ortaya çıkar.

Bu yüzden yalıtım kararı bir denge kararıdır: koşum düzeninin yalıtımı **tam**, ürünün
eşzamanlılık denetimi ise **ayrıca** sınanmalıdır. Bedelin koşuma bağlı yanı bu koşumda
küçüktür — beş yapılandırmanın her biri yirmi milisaniyenin altında sürdü, çünkü koşan şey
süreç içi bir modeldir. Koşumdan bağımsız sayılar ölçümlerdedir: yirmi adım, altı ile sekiz
arası kurulum sorgusu, iki çakışan kayıt ve yüzde dört buçuktan yüzde kırk dörde çıkan seri
pay.

## Özet

- İşçi eklemek süreyi seri pay ve en uzun test kadar kısaltır: kırk testte kırk işçi bile
  dokuz virgül yetmiş sekiz kat hızlanma verdi, taban yirmi yedi birimde kaldı.
- Paylaşılan düzenek paralelde iki kurulum çakışması üretti; çakışan test düşmese bile
  sınamak istediği şeyi sınamamış olur.
- Kontrol-sonra-yaz yarışı yalnız paralel koşumda göründü: sıralı koşumda sıfır düşüş,
  iki işçide dört testin dördü de düştü.
- Kararı tek adıma indirmek testleri yeşile döndürdü; yalıtım ise aynı kusur yerinde
  dururken koşumu yeşil yaptı.
- Yalıtımın bedeli ölçülebilir: ayrı kayıt kümesi kurulum sorgusunu artırmadı, ayrı şema
  altıdan sekize çıkardı ve bu fark işçi başına büyür.

## Kurs Kapanışı

Bu kursun on beş dersi aynı üç soruyu yanıtladı. Tablodaki her satır bir test biçiminin ne
gördüğünü, ne göremediğini ve ne kadara mal olduğunu söyler.

| Ders | Yakaladığı hata sınıfı | Yakalamadığı | Bedeli |
|---|---|---|---|
| Tümleşim Testinin Kapsamı | şema uyuşmazlığı | muhatap kimliği uyuşmazlığı | tur başına 4 sorgu, 9 satır düzenek |
| Test Ortamı Yönetimi | bilinmeyen muhatabın reddi | süreç ömrüne bağlı durum | 1 süreç, 6 adım, koşum başına 1 dosya |
| Test Verisi Yönetimi | dağılıma bağlı rapor kusuru | içeriğe bağlı dışa aktarma kusuru | 772 yükleme sorgusu, 78 kilobayt |
| Veritabanı Testleri | geri göçte veri kaybı | geri doldurma değeri uyuşmazlığı | 13 göç adımı, 21 sorgu, 1 dosya |
| Dış Servis Taklidi | istek biçimi kayması | dondurulmuş yanıtın eskimesi | oynatmada 0 süreç, 0 ağ çağrısı |
| API Testi | yapısal uyuşmazlık | anlamsal uyuşmazlık | 2 süreç, sınama başına 1 istek, 16 kural |
| Tüketici Güdümlü Sözleşme Testi | sözleşmedeki alanın kaybolması | çalıştırılmamış dalın okuduğu alan | 2 bileşen, 100 istek, 235 baytlık dosya |
| Şema Uyumluluk Denetimi | kırıcı değişiklik | biçimi bozmadan anlamı değiştiren değişiklik | 0 süreç, 0 istek, 9 satırlık sınıf tablosu |
| Sahte Sunucularla Geliştirme | biçim sapması | durum sapması | 2 süreç, 10 istek, 15 satır düzenek |
| Test Piramidi | sınır karşılaştırması, birim seviyesinde | durum kodu eşlemesi, alt seviyelerde | 24 vaka için 242 sorgu ve 24 istek |
| Tarayıcı Otomasyonu | kart durumunun güncellenmemesi | düğme etiketinin yanlışlığı | 2000 yüklemede 16.477 yoklama |
| Mobil Otomasyonu | sürüme bağlı tarih ayrıştırma | hücreye özgü kusurun 24/30'u | 12 hücre koşumu, 30 hücrelik matris |
| Görsel Doğrulama | düğme tonunun açılması | etkisiz düğme, aynı pikselleri üretir | görüntü 800 piksel, dışlamayla sayılan 784 |
| Kırılganlık Yönetimi | reddedilen istekte uyarının yokluğu | karantinadaki testin koruduğu sınıf | 40 test × 200 koşum, 8000 test koşumu |
| Paralel Çalıştırma | kontrol-sonra-yaz yarışı | yalıtımın gizlediği paylaşım kusuru | kurulum sorgusu 6'dan 8'e, seri pay %4,5'ten %44,4'e |

Orta sütun kursun kuralıdır: bir test katmanının değeri ancak **sınırıyla** birlikte
anlatılabilir. On beş satırın hiçbirinde "daha güvenli" bir seçenek yoktur; her satırda
yakalanan bir sınıf, kaçan bir sınıf ve ödenen bir bedel vardır. Bir test biçimini
savunmanın tek yolu bu üçlüyü yazmaktır, ve bir biçimi eklemenin gerekçesi ancak
**başka hiçbir katmanda karşılığı olmayan** bir kusur sınıfıdır.

Kursun kendi sınırı da aynı kuralla yazılır. Birinci konu sahte bağımlılıkların gerçeğe
uyup uymadığını sordu, ikincisi sözleşmenin denetlenebilir parçasını ayırdı, üçüncüsü
kullanıcının gördüğü yolu bir bütün olarak koşturdu. Üçünün de sorduğu soru aynıydı:
**işlev doğru mu?** Sistemin yük altında nasıl davrandığı, saldırı altında ne verdiği ve
arıza altında nasıl bozulduğu hiçbir derste sınanmadı. İşlevsel Olmayan Test kursu bu üç
soruyu ele alır.
