İçeriğe geç
academia.sh

Ders 01 / 18

Tek Parça Uygulama

Çalışan bir tek parça ödünç sisteminin kurulması ve ölçülmesi: yedi modül, on ithal kenarı, tek süreç, sıfır ağ atlaması; yerel çağrının ucuzluğu, tek dağıtım biriminin getirdiği değiştirme kolaylığı ve aynı birimin çoğaltılmasıyla ortaya çıkan ölçek sınırı.

İçindekiler

Önceki kurs, Eşzamansız İşleme, kapanışında tek bir varsayımı işaret etti: bütün kuyruklar, teslim garantileri ve geri basınç düzenekleri tek bir dağıtım biriminin içinde kuruldu. Bu kurs o birimi bölmeyi ele alır. Bölmeden önce bölünecek şeyin ne olduğu ortada durmalıdır.

Bu ders, kütüphane ödünç sisteminin çalışan bir tek parça (monolith) gerçekleştirmesini kurar ve ölçer. Kurulan düzenek sonraki beş dersin tabanıdır: her ders onu bir adım böler ya da bölmenin bedelini bu ölçülerin üzerinden sayar. Mimari biçemlerin tanımı Mimari Biçemler kursunda yapıldı; buradaki iş tanım değil, gerçekleştirme ve ölçüdür.

Düzeneğin Sınırı

MS1. Durum tek bir JSON dosyasında tutulur. Gerçek bir dağıtımda burada paylaşılan bir veri deposu olurdu; modelin taşıdığı özellik, durumun süreç dışında ve tek yerde olmasıdır.

MS2. Modüller aynı süreçte, yerel işlev çağrısıyla konuşur. Ağ gecikmesi, serileştirme ve kısmi hata bu modelde yoktur; onlar dördüncü derste gerçek süreçlerle ölçülür.

Düzenek: Tek Parça Ödünç Sistemi

Sistem beş iş alanı taşır: katalog, üyelik, ödünç, bildirim ve ücretlendirme. Durumu tutan ortak bir modül var.

// depo.mjs — butun modullerin durumu; tek dosya, tek surec
import { existsSync, readFileSync, writeFileSync } from "node:fs";
const YOL = "veri.json";
const BASLANGIC = {
  kitap: { 1: { baslik: "Korluk", durum: "rafta" }, 2: { baslik: "Kum Kitabi", durum: "rafta" } },
  uye: { 4: { ad: "Emre", acik: 1, sinir: 3, borc: 0 },
         5: { ad: "Selin", acik: 3, sinir: 3, borc: 0 } },
  odunc: [],
};
export function oku() {
  if (existsSync(YOL) === false) writeFileSync(YOL, JSON.stringify(BASLANGIC));
  return JSON.parse(readFileSync(YOL, "utf8"));
}
export function yaz(d) { writeFileSync(YOL, JSON.stringify(d)); }

Üç iş modülü bu depoyu okur ve yazar; bildirim modülünün durumu yoktur.

// katalog.mjs — kitap kayitlari
import { oku, yaz } from "./depo.mjs";
export function durum(kitapId) { return oku().kitap[kitapId].durum; }
export function oduncIsaretle(kitapId) {
  const d = oku();
  if (d.kitap[kitapId].durum !== "rafta") throw new Error("kitap rafta degil");
  d.kitap[kitapId].durum = "odunc";
  yaz(d);
}
// uyelik.mjs — uye kayitlari ve odunc siniri
import { oku, yaz } from "./depo.mjs";
export function uygunMu(uyeId) { const u = oku().uye[uyeId]; return u.acik < u.sinir; }
export function oduncEkle(uyeId) { const d = oku(); d.uye[uyeId].acik += 1; yaz(d); }
// ucretlendirme.mjs — gecikme ucreti
import { oku, yaz } from "./depo.mjs";
export const GUNLUK = 2;
export function ucret(gecikmeGun) { return gecikmeGun > 0 ? gecikmeGun * GUNLUK : 0; }
export function borcEkle(uyeId, tutar) { const d = oku(); d.uye[uyeId].borc += tutar; yaz(d); }
// bildirim.mjs — uyeye bildirim
export function gonder(uyeId, metin) { console.log(`  bildirim -> uye ${uyeId}: ${metin}`); }

İş akışını yürüten modül dördünü de sırayla çağırır.

// odunc.mjs — odunc verme is akisi; dort modulu sirayla cagirir
import { oku, yaz } from "./depo.mjs";
import * as katalog from "./katalog.mjs";
import * as uyelik from "./uyelik.mjs";
import * as ucretlendirme from "./ucretlendirme.mjs";
import * as bildirim from "./bildirim.mjs";

