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.