İçeriğe geç
academia.sh

Ders 18 / 18

Nihai Tutarlılığın Kullanıcıya Yansıması

Gerilik penceresinin arayüzdeki karşılığı: dört istemci stratejisinin aynı olay dizisinde ürettiği bayat görüş, bekleme turu, çakışma bildirimi ve sessiz kayıp sayıları, iyimser görüntünün gerilik görüş sayısını aşınca sessiz kayıp üretmesi ve kendi yazdığını okumanın yazma deposunu 97,22'den 137,22 işlem/s'ye çıkarması.

İçindekiler

Önceki üç ders gerilik penceresini bir kaynak ölçüsü olarak ele aldı: kaç olay, kaç saat, kaç gigabayt. Pencerenin içinde duran şey ise bir sayı değil, bir kullanıcıdır. Bir alıcı adresini düzeltir ve sayfayı yeniden yükleyip eski adresi görür; iki taraf aynı gönderiyi aynı anda değiştirir ve biri ötekinin değişikliğinin kaybolduğunu hiç öğrenmez.

Katalogdaki başlık “nihai tutarlılık” der; bu kursta kullanılan terim sonunda tutarlılıktır (eventual consistency) ve Sistem Tasarımına Giriş kursunda tanımlandı: yazma durduğunda kopyaların aynı değere yakınsayacağı güvencesi, yakınsamaya kadar okumaların ne göreceği hakkında sessizlik. Monotonik okuma ve kendi yazdığını okuma güvenceleri de orada tanımlıydı; burada tekrarlanmaz. Bu dersin sorusu, o sessizliğin arayüzde nasıl doldurulacağı ve her doldurma biçiminin K01’in hesabına ne eklediğidir.

Ölçünün Kaynağı

Arayüz ölçümü için tarayıcı çıktısı üretilmez; kullanıcının gördüğü değer bir durum makinesinden çıkarılır. Kullanıcı bir turda yazar, sonraki turlarda birkaç kez bakar; izdüşüm yazmaları sabit bir gerilikle uygular ve tur soyut bir adımdır, saniye değildir. Oturumların dörtte birinde aynı gönderiye başka bir taraf da yazar; kütükte sonra gelen kazanır.

Sonuncusu asıl ölçüdür. Sessiz kayıp, kullanıcının yazmasının başka bir yazmayla geçersizleşmesi ve kullanıcının bunu hiç öğrenmemesidir. Bir arayüz kararının kalitesi, bayat görüş sayısından çok bu sayıyla ölçülür.

