İçeriğe geç
academia.sh

Ders 06 / 18

Mimari Geçiş Stratejileri

Çalışan bir tek parçadan aşamalı ayrıştırmaya geçiş: boğucu incir kalıbının kod tarafındaki ölçüsü — iki kod tabanında tutulan ortak mantık, tek bir oran değişikliğinin iki dosyaya dokunması, yolların ayrışması ve yönlendirme anahtarıyla tek adımda geri alınabilirlik.

İçindekiler

Beş biçim kuruldu ve her biri kendi üç sütununu yazdı. Elde çalışan bir tek parça varken bu biçimlerden birine nasıl geçileceği ise hiç ele alınmadı. Bu ders geçişin kendisini ölçer.

Tek seferde bütün sistemi yeni bir biçime taşımak, ölçülen bütün maliyetleri aynı anda üstlenmek demektir. Bunun yerine yol yol ayrıştırılır: bir iş akışının tek bir adımı yeni bir birime taşınır, geri kalan tek parçada kalır ve trafiğin hangi yola gideceğine bir anahtar karar verir. Taşınan yollar arttıkça eski gövde küçülür. Bu düzene boğucu incir geçişi (strangler fig) denir; Vaka Çalışmaları kursunda trafiğin ölçüldüğü bu kalıbın burada ölçülen yanı kod tarafıdır.

MS7. Yeni yol tek bir adımı taşır: gecikme ücreti hesabı. Ödünç iş akışının geri kalanı tek parçada kalır. Ölçülen şey trafik payı değil, aynı mantığın iki kod tabanında tutulmasının bedelidir.

Anahtar

Eski yol, birinci dersteki ücret kuralının kendisidir.

// tekparca/ucret.mjs — eski yoldaki ucret kurali
// KURAL BASI
export const GUNLUK = 2;
export const ucret = (gun) => (gun > 0 ? gun * GUNLUK : 0);
// KURAL SONU

Yönlendirici iş akışıyla ücret adımı arasına girer. Anahtar kapalıyken çağrı yerel kalır, açıkken ağ üzerinden yeni birime gider.

// tekparca/yonlendirici.mjs — bogucu incir anahtari; adim eski ya da yeni yola gider
import { ucret as eskiUcret } from "./ucret.mjs";
export async function ucretHesapla(gun) {
  const p = process.env.YENI_UCRET;
  if (!p) return { ucret: eskiUcret(gun), yol: "eski" };
  return (await fetch(`http://127.0.0.1:${p}/hesapla?gun=${gun}`)).json();
}
// tekparca/uygulama.mjs — odunc is akisi; yalniz ucret adimi yonlendiriciden gecer
import { ucretHesapla } from "./yonlendirici.mjs";
const durum = { kitap: "rafta", acikOdunc: 1, sinir: 5 };
const gun = Number(process.argv[2] ?? 3);
if (durum.acikOdunc >= durum.sinir) { console.log("odunc siniri asildi"); process.exit(0); }
durum.kitap = "odunc";
durum.acikOdunc += 1;
const { ucret, yol } = await ucretHesapla(gun);
console.log(`odunc verildi: kitap 1 -> uye 4, gecikme ${gun} gun, ucret ${ucret} (ucret yolu: ${yol})`);

Yeni birim, kuralı kendi kod tabanında taşır. Taşımak zorundadır: ayrı bir dağıtım birimi olması, tek parçanın dosyalarını ithal edememesi demektir.

// ucret-servisi.mjs — yeni yol; ayni kural ikinci kod tabaninda
import { createServer } from "node:http";
const port = Number(process.argv[2]);
if (!port) { console.log("kullanim: node ucret-servisi.mjs <port>"); process.exit(0); }
// KURAL BASI
export const GUNLUK = 2;
export const ucret = (gun) => (gun > 0 ? gun * GUNLUK : 0);
// KURAL SONU
createServer((istek, yanit) => {
  const gun = Number(new URL(istek.url, "http://y").searchParams.get("gun"));
  yanit.end(JSON.stringify({ ucret: ucret(gun), yol: "yeni" }));
}).listen(port, () => console.log(`ucret ${port}`));
node tekparca/uygulama.mjs 3
odunc verildi: kitap 1 -> uye 4, gecikme 3 gun, ucret 6 (ucret yolu: eski)
# baslat.sh — yeni ucret sureci ayaga kalkar, ayni istek anahtar acikken kosar
node ucret-servisi.mjs 8797 >ucret.log 2>&1 & echo $! >ucret.pid
for i in $(seq 40); do curl -s -o /dev/null "http://127.0.0.1:8797/" && break; sleep 0.2; done
YENI_UCRET=8797 node tekparca/uygulama.mjs 3
odunc verildi: kitap 1 -> uye 4, gecikme 3 gun, ucret 6 (ucret yolu: yeni)

