İçeriğe geç
academia.sh

Ders 10 / 13

Erişilebilirlik ve Tutarlılık Gerilimi

CAP teoreminin doğru okunması: bölünme dayanıklılığının bir seçim olmadığının gösterilmesi, aynı ağ bölünmesi senaryosunda yanıt verme ve reddetme politikalarının yanıtlanan istek ile bayat yanıt sayılarının ölçülmesi, reddetmenin harcadığı kesinti bütçesinin ve yanıt vermenin ürettiği bayat yanıt sayısının hesaplanması, teoremin yaygın yanlış okunuşlarının ayırt edilmesi.

İçindekiler

Önceki ders hizmet erişilebilirliğini bir bütçeye çevirdi: yüzdeler yıllık kesinti süresine dönüştü, bütçenin nasıl harcandığı sayıldı. Açıkta kalan soru bütçenin neden harcandığıdır. Kesintinin bir kısmı arızadan gelir; bir kısmı ise bilinçli bir karardan. Bir kopya ötekinden habersiz kaldığında sistem iki şeyden birini seçmek zorundadır: yanıt vermeyi mi, doğru yanıt vermeyi mi.

Bu ders o seçimi adlandırır. Seçim keyfi bir tercih değildir; bir ağ üzerinde duran her sistemin zorunlu olarak verdiği ve genellikle farkında olmadan verdiği bir karardır. Kararın adı CAP teoremidir ve teorem, alanın en çok yanlış okunan cümlesidir.

Üç Harf ve Seçilmeyen Harf

Teorem üç özelliği adlandırır.

Tutarlılık (consistency): her okuma, en son onaylanmış yazmayı görür. Sistem, dışarıdan tek bir kopyası varmış gibi davranır. Hizmet erişilebilirliği (availability): çalışan her kopyaya gelen her istek bir yanıt alır — hata değil, yanıt. Bu terim arayüz erişilebilirliğiyle (accessibility) karıştırılmamalıdır; o, Erişilebilir Bileşen Kalıpları kursunun konusudur ve bu derste geçmez. Bölünme dayanıklılığı (partition tolerance): kopyalar arasındaki iletiler kaybolduğunda bile sistem çalışmayı sürdürür.

Üçüncü terimdeki ağ bölünmesi, İlişkisel Veritabanı Yönetimi kursunda geçen bölümleme ile aynı şey değildir: bölümleme veriyi bilerek parçalara ayırmaktır, ağ bölünmesi ise kopyaların birbirini görememesidir. Aynı kök, iki ayrı olgu.

Teoremin yanlış okunuşu buradan başlar, çünkü üç harf sanki üç seçenekmiş gibi sunulur. Değildir. Ağ bölünmesi bir tasarım seçeneği değil, bir olgudur: kablo kopar, anahtar yeniden başlar, bir bölge birkaç saniye ötekini duymaz. Bu olguyu “seçmemek” diye bir şey yoktur; olan tek şey, olgu gerçekleştiğinde sistemin ne yapacağıdır. Teoremin gerçek ifadesi şudur: ağ bölünmesi sürerken tutarlılık ile hizmet erişilebilirliği birlikte sağlanamaz. Bölünme yokken teorem sessizdir.

Çoğaltma ve çoğaltma gecikmesi İlişkisel Veritabanı Yönetimi kursunda kuruldu; burada yeniden anlatılmaz. Ölçülecek olan, o kopyaların bağı koptuğunda politikanın ne yaptığıdır.

İki Bölgeli Model

Gerçek bir küme kurmak yerine davranış süreç içi bir modelle gösterilir. Kopyalar birer nesne, bağın kesikliği açık bir parametredir. Aşağıdaki metinde geçen sayıların hepsi bu modelin koşumundan gelir; gerçek bir ağdan ölçülmüş gecikme değildir ve modelde saat kullanılmadığı için her koşumda aynı çıkar.

Gönderi takip servisinin iki bölgesi var: taşıyıcı durum olayları b34 bölgesine gelir, takip sorguları iki bölgeye de gelebilir.

// cap/model.mjs — iki bolgeli kopya modeli: kopyalar birer nesne, bag ve ileti acik parametre
export function kur() {
  const durum = { bag: true, ileti: 0 };
  const kopya = { b34: new Map(), b35: new Map() };
  const komsu = { b34: "b35", b35: "b34" };

  function yaz(bolge, anahtar, deger, tur) {
    kopya[bolge].set(anahtar, { deger, tur });
    if (durum.bag === false) return { yayilan: 0 };
    durum.ileti += 1;
    kopya[komsu[bolge]].set(anahtar, { deger, tur });
    return { yayilan: 1 };
  }

  // "yanit-ver": yerel kaydi dogrulamadan dondurur. "reddet": yanit ancak komsuyla
  // dogrulanabildiginde verilir; bag kesikse istek reddedilir.
  function oku(bolge, anahtar, politika) {
    const yerel = kopya[bolge].get(anahtar) ?? null;
    if (politika === "yanit-ver") return { yanit: yerel, red: false };
    if (durum.bag === false) return { yanit: null, red: true };
    durum.ileti += 1;
    return { yanit: yerel, red: false };
  }

  return { durum, kopya, yaz, oku };
}

