---
title: 'Aşamalı Yayın ve Geri Alma'
source: 'https://academia.sh/tr/kurslar/olusturma-stratejileri/asamali-yayin-ve-geri-alma'
course: 'Oluşturma Stratejileri ve Altyapı'
language: tr
updated: '2026-08-17T18:09:30+00:00'
license: 'CC BY-SA 4.0'
---

# Aşamalı Yayın ve Geri Alma

Yayın riskinin trafiğin payıyla sınırlanması; yapışkan atama, izleme penceresi ve gürültüye dayanıklı eşikle otomatik geri alma, işaretçi değişimiyle atomik sürüm geçişi ve geri alınamayan değişikliklerin genişlet–daralt sırasıyla ele alınması.

Önizlemede sınanmış bir değişiklik yayına girdiğinde bütün kullanıcılara aynı anda ulaşır.
Önizlemenin yakalayamadığı her kusur da öyle: yalnız gerçek trafikte ortaya çıkan yük
davranışları, yalnız gerçek veride bulunan uç durumlar, yalnız belirli aygıtlarda görülen
hatalar.

Bu ders yayını bir anahtar çevirmesi olmaktan çıkarır. Trafiğin küçük bir payı yeni sürüme
yönlendirilir, hata oranı bir eşiğe karşı izlenir, eşik aşıldığında önceki dağıtıma dönülür.
Sorusu şudur: pay, eşik ve geri alma süresi hangi ölçülere göre seçilir?

## Aşamalı Yayının Dört Parçası

**Pay planı.** Yeni sürüme yönlendirilen trafik oranı, aralarında gözlem süresi bırakılan
adımlarla artırılır. Her adım bir karar noktasıdır: devam mı, geri mi.

**Yapışkan atama.** Bir kullanıcının hangi sürümü göreceği, kimliğinden türetilen bir kovayla
belirlenir ve yayın boyunca değişmez. Atama isteğe göre yapılırsa aynı kullanıcı iki sürüm
arasında gidip gelir; bunun üç somut sonucu vardır — belgenin taşıdığı karmalı adlar öteki
sürümün parçalarını istemeye başlar, oturum içinde yarım kalan akışlar bozulur ve ölçüm
kirlenir. Kova sayısı payın çözünürlüğünü verir: yüz kova, yüzde bir adım demektir.

**İzleme penceresi ve eşik.** Karar, son bir pencerede yeni sürüme düşen isteklerin hata
oranına bakılarak verilir. Pencerenin en az bir örneklem büyüklüğüne ulaşmış olması gerekir;
ulaşmadan verilen karar gürültüdür.

**Geri alma.** Eşik aşıldığında payın sıfırlanması. Bunun ön koşulu, dönülecek dağıtımın
hâlâ yayında duruyor olmasıdır — üçüncü dersteki korunan dağıtımlar kuralı burada işe yarar.

## Aynı Kusurun Dört Yayın Biçimindeki Bedeli

