İçeriğe geç
academia.sh

Ders 18 / 18

Sözleşme Evrimi

Bir sözleşme alanının eklenmesi, kaldırılması, adının değişmesi ve zorunlu kılınması: her adımın iki yönde kaç tüketiciyi kırdığı, kırılmanın sessiz mi gürültülü mü olduğu, korumalı okumanın hangi yönü kurtardığı ve geçiş sırasının dağıtım adımı, kırılan çağrı ve taşınan fazla alan olarak ölçülmesi.

İçindekiler

Önceki ders bir söz verdi: yayıncı her olayda artan bir surum alanı yayımlayacak, tüketici onu okuyacak. Aynı türden sözler kurs boyunca birikti: ileti_id kalıcı olacak, gun hep aynı anlamı taşıyacak. Bu alanlar ayrı dağıtım birimlerinin ortak tuttuğu bir sözleşmedir.

Sorun sözleşmenin varlığı değil, değişmesidir. Servisler ayrı birimler olduğu için sözleşme tek bir anda değişemez: üretici ile tüketiciler farklı zamanlarda yayına çıkar ve arada iki sürümün birlikte koştuğu bir pencere açılır. M21/K03 bu sözleşmenin test tarafını, M19/K03 genişlet–daralt kalıbını kurmuştu; ikisi de burada tekrarlanmıyor. Buranın konusu evrim adımlarının kendisi: hangi değişiklik kimi kırıyor, sessiz mi kırıyor, ve adımlar hangi sırayla atılırsa pencere zararsız geçiyor.

Düzenek

DT9. Üretici ve tüketiciler süreç içi işlevlerdir; ölçülen şey sözleşmenin biçimidir, teslim mekanizması değil.

DT10. Kırılma ölçütü: tüketicinin ürettiği iş sonucunun, değişiklikten önceki doğru değerden farklı olması. Değer farkı sessiz yanlış, atılan bir özel durum hata sayılır.

DT11. Pencere, iki dağıtım adımı arasında akan 50 ileti olarak sabitlenmiştir. Gerçek uzunluk dağıtım hızına bağlıdır; sabit tutulduğu için planlar karşılaştırılabilir.

Dört Adım, İki Yön

Ödünç servisi odunc_verildi iletisini yayımlıyor, ücretlendirme gecikme ücretini hesaplıyor. Dört değişiklik türünün her biri iki yönde sınanır: eski tüketici yeni iletiyi okuduğunda ve yeni tüketici eski iletiyi okuduğunda.

// uyumluluk.mjs — dort evrim adimi, iki yon: eski tuketici yeni iletiyi, yeni tuketici eskiyi okur
const temel = { ileti_id: 1, kitap: 101, uye: 4 };

const adimlar = [
  { ad: "alan ekleme   ", dogru: 28,
    eskiIleti: { ...temel, gun: 14 },
    yeniIleti: { ...temel, gun: 14, gunluk_ucret: 2 },
    eskiT: (i) => 2 * i.gun,
    yeniT: (i) => i.gunluk_ucret * i.gun,
    korumaliT: (i) => (i.gunluk_ucret ?? 2) * i.gun },
  { ad: "alan kaldirma ", dogru: 336,
    eskiIleti: { ...temel, gun: 14, saat: 336 },
    yeniIleti: { ...temel, gun: 14 },
    eskiT: (i) => i.saat,
    yeniT: (i) => i.gun * 24,
    korumaliT: (i) => i.saat ?? i.gun * 24 },
  { ad: "ad degistirme ", dogru: 28,
    eskiIleti: { ...temel, gun: 14 },
    yeniIleti: { ...temel, sure_gun: 14 },
    eskiT: (i) => 2 * i.gun,
    yeniT: (i) => 2 * i.sure_gun,
    korumaliT: (i) => 2 * (i.sure_gun ?? i.gun) },
  { ad: "zorunlu yapma ", dogru: 28,
    eskiIleti: { ...temel, gun: 14 },
    yeniIleti: { ...temel, gun: 14, gunluk_ucret: 2 },
    eskiT: (i) => 2 * i.gun,
    yeniT: (i) => { if (i.gunluk_ucret === undefined) throw new Error("gunluk_ucret zorunlu");
                    return i.gunluk_ucret * i.gun; },
    korumaliT: (i) => { if (i.gunluk_ucret === undefined) throw new Error("gunluk_ucret zorunlu");
                        return i.gunluk_ucret * i.gun; } },
];

const dene = (f, ileti, dogru) => {
  try { return f(ileti) === dogru ? "tamam" : "sessiz yanlis"; }
  catch { return "hata"; }
};
const yaz = (s) => s.padEnd(13);