// arayuz/oturum.mjs — kullanici oturumunun surec ici modeli. Tarayici, ekran ve sure yoktur;
// tur soyut bir adimdir ve kullanicinin gordugu deger izdusumun gerilik penceresinden cikar.
// Dort strateji ayni olay dizisini ayni gerilikle gorur, yalniz istemcinin davranisi degisir.
export function kosum({ strateji, gerilik, gorus, oturum }) {
  const yazma = [];
  for (let i = 0; i < oturum; i += 1) {
    const t = i + 1, id = `G${i + 1}`;
    yazma.push({ tur: t, id, deger: `k${i}`, yazan: "kullanici" });
    if (i % 4 === 0) yazma.push({ tur: t, id, deger: `r${i}`, yazan: "rakip" });
  }
  const uygulanmis = (t) => {            // izdusum: tur <= t - gerilik olan yazmalar islenmis
    const m = new Map();
    for (const y of yazma) if (y.tur <= t - gerilik) m.set(y.id, y);
    return m;
  };
  const kutukten = (id) => {             // yazma tarafindan okuma: butun yazmalar gorunur
    let son = null;
    for (const y of yazma) if (y.id === id) son = y;
    return son;
  };

  const s = { bayat: 0, bekleme: 0, istek: 0, yazmaTarafi: 0, bildirim: 0, sessizKayip: 0, dogruTur: 0 };
  for (let i = 0; i < oturum; i += 1) {
    const t0 = i + 1, id = `G${i + 1}`;
    const kazanan = kutukten(id);
    const yenildi = kazanan.yazan !== "kullanici";
    let dogruTur = null, bildirdi = false;

    if (strateji === "ham") {
      for (let k = 1; k <= gorus; k += 1) {
        s.istek += 1;
        const m = uygulanmis(t0 + k);
        if (m.has(id) === false) { s.bayat += 1; continue; }
        if (dogruTur === null) dogruTur = k;
      }
    } else if (strateji === "iyimser") {
      for (let k = 1; k <= gorus; k += 1) {
        s.istek += 1;                    // istemci kendi yazdigini ekranda tutar: bayat yok
        const m = uygulanmis(t0 + k);
        if (m.has(id) === false) continue;
        if (dogruTur === null) dogruTur = k;
        if (yenildi && bildirdi === false) { s.bildirim += 1; bildirdi = true; }
      }
    } else if (strateji === "bekleme") {
      for (let k = 1; k <= gorus + gerilik; k += 1) {
        s.istek += 1;                    // yazma bir surum belirteci dondurur, istemci yoklar
        const m = uygulanmis(t0 + k);
        if (m.has(id) === false) { s.bekleme += 1; continue; }
        dogruTur = k;
        if (yenildi) { s.bildirim += 1; bildirdi = true; }
        break;
      }
      s.istek += gorus - 1;              // bekleme bittikten sonraki normal gorusler
    } else {
      for (let k = 1; k <= gorus; k += 1) {
        s.istek += 1;
        if (k <= gerilik) {              // kendi yazma penceresinde okuma yazma tarafindan
          s.yazmaTarafi += 1;
          if (dogruTur === null) dogruTur = k;
          if (yenildi && bildirdi === false) { s.bildirim += 1; bildirdi = true; }
          continue;
        }
        const m = uygulanmis(t0 + k);
        if (m.has(id) && dogruTur === null) dogruTur = k;
      }
    }
    if (yenildi && bildirdi === false) s.sessizKayip += 1;
    s.dogruTur += dogruTur ?? gorus;
  }
  return { ...s, dogruTur: s.dogruTur / oturum };
}

Dört strateji ayrılıyor. Ham okuma izdüşümü olduğu gibi gösterir. İyimser görüntü istemcinin kendi yazdığını ekranda tutmasıdır; kaynağı sunucu değil istemcidir. Belirteçle bekleme yazmanın döndürdüğü sürüm belirtecine ulaşana kadar yoklar. Kendi yazdığını okuma gerilik penceresi boyunca okumayı izdüşüm yerine yazma tarafına yollar; oradaki yanıt kütüğün katlanmasıyla üretilir ve önceki derste ölçüldüğü gibi 8 işlem eder.

// arayuz/olc.mjs — dort strateji ayni gerilikte karsilastirilir, sonra gerilik taranir ve
// sonuclar K01'in okuma hizina uygulanir. Butun sayilar belirlenimlidir; sinif olarak hesaptir.
import { kosum } from "./oturum.mjs";

const OTURUM = 200, GORUS = 4, GERILIK = 3;
const STRATEJI = ["ham", "iyimser", "bekleme", "kendi-yazdigini"];
const b = (x, n = 2) => x.toFixed(n);
const p = (x, n) => String(x).padStart(n);

console.log(`${OTURUM} oturum, oturum basina ${GORUS} gorus, izdusum geriligi ${GERILIK} tur` +
  `, oturumlarin dortte birinde ayni gonderiye rakip yazma var`);
console.log(`\n${"strateji".padEnd(16)}${"bayat gorus".padStart(13)}${"bekleme turu".padStart(14)}` +
  `${"okuma istegi".padStart(14)}${"yazma tarafi".padStart(14)}${"bildirim".padStart(10)}` +
  `${"sessiz kayip".padStart(14)}${"dogru tur".padStart(11)}`);