```js
// asamali.mjs — yuzde payli yayin, yapiskan atama ve esige gore geri alma.
// Sanal saat kullanilir; Date.now() cagrilmaz, sonuc her kosuda aynidir.
import { createHash } from "node:crypto";

// Belirlenimci uretec (splitmix32). Tohum sabit oldugu icin dizi her kosuda aynidir.
function uretec(tohum) {
  let s = tohum >>> 0;
  return () => {
    s = (s + 0x9e3779b9) >>> 0;
    let z = s;
    z = Math.imul(z ^ (z >>> 16), 0x21f0aaad) >>> 0;
    z = Math.imul(z ^ (z >>> 15), 0x735a2d97) >>> 0;
    return ((z ^ (z >>> 15)) >>> 0) / 4294967296;
  };
}

const SURE = 3600;        // s
const HIZ = 40;           // istek/s
const KULLANICI = 5000;
const ESKI_HATA = 0.004;  // eski surumun taban hata orani
const PLAN = [[0, 1], [300, 5], [600, 25], [1200, 50], [1800, 100]];
const PENCERE = 120;      // s, izleme penceresi
const EN_AZ_ORNEK = 200;  // pencerede gereken en az yeni surum istegi
const ESIK = 0.02;        // pencere hata orani esigi
const ADIM = 30;          // s, degerlendirme araligi

// Esik, ornekleme gurultusunu asacak kadar gecilmelidir: oranin alt siniri bakilir.
const altSinir = (hatali, n) => {
  const p = hatali / n;
  return p - 2 * Math.sqrt((p * (1 - p)) / n);
};

// Yapiskan atama: kullanici kimligi 0-99 arasi bir kovaya sabitlenir.
// Ayni kullanici yayin boyunca ayni tarafta kalir.
const kova = (kullanici) =>
  createHash("sha256").update("kullanici-" + kullanici).digest().readUInt32BE(0) % 100;

const KOVA = Array.from({ length: KULLANICI }, (_, i) => kova(i));
const payi = (t) => PLAN.filter(([an]) => an <= t).at(-1)[1];

function yayinla({ yeniHata, tekSeferde, elle }) {
  // Iki ayri akis: kullanici secimi ile hata cekilisi birbirini etkilemesin.
  const rKullanici = uretec(20_260_412);
  const rHata = uretec(78_345_921);
  const olay = [];   // { t, yeni, hata, kullanici }
  let geriAlma = null, sonrakiDegerlendirme = ADIM;
  const gunluk = [];
  let ilkPay = null;

  for (let n = 0; n < SURE * HIZ; n++) {
    const t = n / HIZ;
    if (elle !== undefined && geriAlma === null && t >= elle) {
      geriAlma = elle;
      gunluk.push([geriAlma, "elle saptandi ve geri alindi"]);
    }
    while (t >= sonrakiDegerlendirme) {
      if (geriAlma === null && elle === undefined) {
        const pencere = olay.filter((o) => o.yeni && o.t > sonrakiDegerlendirme - PENCERE);
        const hatali = pencere.filter((o) => o.hata).length;
        const n = pencere.length;
        if (n >= EN_AZ_ORNEK && altSinir(hatali, n) > ESIK) {
          geriAlma = sonrakiDegerlendirme;
          gunluk.push([geriAlma, "geri alindi, pencere hata orani %" +
            ((100 * hatali) / n).toFixed(1) + " (" + hatali + "/" + n +
            "), alt sinir %" + (100 * altSinir(hatali, n)).toFixed(1)]);
        }
      }
      sonrakiDegerlendirme += ADIM;
    }
    const pay = geriAlma !== null ? 0 : tekSeferde ? 100 : payi(t);
    if (pay !== ilkPay && geriAlma === null) {
      gunluk.push([t, "pay %" + pay]);
      ilkPay = pay;
    }
    const kullanici = Math.floor(rKullanici() * KULLANICI);
    const yeni = KOVA[kullanici] < pay;
    const hata = rHata() < (yeni ? yeniHata : ESKI_HATA);
    olay.push({ t, yeni, hata, kullanici });
  }

  const yeniOlay = olay.filter((o) => o.yeni);
  const hatali = yeniOlay.filter((o) => o.hata);
  return {
    geriAlma,
    yeniIstek: yeniOlay.length,
    hata: hatali.length,
    kullanici: new Set(hatali.map((o) => o.kullanici)).size,
    gunluk,
  };
}

console.log("yuk: " + (SURE * HIZ).toLocaleString("tr-TR") + " istek / " + SURE +
  " s, " + KULLANICI.toLocaleString("tr-TR") + " kullanici, yapiskan atama");
console.log("plan: " + PLAN.map(([t, p]) => t + " s %" + p).join(", "));
console.log("izleme: " + PENCERE + " s pencere, en az " + EN_AZ_ORNEK +
  " ornek, esik %" + (100 * ESIK).toFixed(1) + ", degerlendirme her " + ADIM + " s");
console.log("eski surumun taban hata orani: %" + (100 * ESKI_HATA).toFixed(1));

for (const [ad, ayar] of [
  ["saglikli yeni surum (hata orani %0.5)", { yeniHata: 0.005 }],
  ["kusurlu yeni surum (hata orani %6.0)", { yeniHata: 0.06 }],
]) {
  const s = yayinla(ayar);
  console.log("\nsenaryo: " + ad);
  for (const [t, ileti] of s.gunluk)
    console.log("  " + (t.toFixed(0) + " s").padStart(8) + "  " + ileti);
  console.log("  sonuc: " + (s.geriAlma === null ? "yayin tamamlandi" : "geri alindi") +
    ", yeni surume dusen " + s.yeniIstek.toLocaleString("tr-TR") + " istek, " +
    s.hata + " hata, " + s.kullanici + " ayri kullanici etkilendi");
}

console.log("\nayni kusur (%6.0), dort yayin ve saptama bicimi:");
console.log("  " + "bicim".padEnd(34) + "saptama".padStart(9) + "hata".padStart(8) +
  "etkilenen kullanici".padStart(21));
for (const [ad, ayar] of [
  ["asamali + otomatik izleme", { yeniHata: 0.06 }],
  ["tek seferde + otomatik izleme", { yeniHata: 0.06, tekSeferde: true }],
  ["asamali + elle saptama (600 s)", { yeniHata: 0.06, elle: 600 }],
  ["tek seferde + elle saptama (600 s)", { yeniHata: 0.06, tekSeferde: true, elle: 600 }],
]) {
  const s = yayinla(ayar);
  console.log("  " + ad.padEnd(34) + (s.geriAlma + " s").padStart(9) +
    String(s.hata).padStart(8) +
    (s.kullanici + " (%" + ((100 * s.kullanici) / KULLANICI).toFixed(1) + ")").padStart(21));
}
```