export function oduncVer(kitapId, uyeId, gecikmeGun = 0) {
  if (uyelik.uygunMu(uyeId) === false) throw new Error("odunc siniri asildi");
  katalog.oduncIsaretle(kitapId);
  uyelik.oduncEkle(uyeId);
  const tutar = ucretlendirme.ucret(gecikmeGun);
  if (tutar > 0) ucretlendirme.borcEkle(uyeId, tutar);
  const d = oku();
  d.odunc.push({ kitapId, uyeId });
  yaz(d);
  bildirim.gonder(uyeId, `kitap ${kitapId} odunc verildi`);
  return { kitapId, uyeId, tutar };
}
// uygulama.mjs — tek giris noktasi; is akisini bastan sona kosturur
import { oduncVer } from "./odunc.mjs";
import { durum } from "./katalog.mjs";
const [kitapId = 1, uyeId = 4, gecikme = 0] = process.argv.slice(2).map(Number);
try {
  const s = oduncVer(kitapId, uyeId, gecikme);
  console.log(`odunc verildi: kitap ${s.kitapId} -> uye ${s.uyeId}, ucret ${s.tutar}`);
} catch (h) {
  console.log("basarisiz:", h.message);
}
console.log(`  katalog: kitap ${kitapId} = ${durum(kitapId)}`);

İş Akışının Koşumu

Dört istek dört yolu sınar: geçen bir ödünç, ödünçteki bir kitabın ikinci kez istenmesi, sınırındaki bir üye ve gecikme ücreti çıkaran bir kayıt.

rm -f veri.json
node uygulama.mjs 1 4
echo "--- ayni kitap ikinci kez ---"
node uygulama.mjs 1 4
echo "--- sinirdaki uye ---"
node uygulama.mjs 2 5
echo "--- gecikmeli kayit ---"
node uygulama.mjs 2 4 3
  bildirim -> uye 4: kitap 1 odunc verildi
odunc verildi: kitap 1 -> uye 4, ucret 0
  katalog: kitap 1 = odunc
--- ayni kitap ikinci kez ---
basarisiz: kitap rafta degil
  katalog: kitap 1 = odunc
--- sinirdaki uye ---
basarisiz: odunc siniri asildi
  katalog: kitap 2 = rafta
--- gecikmeli kayit ---
  bildirim -> uye 4: kitap 2 odunc verildi
odunc verildi: kitap 2 -> uye 4, ucret 6
  katalog: kitap 2 = odunc

Üçüncü koşumda dikkat çeken şey, üyelik denetiminin katalogdan önce yapılmış olmasıdır: sınır aşıldığı için kitap hiç işaretlenmedi. Bu sıralamayı ayakta tutan şey, iki denetimin de aynı süreçte, aynı oduncVer gövdesinde durmasıdır. Beşinci koşumdan sonra bu sıralama bir sözleşmeye dönüşecek; şimdilik bir if satırıdır.

Ölçü

Mimari tartışmasının taşıyacağı sayılar kaynak dosyalardan okunur.