const taban = {};
for (const st of STRATEJI) {
  const r = kosum({ strateji: st, gerilik: GERILIK, gorus: GORUS, oturum: OTURUM });
  taban[st] = r;
  console.log(`${st.padEnd(16)}${p(r.bayat, 13)}${p(r.bekleme, 14)}${p(r.istek, 14)}` +
    `${p(r.yazmaTarafi, 14)}${p(r.bildirim, 10)}${p(r.sessizKayip, 14)}${b(r.dogruTur).padStart(11)}`);
}

console.log(`\n${"gerilik".padStart(8)}${STRATEJI.map((s) => `${s} bayat`.padStart(24)).join("")}`);
for (const g of [1, 3, 6, 12]) {
  const satir = STRATEJI.map((s) => {
    const r = kosum({ strateji: s, gerilik: g, gorus: GORUS, oturum: OTURUM });
    return `${r.bayat}/${r.sessizKayip}`.padStart(24);
  });
  console.log(`${p(g, 8)}${satir.join("")}`);
}
console.log("(sutunlar: bayat gorus / sessiz kayip)");

// K01'e donus
const OKUMA = 41.67, YAZMA_ISLEM = 97.22, OY5 = 0.04, KUTUK_KATLAMA = 8;
console.log(`\nK01: onbellek arkasi okuma ${OKUMA}/s; yazma deposu ${YAZMA_ISLEM} islem/s (onceki ders)`);
console.log(`OY5 = ${OY5} -> yazma penceresindeki okuma ${b(OKUMA * OY5)}/s\n`);
console.log(`${"strateji".padEnd(16)}${"okuma/oturum".padStart(14)}${"ek okuma/s".padStart(12)}` +
  `${"okuma deposu/s".padStart(16)}${"depoya istek/s".padStart(16)}${"yazma deposu islem/s".padStart(22)}`);
for (const st of STRATEJI) {
  const r = taban[st];
  const oturumBasina = r.istek / OTURUM;
  const pencere = OKUMA * OY5;
  const ek = pencere * (oturumBasina - GORUS);
  const kayan = pencere * (r.yazmaTarafi / OTURUM);
  const okumaDeposu = OKUMA + ek - kayan;
  console.log(`${st.padEnd(16)}${b(oturumBasina).padStart(14)}${b(ek).padStart(12)}` +
    `${b(okumaDeposu).padStart(16)}${b(okumaDeposu + kayan + 97.22).padStart(16)}` +
    `${b(YAZMA_ISLEM + kayan * KUTUK_KATLAMA).padStart(22)}`);
}
for (const oy of [OY5, 2 * OY5]) {
  const pencere = OKUMA * oy;
  const bek = pencere * (taban.bekleme.istek / OTURUM - GORUS);
  const kdy = pencere * (taban["kendi-yazdigini"].yazmaTarafi / OTURUM) * KUTUK_KATLAMA;
  console.log(`OY5 = ${b(oy)} -> bekleme ek okuma ${b(bek)}/s (${b((100 * bek) / OKUMA)}%)` +
    `, kendi-yazdigini yazma deposuna ${b(kdy)} islem/s (${b((100 * kdy) / YAZMA_ISLEM)}%)`);
}
200 oturum, oturum basina 4 gorus, izdusum geriligi 3 tur, oturumlarin dortte birinde ayni gonderiye rakip yazma var

strateji          bayat gorus  bekleme turu  okuma istegi  yazma tarafi  bildirim  sessiz kayip  dogru tur
ham                       400             0           800             0         0            50       3.00
iyimser                     0             0           800             0        50             0       3.00
bekleme                     0           400          1200             0        50             0       3.00
kendi-yazdigini             0             0           800           600        50             0       1.00

 gerilik               ham bayat           iyimser bayat           bekleme bayat   kendi-yazdigini bayat
       1                    0/50                     0/0                     0/0                     0/0
       3                  400/50                     0/0                     0/0                     0/0
       6                  800/50                    0/50                     0/0                     0/0
      12                  800/50                    0/50                     0/0                     0/0