```
$ node asamali.mjs
yuk: 144.000 istek / 3600 s, 5.000 kullanici, yapiskan atama
plan: 0 s %1, 300 s %5, 600 s %25, 1200 s %50, 1800 s %100
izleme: 120 s pencere, en az 200 ornek, esik %2.0, degerlendirme her 30 s
eski surumun taban hata orani: %0.4

senaryo: saglikli yeni surum (hata orani %0.5)
       0 s  pay %1
     300 s  pay %5
     600 s  pay %25
    1200 s  pay %50
    1800 s  pay %100
  sonuc: yayin tamamlandi, yeni surume dusen 90.818 istek, 479 hata, 451 ayri kullanici etkilendi

senaryo: kusurlu yeni surum (hata orani %6.0)
       0 s  pay %1
     300 s  pay %5
     420 s  geri alindi, pencere hata orani %9.4 (20/212), alt sinir %5.4
  sonuc: geri alindi, yeni surume dusen 318 istek, 30 hata, 30 ayri kullanici etkilendi

ayni kusur (%6.0), dort yayin ve saptama bicimi:
  bicim                               saptama    hata  etkilenen kullanici
  asamali + otomatik izleme             420 s      30            30 (%0.6)
  tek seferde + otomatik izleme          30 s      67            67 (%1.3)
  asamali + elle saptama (600 s)        600 s      61            53 (%1.1)
  tek seferde + elle saptama (600 s)    600 s    1490         1275 (%25.5)
```

Çıktı dört sonuç verir.

**Küçük pay karar veremez.** Kusurlu sürüm yüzde bir payındayken saptanmadı. Neden aritmetiktir:
saniyede kırk isteğin yüzde biri saniyede 0,4 istektir; yüz yirmi saniyelik pencerede bu
elli isteğe bile ulaşmaz ve iki yüzlük en az örneklem koşulunu sağlamaz. Saptama, pay yüzde
beşe çıktıktan sonra, pencere dolar dolmaz gerçekleşti. **İlk adımın payı, risk iştahından
değil, karar için gereken örneklem büyüklüğünden hesaplanır.**

**Eşik gürültüyle birlikte tanımlanır.** Karar, hata oranının nokta değerine değil alt
sınırına bakar: kusurlu sürümde oran yüzde 9,4, alt sınırı yüzde 5,4'tür ve yüzde 2'lik
eşiği bu alt sınır aşar. Nokta değere bakan bir kural, sağlıklı sürümde de zaman zaman eşiği
aşan pencereler bulur — iki yüz örnekte beş hata görmek, gerçek oranı yüzde 0,5 olan bir
sürüm için sıra dışı değildir. Alt sınır koşulu bu yanlış geri almaları eler.

**Aşamalı yayın saptamayı yavaşlatır, etkiyi küçültür.** Tek seferde yayında kusur otuz
saniyede saptanır, aşamalıda dört yüz yirmi saniyede. Buna karşılık etkilenen kullanıcı
sayısı otomatik izlemede iki kat, elle saptamada yirmi dört kat farklıdır. Aşamalı yayının
sattığı şey hız değil, **yanılma payıdır**.