const say = { tamam: 0, sessiz: 0, hata: 0 }, korumali = { tamam: 0, sessiz: 0, hata: 0 };
const ekle = (k, s) => { k[s === "tamam" ? "tamam" : s === "hata" ? "hata" : "sessiz"] += 1; };

console.log("adim            | eski T <- yeni ileti | yeni T <- eski ileti | korumali T <- eski ileti");
for (const a of adimlar) {
  const yon1 = dene(a.eskiT, a.yeniIleti, a.dogru);
  const yon2 = dene(a.yeniT, a.eskiIleti, a.dogru);
  const yon3 = dene(a.korumaliT, a.eskiIleti, a.dogru);
  ekle(say, yon1); ekle(say, yon2); ekle(korumali, yon1); ekle(korumali, yon3);
  console.log(`${a.ad} | ${yaz(yon1)}        | ${yaz(yon2)}        | ${yaz(yon3)}`);
}
console.log(`duz tuketici : tamam=${say.tamam} sessiz yanlis=${say.sessiz} hata=${say.hata} (8 birlesim)`);
console.log(`korumali     : tamam=${korumali.tamam} sessiz yanlis=${korumali.sessiz} hata=${korumali.hata} (8 birlesim)`);
node uyumluluk.mjs
adim            | eski T <- yeni ileti | yeni T <- eski ileti | korumali T <- eski ileti
alan ekleme    | tamam                | sessiz yanlis        | tamam        
alan kaldirma  | sessiz yanlis        | tamam                | tamam        
ad degistirme  | sessiz yanlis        | sessiz yanlis        | tamam        
zorunlu yapma  | tamam                | hata                 | hata         
duz tuketici : tamam=3 sessiz yanlis=4 hata=1 (8 birlesim)
korumali     : tamam=5 sessiz yanlis=2 hata=1 (8 birlesim)

Sekiz birleşimin yalnız 3’ü sorunsuz. Asıl bulgu, kalan 5’in dağılımıdır: 4 sessiz yanlış, 1 hata. Eksik bir alan undefined olur, hesaba girer ve sonuç sessizce bozulur; hiçbir yerde hata kaydı oluşmaz. Sözleşmeyi kıran değişikliklerin çoğu gürültü çıkarmaz.

Tek gürültülü satır “zorunlu yapma”dır ve gürültünün nedeni değişikliğin türü değil, tüketicinin alanı açıkça denetlemesidir. Sessizlik sözleşmenin değil, doğrulamasızlığın sonucudur.

Sıra kuralı bu tablodan doğrudan okunur. Alan eklemede kırılan yön “yeni tüketici ← eski ileti”dir; öyleyse önce üretici yayına çıkar. Alan kaldırmada kırılan yön “eski tüketici ← yeni ileti”dir; öyleyse önce tüketici yayına çıkar. Ad değiştirme ikisinin toplamıdır ve her iki yön de kırılır: tek bir sıra onu güvenli yapamaz.

Tüketici Koruması

Son sütun, yeni tüketicinin eski adı da okuyacak biçimde yazılmasıdır. Sessiz yanlış 4’ten 2’ye, tamam 3’ten 5’e çıkıyor. Kurtarılanların üçü de aynı yöndedir: yeni tüketici ← eski ileti. Kalan 2 sessiz yanlış öteki yöndedir ve tüketici kodu onları kurtaramaz — o iletiyi okuyan tüketici zaten yayına çıkmış eski koddur, ona dokunulamaz.

Buradan kursun en dar kuralı çıkar: tüketici koruması yalnız bir yönü kapatır; öteki yönü kapatmak üreticinin işidir. Üretici, eski adı bir süre daha göndermeye devam ederek kapatır.

Geçiş Sırası

Ad değişikliği üç planla koşturulur. Dört tüketicinin ikisi alanı okuyor, ikisi okumuyor.

// gecis.mjs — ayni ad degisikligi (gun -> sure_gun), uc gecis plani; pencereler sayilir
const PENCERE = 50;                       // iki adim arasinda akan ileti sayisi