İki politikanın kodda bir tek satırla ayrıldığına dikkat edilmelidir. reddet politikası, bağ kesikken hiçbir şey döndürmez; elinde bir değer olsa bile onun en son değer olduğunu doğrulayamadığı için yanıt vermez. Bütün ödünleşim bu satırın içindedir.

Aynı Senaryo, İki Politika

Sürücü betik on iki turluk bir senaryo koşturur. Her turda bir taşıyıcı durum olayı b34 bölgesine yazılır ve bir takip sorgusu gelir; sorgu sırayla iki bölgeye düşer. Bağ 3. turdan 8. tura kadar kesiktir.

// cap/olc.mjs — ayni senaryo iki okuma politikasiyla: yanitlanan, reddedilen ve bayat sayilari
import { kur } from "./model.mjs";

const TAKIP = "TR-9042";
const OLAY = ["kabul", "cikis", "aktarma-34", "aktarma-41", "hat-35", "dagitim",
  "teslim-denemesi", "adres-dogrulama", "yeniden-dagitim", "teslim", "imza", "kapali"];
const BOLUNME = [3, 8]; // bag bu turlar arasinda kesik (iki uc dahil)

function kosum(politika) {
  const k = kur();
  const s = { istek: 0, yanit: 0, red: 0, bayat: 0, iz: [] };
  for (let tur = 1; tur <= OLAY.length; tur++) {
    k.durum.bag = tur < BOLUNME[0] || tur > BOLUNME[1];
    k.yaz("b34", TAKIP, OLAY[tur - 1], tur); // tasiyici olayi her zaman b34'e gelir
    const gercek = k.kopya.b34.get(TAKIP);
    const bolge = tur % 2 === 1 ? "b35" : "b34"; // takip sorgusu iki bolgeye sirayla gelir
    const r = k.oku(bolge, TAKIP, politika);
    s.istek += 1;
    if (r.red) { s.red += 1; s.iz.push(`${bolge}:red`); continue; }
    s.yanit += 1;
    const bayat = r.yanit.tur < gercek.tur;
    if (bayat) s.bayat += 1;
    s.iz.push(`${bolge}:${r.yanit.deger}${bayat ? "~" : ""}`);
  }
  return { ...s, ileti: k.durum.ileti };
}

const pencere = (a) => a.slice(BOLUNME[0] - 1, BOLUNME[1]);
console.log(`senaryo: ${OLAY.length} tur, bag ${BOLUNME[0]}-${BOLUNME[1]}. turlarda kesik`);
console.log("politika    | yanit | red | bayat | ileti | bolunme penceresi");
console.log("------------|-------|-----|-------|-------|------------------");
const cikti = {};
for (const p of ["yanit-ver", "reddet"]) {
  const s = kosum(p);
  cikti[p] = s;
  console.log(`${p.padEnd(11)} | ${String(`${s.yanit}/${s.istek}`).padStart(5)} | ` +
    `${String(s.red).padStart(3)} | ${String(s.bayat).padStart(5)} | ` +
    `${String(s.ileti).padStart(5)} | ${pencere(s.iz).join(" ")}`);
}
const bp = BOLUNME[1] - BOLUNME[0] + 1;
const say = (p, k) => pencere(cikti[p].iz).filter(k).length;
console.log(`bolunme penceresinde (${bp} tur): yanit-ver bayat = ` +
  `${say("yanit-ver", (x) => x.endsWith("~"))}/${bp}, reddet red = ` +
  `${say("reddet", (x) => x.endsWith("red"))}/${bp}`);
console.log(`bolunme disinda (${OLAY.length - bp} tur): iki politika da ayni yaniti verdi`);
node cap/olc.mjs
senaryo: 12 tur, bag 3-8. turlarda kesik
politika    | yanit | red | bayat | ileti | bolunme penceresi
------------|-------|-----|-------|-------|------------------
yanit-ver   | 12/12 |   0 |     3 |     6 | b35:cikis~ b34:aktarma-41 b35:cikis~ b34:dagitim b35:cikis~ b34:adres-dogrulama
reddet      |  6/12 |   6 |     0 |    12 | b35:red b34:red b35:red b34:red b35:red b34:red
bolunme penceresinde (6 tur): yanit-ver bayat = 3/6, reddet red = 6/6
bolunme disinda (6 tur): iki politika da ayni yaniti verdi