**Otomatik izleme olmadan aşamalı yayın da yetmez.** Son iki satır aynı on dakikalık saptama
gecikmesini iki yayın biçimine uygular: aşamalıda elli üç kullanıcı, tek seferde bin iki yüz
yetmiş beş kullanıcı — kullanıcıların dörtte biri — etkilenir. Pay planı ile izleme birlikte
çalışır; biri olmadan öteki eksik kalır.

## Geri Alınabilir Olmanın Koşulu

Geri alma, eski kaynağı yeniden derleyip yayınlamak değildir; o bir yeni yayındır ve derleme
süresi kadar sürer. Geri almanın kısa olması için iki koşul gerekir: **dönülecek dağıtımın
hâlâ yayında duruyor olması** ve **hangisinin canlı olduğunun tek bir işaretçiyle
belirlenmesi.**

İşaretçinin değişimi atomik olmalıdır: geçici bir dosyaya yazılır, sonra `rename(2)` ile
eskisinin üzerine taşınır. Bu çağrı ya tümüyle olur ya hiç olmaz; okuyan taraf ya eski ya
yeni değeri görür, yarısını görmez.

```js
// degistir.mjs — surum etiketli dagitimlar arasinda atomik gecis.
// Once gecici bir isaretci dosyasi yazilir, sonra rename(2) ile yerine tasinir.
import { writeFileSync, renameSync, readFileSync } from "node:fs";

const hedef = process.argv[2];
const t0 = process.hrtime.bigint();
writeFileSync("yayin/guncel.yeni", hedef + "\n");
renameSync("yayin/guncel.yeni", "yayin/guncel");
const t1 = process.hrtime.bigint();

console.log("gecis -> " + hedef + "   yazma + rename " +
  (Number(t1 - t0) / 1e6).toFixed(3) + " ms   isaretci " +
  readFileSync("yayin/guncel", "utf8").trim());
```

```js
// okuyucu.mjs — gecis surerken surekli okur; kirik ya da karisik okuma sayilir.
import { readFileSync } from "node:fs";

const sayim = new Map();
let kirik = 0, tutarsiz = 0;
for (let i = 0; i < 120_000; i++) {
  try {
    const surum = readFileSync("yayin/guncel", "utf8").trim();
    const govde = readFileSync("yayin/surum/" + surum + "/belge.txt", "utf8").trim();
    if (govde !== surum) tutarsiz += 1;
    sayim.set(surum, (sayim.get(surum) ?? 0) + 1);
  } catch {
    kirik += 1;
  }
}
console.log("okuma  : " + [...sayim].sort().map(([k, v]) => k + "=" + v).join("  ") +
  "   kirik=" + kirik + "   tutarsiz=" + tutarsiz);
```

```bash
#!/usr/bin/env bash
# Surum etiketli iki dagitim, atomik gecis ve geri alma.
rm -rf yayin
mkdir -p yayin/surum/2026.03 yayin/surum/2026.04
echo "2026.03" > yayin/surum/2026.03/belge.txt
echo "2026.04" > yayin/surum/2026.04/belge.txt
echo "2026.03" > yayin/guncel
echo "baslangic: $(cat yayin/guncel)"

node okuyucu.mjs &
okuyucu=$!
sleep 0.3
node degistir.mjs 2026.04
wait "$okuyucu"

node okuyucu.mjs &
okuyucu=$!
sleep 0.3
node degistir.mjs 2026.03
wait "$okuyucu"
```

```
baslangic: 2026.03
gecis -> 2026.04   yazma + rename 0.250 ms   isaretci 2026.04
okuma  : 2026.03=18088  2026.04=101912   kirik=0   tutarsiz=0
gecis -> 2026.03   yazma + rename 0.292 ms   isaretci 2026.03
okuma  : 2026.03=101743  2026.04=18257   kirik=0   tutarsiz=0
```

Okuma sayıları makineye ve yüke bağlıdır; her koşuda değişir. Değişmeyen iki alan sonuncu
ikisidir: hiçbir okuma kırılmaz ve hiçbir okuma işaretçiyle içeriğin uyuşmadığı bir sonuç
vermez. Yüz yirmi bin okumanın her biri ya eski ya yeni sürümü görür.

Geçişin kendisi bir milisaniyenin altındadır. Buradan geri alma süresinin nereden geldiği de
çıkar: süre, işaretçinin değişmesinden değil, **değişimin yayılmasından** gelir. Birinci
dersteki geçersiz kılma ne anında yayılır ne de kullanıcının kendi önbelleğine ulaşır;
belgeye kısa tazelik penceresi vermek bu yüzden geri alma süresinin üst sınırını da belirler.
Karmalı adlar sayesinde eski sürümün varlıkları hâlâ yayındadır, dolayısıyla geri alma yeni
bir varlık indirmesi gerektirmez.