const ileti = {
  v1:    (n) => ({ ileti_id: n, kitap: 101, uye: 4, gun: 14 }),
  gecis: (n) => ({ ileti_id: n, kitap: 101, uye: 4, gun: 14, sure_gun: 14 }),
  v2:    (n) => ({ ileti_id: n, kitap: 101, uye: 4, sure_gun: 14 }),
};
const tuketici = {                        // ilk ikisi alani hic okumuyor, son ikisi okuyor
  uyelik:   { dogru: 4,   v1: (i) => i.uye,     v2: (i) => i.uye },
  bildirim: { dogru: 101, v1: (i) => i.kitap,   v2: (i) => i.kitap },
  ucret:    { dogru: 28,  v1: (i) => 2 * i.gun, v2: (i) => 2 * i.sure_gun },
  gorunum:  { dogru: 14,  v1: (i) => i.gun,     v2: (i) => i.sure_gun },
};
const planlar = {
  "tek adim, once uretici ": [["uretici", "v2"], ["ucret", "v2"], ["gorunum", "v2"]],
  "tek adim, once tuketici": [["ucret", "v2"], ["gorunum", "v2"], ["uretici", "v2"]],
  "genislet-daralt        ": [["uretici", "gecis"], ["ucret", "v2"], ["gorunum", "v2"],
                              ["uretici", "v2"]],
};

for (const [ad, plan] of Object.entries(planlar)) {
  const durum = { uretici: "v1", uyelik: "v1", bildirim: "v1", ucret: "v1", gorunum: "v1" };
  let kirilan = 0, cagri = 0, fazlaAlan = 0, fazlaBayt = 0;
  for (const [birim, surum] of plan) {
    durum[birim] = surum;                 // bir dagitim adimi
    for (let n = 1; n <= PENCERE; n++) {
      const i = ileti[durum.uretici](n);
      if (durum.uretici === "gecis") {          // iki ad birlikte tasiniyor
        fazlaAlan += Object.keys(i).length - Object.keys(ileti.v2(n)).length;
        fazlaBayt += JSON.stringify(i).length - JSON.stringify(ileti.v2(n)).length;
      }
      for (const [t, k] of Object.entries(tuketici)) {
        cagri += 1;
        let s; try { s = k[durum[t]](i); } catch { s = null; }
        if (s !== k.dogru) kirilan += 1;
      }
    }
  }
  console.log(`${ad} | dagitim adimi=${plan.length} tuketici cagrisi=${cagri}` +
    ` | kirilan cagri=${kirilan} | tasinan fazla alan=${fazlaAlan} fazla bayt=${fazlaBayt}`);
}
const okuyan = Object.values(tuketici).filter((k) => String(k.v1).includes("gun")).length;
console.log(`pencere basina ileti=${PENCERE} | tuketici=${Object.keys(tuketici).length}` +
  ` | alani okuyan tuketici=${okuyan}` +
  ` | gecis surumunde fazla alan=${Object.keys(ileti.gecis(1)).length - Object.keys(ileti.v2(1)).length}`);
node gecis.mjs
tek adim, once uretici  | dagitim adimi=3 tuketici cagrisi=600 | kirilan cagri=150 | tasinan fazla alan=0 fazla bayt=0
tek adim, once tuketici | dagitim adimi=3 tuketici cagrisi=600 | kirilan cagri=150 | tasinan fazla alan=0 fazla bayt=0
genislet-daralt         | dagitim adimi=4 tuketici cagrisi=800 | kirilan cagri=0 | tasinan fazla alan=150 fazla bayt=1350
pencere basina ileti=50 | tuketici=4 | alani okuyan tuketici=2 | gecis surumunde fazla alan=1

İki tek adımlı plan aynı sayıyı verdi: 600 tüketici çağrısının 150’si kırıldı. Sıra kırılmanın yerini değiştirir, miktarını değiştirmez — çünkü ad değiştirme her iki yönde de kırar ve hangi taraf önce giderse öteki taraf o kadar süre yanlış okur.

Genişlet–daralt planı kırılan çağrıyı 0‘a indirir. Bedeli üç kalemdir: dağıtım adımı 3’ten 4’e çıktı, pencere boyunca 150 ileti fazladan bir alan taşıdı (1350 bayt), ve üretici o süre boyunca aynı bilgiyi iki adla üretmek zorunda kaldı. Çift kodun nerede duracağı bir seçimdir: üreticide tutulursa 1 birim iki adı birlikte taşır, tüketicide tutulursa alanı okuyan 2 birim korumalı okuma satırını taşır. Ölçüm ikinci seçeneğin daha pahalı olduğunu söyler — okuyan tüketici sayısı arttıkça fark büyür.

Dördüncü adım — daraltma — atlanabilir görünür. Atlandığında sistem çalışmaya devam eder; sözleşmede ölü bir alan kalır ve onu kimin okuduğunu kimse bilmez.