// olc.mjs — modul, ithal kenari ve is akisindaki modullerarasi cagri sayisi
import { readdirSync, readFileSync } from "node:fs";
const KENAR = /from "\.\/([\w-]+\.mjs)"/g;
const dosyalar = readdirSync(".").filter((a) => a.endsWith(".mjs") && a !== "olc.mjs").sort();
let toplam = 0;
for (const d of dosyalar) {
  const hedef = [...readFileSync(d, "utf8").matchAll(KENAR)].map((m) => m[1].replace(".mjs", ""));
  toplam += hedef.length;
  console.log(`${d.padEnd(18)} -> ${hedef.join(", ") || "(yok)"}`);
}
const akis = [...readFileSync("odunc.mjs", "utf8")
  .matchAll(/\b(katalog|uyelik|ucretlendirme|bildirim)\.\w+\(/g)];
console.log(`modul: ${dosyalar.length}   ithal kenari: ${toplam}`);
console.log(`is akisindaki modullerarasi cagri: ${akis.length}   ag atlamasi: 0`);
console.log(`surec: 1   dagitim birimi: 1   baslatma adimi: 1`);
node olc.mjs
bildirim.mjs       -> (yok)
depo.mjs           -> (yok)
katalog.mjs        -> depo
odunc.mjs          -> depo, katalog, uyelik, ucretlendirme, bildirim
ucretlendirme.mjs  -> depo
uyelik.mjs         -> depo
uygulama.mjs       -> odunc, katalog
modul: 7   ithal kenari: 10
is akisindaki modullerarasi cagri: 6   ag atlamasi: 0
surec: 1   dagitim birimi: 1   baslatma adimi: 1

Yedi modül, on ithal kenarı, altı modüller arası çağrı. Bu altı çağrının hepsi yerel: yığıt üzerinde parametre geçirir, dönüş değerini doğrudan alır, hata throw ile yayılır. Ağ atlaması sıfırdır. Sistemi ayağa kaldırmak tek adımdır; ödünç iş akışını uçtan uca koşturmak için başka bir sürecin hazır olmasını beklemek gerekmez.

Bu üç sayı — altı yerel çağrı, sıfır atlama, bir dağıtım birimi — tek parçanın ucuzlattığı şeyi tek başına anlatır. Bir çağrının yarı yolda kaybolması diye bir durum yoktur; ya işlev döner ya hata atar. Katalog ile üyelik arasındaki tutarlılık için bir düzen kurmak gerekmez, çünkü ikisi de aynı veri.json dosyasına aynı süreçten yazar. Beş alana yayılan bir değişiklik tek bir derlemede sınanır ve tek bir dağıtımda yayına çıkar.

Ölçek Sınırı

Bedel, aynı birimin bölünememesinde görünür. Ödünç yolunun yükü arttığında yapılabilecek tek şey, birimin tamamını çoğaltmaktır.

# olcek.sh — ayni tek parcanin uc kopyasi; her kopya butun modulleri tasir
rm -rf kopya1 kopya2 kopya3
for k in kopya1 kopya2 kopya3; do
  mkdir "$k"
  for m in depo katalog uyelik ucretlendirme bildirim odunc uygulama; do cp "$m.mjs" "$k/"; done
  ( cd "$k" && node uygulama.mjs 1 4 | grep '^odunc' | sed "s|^|$k: |" )
done
echo "dagitilan modul dosyasi: $(ls kopya*/*.mjs | wc -l | tr -d ' ')"
kopya1: odunc verildi: kitap 1 -> uye 4, ucret 0
kopya2: odunc verildi: kitap 1 -> uye 4, ucret 0
kopya3: odunc verildi: kitap 1 -> uye 4, ucret 0
dagitilan modul dosyasi: 21

İki sayı okunmalı. Birincisi: yalnız ödünç yoluna kapasite eklemek istenmesine karşın dağıtılan modül dosyası 7’den 21’e çıktı. Bildirim ve ücretlendirme kodunun üç kopyası, ihtiyaç duyulmadığı hâlde ayakta duruyor. Ölçekleme birimi kod tabanının tamamıdır; darboğazın hangi modülde olduğu bu birimi küçültmez.

İkincisi, koşumun kendisi: üç kopya da aynı kitabı ödünç verdi. Her kopya kendi veri.json dosyasını taşıdığı için üçü de kitabı rafta gördü. Gerçek bir dağıtımda durum ortak bir depoya taşınır ve bu üçleme ortadan kalkar — ama o zaman da bütün kopyalar tek bir depoya yüklenir. Tek parçanın ölçek sınırı buradadır: süreç çoğaltılabilir, durum çoğaltılamaz.

Değiştirme tarafında da bir sınır var. Dağıtım birimi bir tane olduğu için, ücret oranındaki tek satırlık bir değişiklik de katalog ile üyeliği yeniden yayına çıkarır. Ölçüde bunun karşılığı şudur: dokunulan dosya bir, yeniden dağıtılan modül yedi.

Üç Sütun

Bu kursun her dersi aynı işi iki gerçekleştirmede karşılaştırır ve üç sütunu doldurur.

Tek parça
Ucuzlattığı Yerel çağrı, ortak işlem sınırı, tek derleme, tek dağıtım adımı
Pahalılaştırdığı Ölçekleme birimi tüm kod tabanı; bir satırlık değişiklik yedi modülü yeniden yayınlar
Doğan arıza kipi Bir modüldeki bellek sızıntısı ya da sonsuz döngü bütün alanları birlikte durdurur

Üçüncü satır ölçülmedi, çünkü tek süreçte bunu ölçmenin yolu süreci çökertmektir. Kayıt olarak bırakılıyor: dördüncü derste aynı arıza ayrı süreçlerde denendiğinde karşılaştırılacak.

Özet

  • Kütüphane ödünç sistemi tek parça olarak kuruldu: 7 modül, 10 ithal kenarı, 1 süreç, 1 dağıtım birimi; ödünç iş akışı uçtan uca koşuyor.
  • İş akışındaki 6 modüller arası çağrının hepsi yerel; ağ atlaması sıfır, başlatma adımı bir.
  • Tek işlem sınırı ve yerel çağrı, katalog ile üyelik arasındaki tutarlılığı ek bir düzen gerektirmeden sağlıyor.
  • Ölçek sınırı çoğaltmada görünür: yalnız ödünç yoluna kapasite eklemek 7 modülü 21 dosyaya çıkardı, çünkü ölçekleme birimi kod tabanının tamamıdır.
  • Tek dağıtım birimi, tek satırlık bir ücret değişikliğinde bile bütün modülleri yeniden yayına çıkarır.

Sonraki Adım

Ölçülen sayılardan biri henüz sorgulanmadı: odunc.mjs dört modülü de doğrudan ithal ediyor, ama bunu engelleyen bir şey yok — bildirim.mjs de katalog.mjs dosyasını ithal edebilir, katalog da üyeliğin içine uzanabilir. Tek parçanın kod içinde bir sınırı yoktur; ithal grafiği kimse bakmazsa tamamlanmış bir grafiğe doğru büyür. Sonraki ders bu grafiği ölçer, bir modülün başkasının içine uzandığı sınır ihlallerini sayar ve sınırın kod içinde bir kuralla korunduğu düzeni kurar: süreç hâlâ tek, dağıtım birimi hâlâ tek, ama ithal kenarları artık serbest değil.

İ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