Sayıların Okunması

Son satır teoremin sessiz kaldığı yeri gösteriyor: bölünme dışındaki altı turda iki politika birbirinden ayırt edilemez. Ödünleşim yalnız bölünme penceresinde vardır ve orada keskindir.

yanit-ver politikası 12/12 istek yanıtladı, hiçbirini reddetmedi; karşılığında 3 bayat yanıt üretti. İzdeki ~ işareti bayat yanıtı gösteriyor: b35 bölgesi bölünme boyunca cikis durumunu döndürdü, oysa gönderi o sırada dagitim ve adres-dogrulama aşamalarına geçmişti. Takip sorgusu yanıt aldı ama yanıt gerçeği söylemiyordu.

reddet politikası 6/12 istek yanıtladı; bölünme penceresindeki altı isteğin altısını da geri çevirdi ve tek bir bayat yanıt üretmedi. Burada gözden kaçan bir ayrıntı var: politika b34 bölgesinden gelen istekleri de reddetti — oysa b34 her turda en yeni değeri elinde tutuyordu. Sebep, kopyanın elindeki değerin en yeni olduğunu bilememesidir. Bir kopya, komşusunun kendisinden habersiz bir yazma almış olup olmadığını komşuya sormadan öğrenemez. Tutarlılık güvencesi doğru değeri döndürmeyi değil, döndürülen değerin doğruluğunu doğrulayabilmeyi gerektirir; doğrulama kanalı kesildiğinde güvence de kesilir.

İleti sütunu üçüncü bir maliyeti sayıyor. yanit-ver politikası 6 ileti harcadı: yalnız bağ açıkken yayılan altı yazma. reddet politikası 12 ileti harcadı — aynı altı yayılma, artı yanıtlanan her okuma için bir doğrulama iletisi. Bu sayı bölünmeyle ilgili değildir; bölünme olmasa da orada olurdu. Sonraki dersin konusu tam olarak bu farktır.

Bütçenin Nereye Gittiği

Modelin ürettiği oranlar bir yıllık bütçeye çevrilebilir. Girdiler varsayımdır: ağ bölünmesinin yılda kaç kez ve ne kadar sürdüğü, bölünme süresince saniyede kaç takip sorgusu geldiği. İkinci koşum varsayımın duyarlılığını gösterir: bölünme süresi iki katına çıkarsa sonuçlar ne olur.

// cap/butce.mjs — modelin oranlarini yillik butceye cevirir; girdiler varsayimdir
const YIL = 365 * 24 * 3600;
const VARSAYIM = { bolunme: 12, sure: 40, sorgu: 30 }; // yilda kez, saniye, saniyedeki takip sorgusu
const MODEL = { redOrani: 6 / 6, bayatOrani: 3 / 6 };  // olcumden gelen oranlar

const yuzde = (s) => (100 * (1 - s / YIL)).toFixed(4);
for (const kat of [1, 2]) {
  const s = VARSAYIM.bolunme * VARSAYIM.sure * kat;
  const istek = s * VARSAYIM.sorgu;
  console.log(`bolunme ${s} sn/yil (${VARSAYIM.bolunme} kez x ${VARSAYIM.sure * kat} sn)` +
    ` -> pencerede ${istek} takip sorgusu`);
  console.log(`  reddet   : red = ${Math.round(istek * MODEL.redOrani)} istek,` +
    ` hizmet erisilebilirligi = %${yuzde(s * MODEL.redOrani)}, bayat = 0`);
  console.log(`  yanit-ver: red = 0 istek,` +
    ` hizmet erisilebilirligi = %${yuzde(0)}, bayat = ` +
    `${Math.round(istek * MODEL.bayatOrani)} yanit`);
}
bolunme 480 sn/yil (12 kez x 40 sn) -> pencerede 14400 takip sorgusu
  reddet   : red = 14400 istek, hizmet erisilebilirligi = %99.9985, bayat = 0
  yanit-ver: red = 0 istek, hizmet erisilebilirligi = %100.0000, bayat = 7200 yanit
bolunme 960 sn/yil (12 kez x 80 sn) -> pencerede 28800 takip sorgusu
  reddet   : red = 28800 istek, hizmet erisilebilirligi = %99.9970, bayat = 0
  yanit-ver: red = 0 istek, hizmet erisilebilirligi = %100.0000, bayat = 14400 yanit