(sutunlar: bayat gorus / sessiz kayip)

K01: onbellek arkasi okuma 41.67/s; yazma deposu 97.22 islem/s (onceki ders)
OY5 = 0.04 -> yazma penceresindeki okuma 1.67/s

strateji          okuma/oturum  ek okuma/s  okuma deposu/s  depoya istek/s  yazma deposu islem/s
ham                       4.00        0.00           41.67          138.89                 97.22
iyimser                   4.00        0.00           41.67          138.89                 97.22
bekleme                   6.00        3.33           45.00          142.22                 97.22
kendi-yazdigini           4.00        0.00           36.67          138.89                137.22
OY5 = 0.04 -> bekleme ek okuma 3.33/s (8.00%), kendi-yazdigini yazma deposuna 40.00 islem/s (41.15%)
OY5 = 0.08 -> bekleme ek okuma 6.67/s (16.00%), kendi-yazdigini yazma deposuna 80.01 islem/s (82.29%)

Sayıların Okunması

İlk tablo dört stratejiyi aynı pencerede karşılaştırıyor. Ham okuma 800 görüşün 400’ünde kullanıcının yazmasından önceki değeri gösteriyor ve 50 oturumda yazma sessizce kayboluyor — kullanıcı kendi değerini hiç görmediği için sistemin ona söyleyeceği bir şey de yok. Bu, en ucuz ve en zararlı seçenektir.

İyimser görüntü bayat görüşü sıfıra indiriyor ve 50 çakışmanın hepsini bildiriyor; ek istek yok. Yalnız iyimser görüntünün doğruluğu bir koşula bağlı ve ikinci tablo o koşulu gösteriyor: gerilik 6 ve 12 turken sessiz kayıp yeniden 50 oluyor. Sebep aritmetiktir — kullanıcı dört kez bakıyor, izdüşüm altı tur sonra tazeleniyor, karşılaştırma hiç yapılmıyor. İyimser görüntü, gerilik penceresi kullanıcının bakış sayısını aştığında sessizleşir.

Belirteçle bekleme bayat görüşü ve sessiz kaybı sıfırda tutuyor ama 400 bekleme turu ve 1200 okuma isteği ödüyor: oturum başına 4 yerine 6 istek, yani yüzde 50 fazla. Kendi yazdığını okuma da ikisini sıfırda tutuyor ve doğru değeri ilk turda gösteriyor (öbür üçünde 3,00). Bedeli 600 okumanın yazma tarafına düşmesidir.

Gerilik taraması stratejileri sıralıyor. Gerilik 1 iken dördü de temiz; büyüdükçe önce ham okuma, sonra iyimser görüntü bozuluyor. Bekleme ile kendi yazdığını okuma her gerilikte temiz kalıyor, çünkü ikisi de pencereyi beklemek ya da atlamak yoluyla kapatıyor.

Hesaba Dönüş

Sayıları K01’e bağlamak için bir oran gerekiyor.

OY5 — takip okumalarının kendi yazmasından sonraki gerilik penceresinde yapılan oranı: 0,04. Bu konunun varsayımıdır, K01’in tablosuna eklenmez. Gerekçesi, durum olaylarının çoğunun taşıyıcı entegrasyonundan gelmesi ve arayüzden yazan tarafın yalnız adresini düzelten alıcı ile gönderisini güncelleyen satıcı olmasıdır. Duyarlılığı 0,08 ile veriliyor.

Son tablo dört stratejinin K01 satırlarına ne yaptığını gösteriyor. Ham okuma ve iyimser görüntü hiçbir sayıyı oynatmıyor: depoya ulasan istek/s 138,89’da kalıyor. Belirteçle bekleme yoklama istekleriyle onu 142,22’ye çıkarıyor; artış okuma deposunda ve yüzde 8. Kendi yazdığını okuma istek sayısını değiştirmiyor ama 5,00 istek/s’yi okuma deposundan yazma deposuna kaydırıyor; her biri 8 işlem ettiği için yazma deposunun yükü 97,22’den 137,22 işlem/s’ye, yani yüzde 41,15 artıyor. OY5 iki katına çıkarsa artış yüzde 82,29 olur.