İki yol da aynı sonucu verdi. Geçişin istenen durumu budur: yeni yol açıldığında dışarıdan hiçbir fark görünmez. Fark yalnız ucret yolu alanında.

İki Kod Tabanı

Ölçülecek şey bu iki koşumun eşitliğinde değil, o eşitliği sürdürmenin bedelinde.

// cift.mjs — ayni is kuralinin iki kod tabaninda tutuldugunu olcer
import { readFileSync } from "node:fs";
const kural = (yol) => {
  const s = readFileSync(yol, "utf8").split("\n");
  return s.slice(s.indexOf("// KURAL BASI") + 1, s.indexOf("// KURAL SONU"))
    .map((x) => x.trim()).filter(Boolean);
};
const eski = kural("tekparca/ucret.mjs"), yeni = kural("ucret-servisi.mjs");
const ayni = eski.filter((s) => yeni.includes(s));
console.log(`kural satiri: eski ${eski.length}, yeni ${yeni.length}, birebir ayni ${ayni.length}`);
console.log(`ayni mantigin tutuldugu kod tabani: 2   bir oran degisiminde dokunulacak dosya: 2`);
console.log(`yonlendirme anahtari: YENI_UCRET   geri alma adimi: 1   yeniden dagitim: 0`);
node cift.mjs
kural satiri: eski 2, yeni 2, birebir ayni 2
ayni mantigin tutuldugu kod tabani: 2   bir oran degisiminde dokunulacak dosya: 2
yonlendirme anahtari: YENI_UCRET   geri alma adimi: 1   yeniden dagitim: 0

Kuralın iki satırı da iki kod tabanında birebir duruyor. Geçiş süresince bu bir seçim değil, zorunluluktur: eski yol hâlâ trafik alıyorsa mantığı taşımak zorundadır, yeni yol da kendi kopyasını taşımak zorundadır çünkü ötekinin dosyalarına erişemez.

Çift Bakım

Bedel, kural değiştiğinde ortaya çıkar. Günlük oran yalnız yeni kod tabanında güncellensin.

# ayrik.sh — oran yalniz yeni kod tabaninda degisir; iki yol ayrisir
kill "$(cat ucret.pid)"
sleep 0.3
sed -i.y 's/export const GUNLUK = 2;/export const GUNLUK = 5;/' ucret-servisi.mjs
rm -f ucret-servisi.mjs.y
node ucret-servisi.mjs 8797 >>ucret.log 2>&1 & echo $! >ucret.pid
for i in $(seq 40); do curl -s -o /dev/null "http://127.0.0.1:8797/" && break; sleep 0.2; done
YENI_UCRET=8797 node tekparca/uygulama.mjs 3
node tekparca/uygulama.mjs 3
odunc verildi: kitap 1 -> uye 4, gecikme 3 gun, ucret 15 (ucret yolu: yeni)
odunc verildi: kitap 1 -> uye 4, gecikme 3 gun, ucret 6 (ucret yolu: eski)

Aynı üye, aynı kitap, aynı gecikme — iki farklı ücret. Hangi tutarın uygulanacağını iş kuralı değil, isteğin hangi yola düştüğü belirliyor. Bu, geçiş dönemine özgü bir arıza kipidir ve hiçbir sürecin çökmesini gerektirmez: iki kod tabanı da çalışıyor, ikisi de kendince doğru.

Ölçü şudur: geçiş süresince ücret kuralına dokunan her değişiklik iki dosyaya yazılmak zorundadır. Bu maliyet taşınan yol sayısıyla değil, taşıma süresinin uzunluğuyla çarpılır. Yarım kalmış bir geçiş, kalıcı olarak iki katı bakım demektir.

Geri Alınabilirlik

Buna karşılık kalıbın asıl kazancı da aynı yerdedir.

# geri-al.sh — yonlendirme anahtari kapatilir; yeni servis durdurulmaz
echo "anahtar kapali, yeniden dagitim yok"
node tekparca/uygulama.mjs 3
echo "yeni servis hala ayakta: $(curl -s 'http://127.0.0.1:8797/hesapla?gun=3')"
anahtar kapali, yeniden dagitim yok
odunc verildi: kitap 1 -> uye 4, gecikme 3 gun, ucret 6 (ucret yolu: eski)
yeni servis hala ayakta: {"ucret":15,"yol":"yeni"}