## Geri Alınamayan Değişiklikler

Kodun geri alınması, kodun yaptığı işin geri alınması değildir. Üç sınıf değişiklik bu
kuralın dışında kalır ve aşamalı yayının koruması onlarda çalışmaz.

**Şema değişikliği.** Yeni sürüm bir alanı kaldırdıysa, geri alınan eski sürüm o alanı arar
ve bulamaz. Çözüm, değişikliği geri alınabilir adımlara bölmektir: **genişlet–daralt
(expand–contract)**. Önce şema, iki sürümün de çalışabileceği biçime genişletilir — yeni alan
eklenir, eskisi durur. Sonra kod yayınlanır ve kararlılığı beklenir. Ancak bundan sonra, ayrı
bir yayında, eski alan daraltılır. Her adımın kendi geri alınabilirliği vardır.

**Yeni biçimde yazılmış veri.** Yeni sürümün ürettiği kayıtları eski sürüm okuyamıyorsa geri
alma veriyi okunamaz bırakır. Kural, okuma desteğinin yazma değişikliğinden **bir yayın önce**
girmesidir: önce her iki biçimi de okuyabilen sürüm yayınlanır, sonra yeni biçimde yazan
sürüm.

**Dışa çıkmış yan etkiler.** Gönderilmiş bildirim, yapılmış ödeme çağrısı ya da üçüncü
tarafta oluşmuş kayıt geri alınamaz. Bunlar için tek yol ileri düzeltmedir: kusuru gideren
yeni bir sürümü yayınlamak. Aşamalı yayının burada sağladığı tek şey, yan etkinin ulaştığı
kullanıcı sayısını küçük tutmuş olmasıdır.

Üçü birlikte bir tasarım kuralı verir: **geri alınabilirlik bir özelliktir, kendiliğinden
gelmez.** Bir değişiklik geri alınamaz hâle geldiği anda, aşamalı yayın yalnız etkiyi
küçültür; düzeltmeyi kolaylaştırmaz.

## Özet

- Aşamalı yayın dört parçadan oluşur: pay planı, yapışkan atama, izleme penceresi ve eşik,
  geri alma. Kova sayısı payın çözünürlüğünü belirler.
- İlk adımın payı risk iştahından değil, karar için gereken örneklem büyüklüğünden
  hesaplanır; örnekte yüzde birlik pay pencereyi dolduramadığı için karar veremedi.
- Eşik, hata oranının nokta değerine değil alt sınırına uygulanır; nokta değere bakan bir
  kural sağlıklı sürümleri de geri alır.
- Aşamalı yayın saptamayı yavaşlatır ama etkiyi küçültür; saptama gecikmesi büyüdükçe fark
  büyür — örnekte elli üç kullanıcıya karşılık bin iki yüz yetmiş beş kullanıcı.
- Geri alma, korunan bir dağıtıma tek bir işaretçiyle dönmektir; `rename(2)` ile yapılan
  değişim bir milisaniyenin altındadır ve süreyi belirleyen şey değişimin yayılmasıdır.
- Şema değişiklikleri genişlet–daralt sırasıyla, yazma biçimi değişiklikleri okuma desteği
  bir yayın önde olacak biçimde yapılır; dışa çıkmış yan etkiler yalnız ileri düzeltmeyle
  karşılanır.

## Sonraki Adım

Buraya kadarki her dağıtım biçimi aynı varsayımı paylaşıyordu: çıktı bir adrese konur,
kullanıcı o adresi bir tarayıcıyla açar ve yeni sürüm bir sonraki istekte geçerli olur. Bu
varsayımın geçerli olmadığı hedefler vardır. Aynı derleme çıktısı bir masaüstü kabuğunun
içine, bir yerel gövdenin içindeki web görünümüne ya da bir uygulama mağazasından dağıtılan
pakete konabilir. O zaman adresler göreli olmak zorunda kalır, güncelleme kanalı bir inceleme
sürecinden geçer ve geri alma bu derste ölçülen milisaniyelerle değil günlerle anılır. Son
ders bu hedefleri ve getirdikleri kısıtları ele alır.