Karşılaştırma tek cümlede toplanıyor: bekleme bedeli okuma deposuna, kendi yazdığını okuma bedeli yazma deposuna yazar. Üç ders boyunca yazma deposu korunmaya çalışılmıştı; en güçlü oturum güvencesi tam oraya yükleniyor. Bu yüzden karar gönderi bazında verilir: adresini düzelten kullanıcı kendi yazdığını okur, gönderiyi yalnız izleyen kullanıcı izdüşümden okur.

Özet

  • Sonunda tutarlılığın arayüzdeki ölçüsü bayat görüş değil sessiz kayıptır: kullanıcının yazmasının geçersizleşmesi ve bunu hiç öğrenmemesi.
  • Ham okuma 800 görüşün 400’ünde eski değer gösterdi ve 50 sessiz kayıp üretti; iyimser görüntü ikisini de sıfırladı ve 50 çakışmayı bildirdi.
  • İyimser görüntünün koşulu geriliğin bakış sayısından küçük olmasıdır: gerilik 6 ve 12 turken sessiz kayıp yeniden 50 oldu.
  • Belirteçle bekleme 400 bekleme turu ve oturum başına 4 yerine 6 istek ödüyor; kendi yazdığını okuma doğru değeri ilk turda gösteriyor (öbürlerinde 3,00) ve 600 okumayı yazma tarafına yolluyor.
  • K01’e dönüş: bekleme depoya ulasan istek/s satırını 138,89’dan 142,22’ye çıkarıyor; kendi yazdığını okuma istek sayısını değiştirmeden yazma deposunu 97,22’den 137,22 işlem/s’ye (yüzde 41,15) çıkarıyor. OY5 = 0,08’de artış yüzde 82,29 olur.

Kurs Kapanışı

Kurs tek bir depo varsayımını kaldırdı ve üç konu boyunca aynı üç soruyu sordu: karar nedir, K01’in hesabındaki hangi sayıyı oynatıyor, karşılığında hangi sayı büyüyor.