Özet

  • Sekiz değişiklik–yön birleşiminin 3’ü sorunsuz, 4’ü sessiz yanlış, 1’i hata verdi; sözleşmeyi kıran değişikliklerin çoğu gürültü çıkarmaz.
  • Tek gürültülü satır, tüketicinin alanı açıkça denetlediği satırdır; sessizlik sözleşmenin değil doğrulamasızlığın sonucudur.
  • Sıra kuralı ölçümden okunur: alan eklemede önce üretici, alan kaldırmada önce tüketici yayına çıkar. Ad değiştirme ikisinin toplamıdır ve tek bir sıra onu güvenli yapamaz.
  • Korumalı okuma sessiz yanlışı 4’ten 2’ye indirdi; kurtardığı üç birleşimin üçü de “yeni tüketici ← eski ileti” yönündedir. Öteki yön yalnız üreticinin eski adı bir süre daha göndermesiyle kapanır.
  • Ad değişikliğinde iki tek adımlı plan da 600 çağrının 150’sini kırdı; genişlet–daralt 0 kırdı. Bedeli 1 ek dağıtım adımı, 150 fazla alan ve 1350 fazla bayttır.
  • Daraltma adımı atlandığında hiçbir şey kırılmaz; sözleşmede kalıcı olarak ölü bir alan kalır.

Kurs Kapanışı

Kurs boyunca aynı kütüphane ödünç sistemi on sekiz kez, on sekiz farklı biçimde kuruldu. Her ders aynı üç sütunu yazdı.

Ders Neyi ucuzlattı Neyi pahalılaştırdı Doğan arıza kipi
Tek Parça Uygulama Yerel çağrı, ortak işlem sınırı, tek dağıtım adımı Ölçekleme birimi tüm kod tabanı; bir satırlık değişiklik 7 modülü yeniden yayınlar Bir modüldeki sızıntı ya da sonsuz döngü bütün alanları birlikte durdurur
Modüler Tek Parça Bir alanın iç yapısı değişince dokunulan dosya 3’ten 1’e indi Genel yüz 7’den 11’e çıktı; her alanlar arası ihtiyaç bir dolaylılık ekliyor Kural yalnız denetleyici koşarken geçerli; tek satırlık ithal sınırı sessizce geri alıyor
Servis Yönelimli Mimari Birimler birbirini ithal etmiyor; yeni birim eklemek mevcutları değiştirmiyor Ortak modele bağlı birim 1’den 3’e; bir alan adı değişikliği 3 dosyayı birlikte yayına zorluyor Sahibi güncellenip tüketicisi güncellenmediğinde hata alınmıyor, sınır denetimi sessizce kalkıyor
Mikroservisler Bir oran değişikliği 5 dağıtım biriminin 1’ini yeniden başlattı İstek başına 9 ağ atlaması, 7 sıralı tur; başlatma 1 adımdan 6 adıma Zincirin ortasında kırılan istek yarım durum bırakıyor; arıza kısmi
Sunucusuz Yaklaşım Ayakta tutulan süreç 5’ten 1’e düştü; dağıtım birimi tek dosya İstek başına 4 yeni süreç; depo çağrısı 2’den 9’a çıktı Süre sınırı işi ortasından keser: kod hatasız, sonuç eksik, kısmi etki depoda kalır
Mimari Geçiş Stratejileri Geri alma 1 adım, yeniden dağıtım 0; yeni biçim tek yolda sınanıyor Aynı kural 2 kod tabanında; her değişiklik 2 dosyaya yazılıyor Yalnız bir kod tabanı güncellenince aynı istek iki farklı sonuç veriyor
Sınır Çizme Ölçütleri İyi çizilen sınırda değişiklik tek dağıtım biriminde kalıyor: tek doğrulama, tek geri alma Kötü çizilende aynı değişiklik 2 birime dokunuyor; 1 ithal kenarı ağ çağrısına, 2 yazma sınır ihlaline dönüyor İki yayım arasında pencere: yeni oran bir birimde yürürlükte, öteki eski alanı gönderiyor
Eşzamanlı Servis Çağrıları Dört iş adımı üç turda bitiyor; bir servisin kapanması üç ucu ayakta bırakıyor İstek başına 3 ağ atlaması, 4 süreç, 4 kat işlenen istek Kısmi arıza; ardılın durum kodu denetlenmezse eksik iş yapılmış başarılı yanıt
Eşzamansız Tümleştirme Yanıt turu 2’den 1’e, yanıt yolundaki birim 3’ten 1’e indi Sözleşme 3 dağıtım birimine yayıldı; alan başına 2,17 birim bağlandı Tutarlılık penceresi ve yanıt yolunda hiç görünmeyen tüketici arızası
API Ağ Geçidi Yinelenen kenar satırı 15’ten 6’ya; dokunulan dosya ve istemci çağrısı 3’ten 1’e İstek başına atlama 3’ten 4’e; yeni bir dağıtım birimi; uç yolu değişince dokunulan dosya yine 2 Ağ geçidi bütün dış trafiğin bağımlılık kapanışı; yalın servisler kendini savunmuyor
Ön Yüz İçin Backend İstek 1’e, fazla bayt 0’a indi; istemcinin okuduğu aşağı alan adı 6’dan 0’a 2 yeni dağıtım birimi, 38 satırın 13’ü birebir aynı; aşağı servise ulaşan istek değişmiyor Aşağı alan adı değişip tek katman güncellenince istemciler sessizce ayrışıyor
Servis Ağı Uygulama kodu 38 satırdan 13’e; ağ ilgisi diye işaretli 18 satır uygulamada kalmadı Süreç 2’den 3’e, yerel durak 1’den 2’ye, ücret servisine ulaşan istek 4’ten 5’e Durum koduna bakan yeniden deneme, yinelemeye kapalı ödeme çağrısını yineledi: tahsilat 2
Servis Keşfi Yeni kopya eklenince dokunulan yapılandırma 3’e karşı 0; kira dolunca 6/6’ya dönüş 1 ek süreç ve tek arıza noktası, çağrı başına 1 ek çözümleme isteği Kayıt defteri gerçeğin gecikmeli kopyasını tutar; keşif penceresinde 6 isteğin 2’si ölü adrese gidiyor
Dağıtık İşlem Alternatifleri Kısmi sonuç ortadan kalktı; uygulama kodunda telafi mantığı yazılmıyor Tur 1’den 2’ye, mesaj n’den 2n‘e; kilit 459,5 ms tutuldu, 40 isteğin 6’sı bloke Belirsiz durum: oy vermiş ama karar almamış katılımcı kilidi süresiz tutar
Saga Kalıbı Kilit penceresi sıfırlandı; hiçbir kaynak başka servisin yanıtı beklenirken tutulmuyor Her adıma bir telafi; sıra telafi edilebilirliğe bağlandı; koreografide akış bilgisi 1 dosyadan 4’e Yarım kalmış akış: telafi edilemeyen adımdan sonraki hata 2 geri alınamayan teslim bıraktı
Giden Kutusu Kalıbı “Yapıldı ama duyurulmadı” kalktı: 22 kayıp 0’a indi ve kayıp sessiz olmaktan çıktı Her istek iki tabloya yazıyor; yeni bir süreç ve yoklama aralığı; kutu budanmazsa büyüyor İkinci teslim (24 ileti) ve aktarıcı gerilemesi: sistem hata vermez, yalnız geç kalır
Olay Sıralama ve Yinelenme Bozulmuş akıştan doğru son durum: 4 yanlış kitap 0’a, fazladan bildirim 0’a Tüketici şemasına bir sütun, ileti başına bir okuma (+1471 sorgu), yayıncıya sürüm yükümlülüğü Sessiz atma: 585 gerçek olay hiçbir hata üretmeden yutuldu
Sözleşme Evrimi Genişlet–daralt kırılan çağrıyı 150’den 0’a; korumalı okuma sessiz yanlışı 4’ten 2’ye indirdi 3 yerine 4 dağıtım adımı; pencerede 150 fazla alan ve 1350 fazla bayt taşındı Daraltma atlanınca hiçbir şey kırılmaz; sözleşmede ölü bir alan kalıcı olarak kalır