Eski davranış tek adımda geri geldi: bir ortam değişkeni verilmedi, o kadar. Yeni servis durdurulmadı, hiçbir şey yeniden dağıtılmadı, hiçbir dosya değişmedi. Eski kod silinmiş olsaydı bu adımın karşılığı eski kod tabanını yeniden yayına çıkarmak olurdu.

Geri alınabilirliğin bedeli, eski yolun silinmemiş olmasıdır. Geçişin bitmiş sayılması, anahtarın kalıcı olarak açılması ve eski kuralın silinmesiyle olur; o ana kadar çift bakım sürer. İki maliyet aynı madalyonun iki yüzüdür.

# esitle.sh — ayni degisiklik eski kod tabanina da tasinir; iki yol yeniden ayni
sed -i.y 's/export const GUNLUK = 2;/export const GUNLUK = 5;/' tekparca/ucret.mjs
rm -f tekparca/ucret.mjs.y
echo "dokunulan dosya: 2 (tekparca/ucret.mjs, ucret-servisi.mjs)"
YENI_UCRET=8797 node tekparca/uygulama.mjs 3
node tekparca/uygulama.mjs 3
kill "$(cat ucret.pid)" 2>/dev/null
dokunulan dosya: 2 (tekparca/ucret.mjs, ucret-servisi.mjs)
odunc verildi: kitap 1 -> uye 4, gecikme 3 gun, ucret 15 (ucret yolu: yeni)
odunc verildi: kitap 1 -> uye 4, gecikme 3 gun, ucret 15 (ucret yolu: eski)

Üç Sütun

Aşamalı geçiş
Ucuzlattığı Geri alma 1 adım, yeniden dağıtım 0; yeni biçimin maliyeti tek yolda sınanıyor, sistemin tamamı taşınmıyor
Pahalılaştırdığı Aynı kural 2 kod tabanında; her değişiklik 2 dosyaya yazılıyor; yönlendirici bir ek dolaylılık katmanı
Doğan arıza kipi Yalnız bir kod tabanı güncellendiğinde aynı istek iki farklı sonuç veriyor; hiçbir süreç çökmüyor, iki yol da kendince doğru

Özet

  • Boğucu incir geçişi bir iş akışının tek adımını yeni bir birime taşır, geri kalanı yerinde bırakır ve trafiği bir yönlendirme anahtarına bağlar.
  • Yeni birim ayrı bir dağıtım birimi olduğu için kuralı kendi kod tabanında taşımak zorunda; ölçümde aynı iki satır iki kod tabanında birebir duruyor.
  • Çift bakımın ölçüsü, oran değişikliğinin 2 dosyaya yazılmak zorunda olmasıdır; yalnız birine yazıldığında aynı istek 15 ve 6 gibi iki farklı sonuç verdi.
  • Geri alma 1 adımdır ve yeniden dağıtım gerektirmez, çünkü eski yol hâlâ duruyor; bu kazancın bedeli çift bakımın sürmesidir.
  • Geçiş, anahtarın kalıcı olarak açılması ve eski kuralın silinmesiyle biter; yarım kalmış bir geçiş kalıcı iki katı bakım demektir.

Sonraki Adım

Bu konu boyunca aynı ödünç iş akışı beş biçimde kuruldu — tek parça, modüler tek parça, servis yönelimli düzen, mikroservisler, sunucusuz işlevler — ve altıncı derste bunlardan birine geçmenin bedeli ölçüldü. Her ölçümde bir sayı değişti: ithal kenarı, sınır ihlali, ortak modele bağlı birim, süreç, uç, ağ atlaması, depo çağrısı, dokunulan dosya.

Bütün bu ölçümlerin ortak bir varsayımı vardı ve hiçbir yerde sorgulanmadı: sınırın nereden geçtiği verili kabul edildi. Katalog, üyelik, ödünç, bildirim ve ücretlendirme daha ilk derste beş ayrı modül olarak çizildi; sonraki beş ders o çizgiyi olduğu gibi devraldı ve yalnız çizginin hangi biçimde gerçekleştirileceğini tartıştı. Oysa asıl karar biçim değil, çizginin yeridir. Ücretlendirme neden üyeliğin içinde değil? Bildirim neden ödünçten ayrı?

Bu soru şimdiye kadar hiçbir ölçüte bağlanmadı. Sonraki konu tam olarak bunu ele alır: bir sınırın nereden geçmesi gerektiğine kodda ve koşumda görünen hangi kanıtla karar verilir.

İ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