Ders Kararı Oynadığı sayı Karşılığında büyüyen
Veri Deposu Seçimi örüntüye göre depo ayrımı okuma tarafında işlenen kayıt 291,69 → 41,67 depo işlem hızı 138,89 → 236,11; oran 2,33 → 4,67
Çoğaltma okumanın kopyalara bölünmesi kopya başına okuma 41,67 → 8,33 saklanan veri GB 718,24 → 4309,44 (beş kopya); oran 14,00
Federasyon verinin işleve göre bölünmesi tarama hızı 833,33 kayıt/s → 0 (çevrimiçi depo) 976 → 1232 MB/gün, 712,48 → 899,36 GB
Parçalama anahtarın erişim örüntüsünden seçilmesi düğüm başına 78,11 GB ve 17,36 işlem/s süzemeyen örüntüde dokunuş 138,89 → 1111,12/s
Bölümleme Stratejileri anahtarı bölüme çeviren kural sıcak nokta oranı 8,00 → 1,00; taranan kayıt 12.000.000 → 1.500.137 depoya ulasan istek/s 138,89 → 277,78
Denormalizasyon okuma yolunun kısaltılması ikincil çekilen kayıt 9000 → 40 oran 2,33 → 8,71; 712,48 → 733,27 GB
Somutlaştırılmış Görünümler sonucun önceden hesaplanması tarama 833,33 → 8,33 kayıt/s, 10.800 → 3,28 MB oran 2,33 → 7,13 (dakikalık tazelemede)
Depo Türleri erişim biçimine göre tür seçimi çizgede üç adımlık dolaşma 3 → 1 istek anahtar–değer taramasında 1 → 3001 istek; 138,89 → 236,11/s
Önbellek Yerleşimi isabetin hangi katmanda karşılandığı uygulamaya varan okuma 416,67 → 60,51/s ıska başına 9 dokunuş, ortalama 2,531 dokunuş/istek
Yanında Okuma getirme sorumluluğunun uygulamada olması isabet 0,90 varsayımı 0,7287 çıkıyor depoya ulasan istek/s 138,89 → 210,26; oran 2,33 → 0,86
İçinden ve Arkadan Yazma yazma yolunun önbelleğe bağlanması isabet 0,7285 → 0,9170; istek 229,01 → 151,04/s kayıp penceresi 200 olay, 44.000 bayt
Önden Tazeleme süre dolmadan yenileme bekleyen istek 57.815 → 34.270 tazeleme 165.730, boşa 27.825; istek 151,04 → 217,67/s
Önbelleksiz Tasarım önbelleğin gerekmediği yer depoya ulasan istek/s 513,89 → 139,90 tarama örüntüsünde istek başına 2,00 boşa dokunuş
Tutarlılık ve Bayat Veri bayatlık penceresinin kabul edilmesi pencere 5 → 60 s; istek 217,67 → 140,23/s bayat oranı 0,001195, en büyük gerilik 1 olay
Komut ve Sorgu Ayrımı okuma ve yazma yollarının ayrılması sorgu dokunuşu 8 → 1; düğüm 17 → 7 (okuma ×4) komut 3 → 5 dokunuş; yazma ×4’te düğüm 15 → 21
Olay Kaynaklı Tasarım değişimin kaynak kayıt olması istek başına işlem 4,50 → 3,10 976 → 1168 MB/gün, 712,48 → 852,64 GB; tarama 10,80 → 35,04 GB
Okuma Modellerinin Türetilmesi izdüşümün kütükten türetilmesi yeniden üretim 2.336.000.000 → 16.000.000 olay 1168 → 1360 MB/gün, 852,64 → 992,80 GB
Nihai Tutarlılığın Yansıması pencerenin arayüzde kapatılması sessiz kayıp 50 → 0; doğru değeri görme 3,00 → 1,00 tur yazma deposu 97,22 → 137,22 işlem/s

Tablonun kuralı adıyla anılmalı. Bu kursta bir veri kararı ancak bir erişim örüntüsüyle birlikte savunulabilir. Aynı karar bir örüntüde kazanç, başka bir örüntüde kayıptır: parçalama anahtarı süzen örüntüde ucuz, süzmeyen örüntüde sekiz kat pahalı; önbellek tekrarlı okumada kazanç, taramada ek dokunuş; olay kütüğü yazmada kazanç, dönemsel taramada 3,24 kat kayıp; okuma–yazma ayrımı okuma büyürken kazanç, yazma büyürken kayıp. Örüntü söylenmeden verilen bir karar ölçülemez, çünkü hangi sayının oynayacağı belli değildir. Kursun kendi varsayımları — Veri Dağıtımı ile Önbellek Mimarisi konularının kendi adlarıyla yazdıkları ve bu konunun OY1–OY5’i — K01’in tablosuna karışmadı; her biri gerekçesi ve duyarlılığıyla yazıldı.

Kursun bıraktığı soru bütün bu parçaların ortak varsayımındadır. Veri çoğaltıldı, işleve bölündü, parçalandı, önbelleğe alındı ve okuma modeli yazma modelinden ayrıldı; her karar bir sayıyı oynattı ve bir sayıyı büyüttü. Ama bütün ölçümler parçaların çalıştığını varsaydı. Bir kopya düştüğünde, bir parça yavaşladığında, izdüşümü besleyen kütük ile okuma tarafı arasındaki bağ koptuğunda ne olacağı bu kursta tasarlanmadı: kısmi arıza, yavaşlamanın yayılması, ağ bölünmesi, devre kesme, geri basınç ve kurtarma hedefleri hiç sayılmadı. Sonraki kurs Dayanıklılık ve Güvenilirlik o soruyu ele alır.

İ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