Tablo kursun kuralını taşıyor: bir mimari ancak bu üç sütunla savunulur. “Daha ölçeklenebilir” bir karar değildir; hangi birimin ölçeklendiğini, karşılığında kaç ağ atlamasının ve kaç dağıtım adımının eklendiğini ve hangi yeni arıza kipinin doğduğunu söylemeyen bir cümle mimari bir gerekçe sayılmaz. Yukarıdaki on sekiz satırın hiçbirinde üçüncü sütun boş değildir: her sınır, kaldırdığı sorunun yerine yenisini koydu.

Bir şey de eksik kaldı. Bu tablodaki arıza kiplerinin çoğu sessizdir: sessizce geri alınan sınır, sessizce ayrışan istemciler, sessizce yutulan olay, sessizce yanlış okunan alan. Sistem artık birden çok süreçte koşuyor ve her sınır yeni bir arıza kipi doğurdu; ama bir isteğin nerede yavaşladığını ya da nerede kaybolduğunu görmenin aracı hiç kurulmadı. Her ders kendi ölçümünü kendi yazdığı sayaçla yaptı; koşan sistemin kendisi bu sayaçları taşımıyor. Sonraki kurs, M16/K07 Gözlemlenebilirlik ve Güvenilirlik, tam olarak bu eksiği 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