İki politika aynı olguyu iki ayrı para birimiyle ödüyor. reddet politikası bütçeden 480 saniye harcadı ve hizmet erişilebilirliğini yüzde 99,9985’e indirdi; bunun karşılığında yanlış bilgi üretmedi. yanit-ver politikası bütçeden hiç harcamadı ama 7.200 takip sorgusuna eski durumu söyledi. Varsayım iki katına çıktığında iki sayı da doğrusal büyüdü: yüzde 99,9970 ve 14.400.

Buradaki asıl sonuç, iki maliyetin karşılaştırılabilir olmadığıdır. Kesinti biter; yanlış söylenmiş bir teslim durumu, ona bakıp karar veren birinin işine karışmıştır. Bu yüzden karar sayıların büyüklüğüne değil, işin hangi hatayı taşıyabildiğine bakılarak verilir. Takip sorgusunun birkaç saniye eski bir durum göstermesi katlanılabilir; gün sonu ücretlendirmesinin eski bir duruma göre fatura kesmesi katlanılamaz. Aynı sistemde iki akış, iki ayrı politika.

Teoremin Yanlış Okunuşları

Dört yanlış okuma yaygındır ve dördü de ölçümle çürütülür.

“Üçten ikisini seç.” Teorem bir menü sunmaz. Bölünme dayanıklılığı seçilen bir özellik değil, ağ üzerinde durmanın sonucudur. Seçim iki harf arasındadır ve yalnız bölünme süresince geçerlidir.

“Bizim sistemimiz CA.” Ağ üzerinde dağılmış bir sistem için bu bir sınıf değil, bölünme düşünülmemiş bir tasarımdır. Bölünme geldiğinde sistem gerçekte bir seçim yapar; kod bunu yazmadıysa seçimi kütüphanenin öntanımlı davranışı yapar. Ölçümdeki tek satırlık fark, bu öntanımlı davranışın nerede saklandığını gösteriyor.

“Seçim sistem geneli içindir.” Ölçümdeki iki koşum aynı modeli kullandı; ayrılan tek şey okuma politikasıydı. Politika istek başına verilebilir. Takip sorgusu yanit-ver, ücretlendirme okuması reddet politikasıyla çalışabilir.

“Tutarlılık seçilirse sistem yavaşlar, hepsi bu.” İleti sütunu bunun yarısının doğru olduğunu gösteriyor: doğrulama gerçekten ileti harcar. Ama bölünme süresince harcanan şey süre değil, yanıtın kendisidir — 6 istek reddedildi. İkisi ayrı maliyetlerdir ve ayrı ele alınır.

Özet

  • CAP teoremi üç seçenek sunmaz: ağ bölünmesi bir olgudur, seçim yalnız bölünme sürerken tutarlılık ile hizmet erişilebilirliği arasındadır.
  • Aynı on iki turluk senaryoda yanit-ver politikası 12/12 istek yanıtladı ve 3 bayat yanıt üretti; reddet politikası 6/12 yanıtladı, 6 istek reddetti ve 0 bayat yanıt üretti.
  • Bölünme dışındaki altı turda iki politika ayırt edilemedi; ödünleşim yalnız pencerede vardır.
  • reddet politikası en yeni değeri elinde tutan bölgede de reddetti: tutarlılık güvencesi doğru değeri döndürmeyi değil, doğruluğunu doğrulayabilmeyi gerektirir.
  • Yılda 12 kez 40 saniye bölünme varsayımıyla reddet politikası 14.400 isteği reddederek hizmet erişilebilirliğini yüzde 99,9985’e indirdi; yanit-ver politikası bütçeden harcamadan 7.200 bayat yanıt üretti. Varsayım iki katına çıkınca iki sayı da iki katına çıktı.
  • İki maliyet aynı birimde değildir; karar, akışın hangi hatayı taşıyabildiğine göre istek başına verilir.

Sonraki Adım

Ölçümde bir sütun açıklanmadan kaldı. reddet politikası 12 ileti, yanit-ver politikası 6 ileti harcadı ve bu farkın bölünmeyle ilgisi yoktu: doğrulama iletileri bağ açıkken de gönderildi. Yani seçim, ağ hiç bölünmese bile bitmiyor. Bölünme yokken de her yazmanın iki kopyaya da onaylatılması ile bir kopyaya yazıp ötekine yayılmasının beklenmesi arasında bir karar vardır; bu kararın bedeli reddedilen istek değil, beklenen tur sayısıdır. Sonraki ders CAP teoreminin sustuğu bu bölgeyi adlandırır ve orada ölçülecek üç şeyi sayar: onay bekleme adımı, okumada görülen eskilik penceresi ve yazma başına ileti sayısı.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat