İçeriğe geç
academia.sh

Ders 14 / 21

Çalıştırma Seçenekleri

Konteynerin dışarıdan aldığı iki ayar: kaynak sınırı aşıldığında bekletme, öldürme ve azaltma kararlarının gecikme, kaybedilen iş ve yeniden başlatma cinsinden bedeli, çok sıkı sınırın kendi işine ve çok gevşek sınırın komşu konteynere yansıması, ortam değişkenlerinin imajın dışında bıraktığı fark ve kaç değişkenin tanımsız bırakıldığında servisi düşürdüğü.

İçindekiler

Bir önceki konu çıktıyı durur hâlde ölçtü: kaç katman, kaç bayt, hangi özet, hangi imza. O ölçümlerin tamamı bir dosya kümesi üzerineydi ve o dosya kümesi hiç çalışmıyordu. Çalışmaya başladığı anda ölçülen şey değişir. Yalıtımın bir bölümü sürer — dosya sistemi hâlâ imajın dediğidir, kütüphane sürümü hâlâ donmuştur. Bir bölümü ise düşer: işlemci, bellek ve ayar artık imajın içinden değil, dışından gelir.

Bu ders düşen bölümün ilk ikisini sayar: konteynere verilen kaynak sınırı ve ortam değişkenleri. İkisinin ortak yanı şudur — hiçbiri imajın içinde yazmaz. Aynı imaj, iki farklı sınırla iki farklı davranış gösterir; aynı imaj, eksik bir değişkenle hiç kalkmaz. Yalıtım bütçesi açısından bu, imajın kapatamadığı bir açıktır ve büyüklüğü ölçülebilir.

Ölçümler kurgu bir bölgesel ölçüm ağı üzerinden yapılıyor: su sayaçlarından okuma toplayan, doğrulayan, faturaya çeviren ve saha ekiplerine iş emri açan bir yazılım. Sistem kurgudur, sayılar aşağıdaki koşumdan gelir.

Sınır Aşıldığında Verilen Karar

Bir kaynak sınırı, aşıldığında bir şey yapılmasını gerektirir. Sınırın kendisi bir sayıdır; karar o sayıya çarpıldığında verilir ve üç seçenek vardır: işi bekletmek, konteyneri öldürmek, işi azaltmak. Aşağıdaki koşum bir pay dağıtıcı modelidir — makinenin dönem başına dağıttığı işlemci payını iki konteyner arasında bölüştürür ve üç kararın bedelini ayrı ayrı sayar.

  • CZ1 — Ölçüm ağının dört servisi tek makinede, dört ayrı konteyner olarak koşuyor.
  • CZ2 — Makinenin kapasitesi dönem başına 100 pay olarak normalize edildi; pay ve dönem soyut birimlerdir, saniye ya da çekirdek değildir.
  • CZ3 — Doğrulayıcı her dönem 40–50 pay istiyor; gecelik toplu iş 60. dönemde başlayıp 140. dönemde bitiyor ve o pencerede 60–80 pay istiyor.
  • CZ4 — Öldürme kararı biriken iş 300 birimi aştığında veriliyor; yeniden başlatma 5 dönem sürüyor ve o dönemlerde gelen iş kaybediliyor.
  • CZ5 — Azaltma kararı, sınırı aşan işin 0,6’sını tutup kalanını düşürüyor.
// calisma/pay-dagitici.mjs — sinir asildiginda verilen kararin bedeli.
// MODELDIR: donem soyut bir adim, pay soyut bir birim; talep tohumlu bir uretecten cekiliyor.
const DONEM = 200, MAKINE = 100;                 // CZ2: makine kapasitesi 100 pay/donem
const BAS = 60, BIT = 140, TOHUM = 20260801;     // CZ3: gecelik isin penceresi
const KUYRUK_SINIR = 300, YENIDEN = 5;           // CZ4: oldurme esigi ve yeniden baslatma
const AZALT = 0.6;                               // CZ5: azaltmada tutulan pay

const uretec = (t) => { let s = t % 2147483647; return () => (s = (s * 48271) % 2147483647) / 2147483647; };
const talep = (r, t) => ({
  dogrulayici: 40 + Math.round(r() * 10),
  gecelik: t >= BAS && t < BIT ? 60 + Math.round(r() * 20) : 4,
});

function kosum({ karar, sinir }) {
  const r = uretec(TOHUM);
  const ad = ["dogrulayici", "gecelik"];
  const d = Object.fromEntries(ad.map((a) => [a, { kuyruk: 0, tamam: 0, kayip: 0, bekleme: 0, oldu: 0, kapali: 0, kendi: 0, makine: 0 }]));

  for (let t = 0; t < DONEM; t += 1) {
    const gelen = talep(r, t), istek = {}, pay = {};
    for (const a of ad) {
      if (d[a].kapali > 0) { d[a].kapali -= 1; d[a].kayip += gelen[a]; istek[a] = 0; pay[a] = 0; continue; }
      istek[a] = gelen[a] + d[a].kuyruk;
      pay[a] = Math.min(istek[a], sinir[a]);          // kendi sinirinda kirpma
      d[a].kendi += istek[a] - pay[a];
    }
    const toplam = pay.dogrulayici + pay.gecelik;
    for (const a of ad) {
      const verilen = toplam > MAKINE ? Math.floor((MAKINE * pay[a]) / toplam) : pay[a];
      d[a].makine += pay[a] - verilen;                // makinede kirpma: komsuya yansiyan kisim
      d[a].tamam += verilen;
      if (d[a].kapali > 0) continue;
      let artan = istek[a] - verilen;
      if (karar === "azaltma") { const tut = Math.round(artan * AZALT); d[a].kayip += artan - tut; artan = tut; }
      d[a].kuyruk = artan;
      if (karar === "oldurme" && d[a].kuyruk > KUYRUK_SINIR) {
        d[a].oldu += 1; d[a].kayip += d[a].kuyruk; d[a].kuyruk = 0; d[a].kapali = YENIDEN;
      }
      d[a].bekleme += d[a].kuyruk;
    }
  }
  return d;
}

const b = (x, n = 2) => x.toFixed(n);
const SINIR = { dogrulayici: 50, gecelik: 60 };
console.log(`model: ${DONEM} donem, makine ${MAKINE} pay/donem, sinirlar ${SINIR.dogrulayici}+${SINIR.gecelik}=` +
  `${SINIR.dogrulayici + SINIR.gecelik} (asiri taahhut), gecelik is ${BAS}-${BIT}. donem, tohum ${TOHUM}`);
console.log(`\n${"karar".padEnd(11)}${"is".padEnd(13)}${"tamam".padStart(9)}${"kayip".padStart(8)}` +
  `${"ort. bekleme".padStart(14)}${"oldurme".padStart(9)}${"kapali donem".padStart(14)}`);
const kar = {};
for (const karar of ["bekletme", "oldurme", "azaltma"]) {
  const k = kosum({ karar, sinir: SINIR });
  kar[karar] = k;
  for (const a of ["dogrulayici", "gecelik"])
    console.log(`${karar.padEnd(11)}${a.padEnd(13)}${String(k[a].tamam).padStart(9)}${String(k[a].kayip).padStart(8)}` +
      `${b(k[a].bekleme / k[a].tamam).padStart(14)}${String(k[a].oldu).padStart(9)}${String(k[a].oldu * YENIDEN).padStart(14)}`);
}
console.log(`\n${"gecelik sinir".padEnd(15)}${"gecelik tamam".padStart(15)}${"gecelik bekleme".padStart(17)}` +
  `${"dogrulayici tamam".padStart(19)}${"dogrulayici bekleme".padStart(21)}${"makine kirpmasi".padStart(17)}`);
for (const s of [20, 40, 60, 80, 100]) {
  const k = kosum({ karar: "bekletme", sinir: { dogrulayici: 50, gecelik: s } });
  console.log(`${String(s).padEnd(15)}${String(k.gecelik.tamam).padStart(15)}${b(k.gecelik.bekleme / k.gecelik.tamam).padStart(17)}` +
    `${String(k.dogrulayici.tamam).padStart(19)}${b(k.dogrulayici.bekleme / k.dogrulayici.tamam).padStart(21)}` +
    `${String(k.dogrulayici.makine + k.gecelik.makine).padStart(17)}`);
}
model: 200 donem, makine 100 pay/donem, sinirlar 50+60=110 (asiri taahhut), gecelik is 60-140. donem, tohum 20260801

karar      is               tamam   kayip  ort. bekleme  oldurme  kapali donem
bekletme   dogrulayici       9012       0          0.12        0             0
bekletme   gecelik           6092       0         10.54        0             0
oldurme    dogrulayici       9012       0          0.06        0             0
oldurme    gecelik           4124    1968          2.15        3            15
azaltma    dogrulayici       8891     121          0.02        0             0
azaltma    gecelik           4986    1106          0.33        0             0

gecelik sinir    gecelik tamam  gecelik bekleme  dogrulayici tamam  dogrulayici bekleme  makine kirpmasi
20                        3040           122.72               9012                 0.00                0
40                        5840            30.08               9012                 0.00                0
60                        6092            10.54               9012                 0.12             1033
80                        6092             5.48               8625                 6.00             2817
100                       6092             2.49               8306                10.38             4215

Bu sayılar ölçüm sınıfındadır ve tohuma bağlıdır.

Üç Kararın Üç Ayrı Bedeli

Gecelik işin toplam talebi her üç koşumda da 6092 birimdir; kararlar bu 6092’yi farklı yerlere dağıtır ve toplam korunur.

Bekletme hiçbir iş kaybetmez: kayıp 0, tamamlanan 6092. Bedeli tamamen zamandır — tamamlanan her birim ortalama 10,54 dönem kuyrukta bekliyor. Doğrulayıcının beklemesi aynı koşumda 0,12 dönem; yani bekletme kararının bedelini neredeyse tümüyle sınıra dayanan işin kendisi ödüyor.

Öldürme bekleme sayısını 2,15 döneme indiriyor, çünkü kuyruk 300 birimi aştığında siliniyor. Bu indirim bir muhasebe hilesidir: kuyruk kısaldı çünkü içindeki iş atıldı. Bedel iki kalemde görünür — 1968 birim kayıp (talebin yüzde 32’si) ve 3 öldürme, her biri 5 dönem kapalı kalmayla, toplam 15 dönem hiç iş yapılmayan süre. Tamamlanan iş 6092’den 4124’e düşüyor.

Azaltma ikisinin ortasındadır ve sayısı en tuhaf olanıdır: bekleme 0,33 döneme iniyor, kayıp 1106 birimde kalıyor, öldürme yok. Sınırı aşan işin yüzde 40’ı düşürüldüğü için kuyruk hiç büyümüyor. Ama azaltma koşumunda dikkat çeken sayı gecelik işin değil, doğrulayıcının kaybıdır: 121 birim. Doğrulayıcı hiçbir zaman kendi 50 pay sınırına dayanmadı; onu kırpan şey makinedir. Ve azaltma kararı bütün konteynerlerde geçerli olduğu için, makinenin kırptığı işin yüzde 40’ı doğrulayıcı için de düşürülüyor. Karar tek konteynerde verilir, sonucu iki konteynerde görünür.

Seçim veri sınıfına bağlıdır. Faturaya girecek bir okuma için 1968 birimlik kayıp kabul edilemez; bekletme tek seçenektir ve 10,54 dönemlik gecikme ödenir. Bir eğilim grafiğini besleyen özet için azaltma en ucuz karardır: 1106 birim kayba karşılık bekleme 10,54’ten 0,33’e iner. Öldürme kimsenin seçtiği karar değildir; bellek sınırında verilen karar budur ve seçilmemiş olması bedelini değiştirmez.

Sınır Taraması: Sıkı Uç, Gevşek Uç

İkinci tablo aynı işi beş ayrı sınırla koşturuyor ve sınır seçiminin iki ayrı yöne bedel yüklediğini gösteriyor.

Sıkı uçta bedel konteynerin kendi işindedir. 20 pay sınırında gecelik iş 200 dönem boyunca yalnız 3040 birim bitiriyor — talebin yarısı bile değil; koşum bittiğinde 3052 birim hâlâ kuyrukta duruyor ve birim başına bekleme 122,72 döneme çıkıyor. Bu sınırda gecelik iş bitmiyor, sadece ertesi güne taşıyor. Buna karşılık komşu tamamen dokunulmamış: doğrulayıcının beklemesi 0,00, makine kırpması 0. Sıkı sınır yalıtımı tam kurar ve faturayı kendi konteynerine keser.

Gevşek uçta bedel yer değiştirir. Sınır 60’ta gecelik iş zaten tamamını bitiriyor: 6092. Sınırı 80’e, sonra 100’e çıkarmak bitirilen işi hiç artırmıyor — üç satırda da 6092. Değişen tek şey beklemedir: 10,54 → 5,48 → 2,49. Yani gevşetmenin kazancı yalnız gecikmedir ve bu kazancın parasını komşu öder. Doğrulayıcının tamamladığı iş 9012’den 8306’ya iniyor (706 birim eksik) ve beklemesi 0,12’den 10,38 döneme çıkıyor. Makine kırpması 0’dan 4215 birime tırmanıyor.

Bu, kaynak sınırının en yanlış anlaşılan yanıdır ve tabloda çıplak duruyor: sınır bir tavandır, bir garanti değildir. 50 ve 100 sınırlarının toplamı 150’dir, makine 100 verebilir. Toplam aşıldığı anda kim ne kadar alacağına karar veren şey konteynerin sınırı değil, dağıtıcıdır — ve o dağıtıcı konteynerin dışındadır. Yalıtımın delindiği yer burasıdır: ad alanı süreçleri ayırır, sınır tavanı koyar, ama makinenin kendisi paylaşılmaya devam eder ve aşırı taahhütte komşunun gecikmesi 86 katına çıkabilir.

Ortamda Kalan Fark

İkinci ayar kalemi ortam değişkenleridir. İmaj dosya sistemi farkını içine aldı: kurulu paketler, kütüphane sürümleri, dosya yolları donduruldu. Donmayan şey aynı imajın farklı yerlerde farklı davranmasını sağlayan değerlerdir — veri ucu, parola, tarife tablosu, saat dilimi. Bunlar imajın dışındadır çünkü imaja girselerdi imaj her yer için ayrı olurdu.

  • CZ6 — Dört servisin ortam değişkeni envanteri kurgudur; adlar ve zorunluluk sınıfları model verisidir. Eksik tanım kontrolü gerçekten koşuyor.
// calisma/ortam-degiskenleri.mjs — imaja alinamayan farkin sayimi.
// MODELDIR: dort servisin degisken envanteri kurgudur; eksik tanim kontrolu gercekten kosuyor.
const SERVIS = {                                                        // CZ6: kurgu envanter
  toplayici: { zorunlu: ["SAYAC_AG_UCU", "OKUMA_KUYRUGU", "TOPLAMA_ARALIGI"],
    varsayilanli: { GUNLUK_DUZEYI: "bilgi", ES_ZAMAN: "4" } },
  dogrulayici: { zorunlu: ["OKUMA_KUYRUGU", "KURAL_SURUMU", "VERI_UCU", "VERI_PAROLASI"],
    varsayilanli: { SAPMA_ESIGI: "0.15", GUNLUK_DUZEYI: "bilgi" } },
  faturalama: { zorunlu: ["VERI_UCU", "VERI_PAROLASI", "TARIFE_TABLOSU", "SAAT_DILIMI"],
    varsayilanli: { YUVARLAMA: "2", GUNLUK_DUZEYI: "bilgi" } },
  isemri: { zorunlu: ["VERI_UCU", "VERI_PAROLASI", "SAHA_UCU"],
    varsayilanli: { YENIDEN_DENEME: "3" } },
};

function baslat(ad, ortam) {                       // eksik zorunlu tanim varsa servis kalkmaz
  const s = SERVIS[ad], eksik = s.zorunlu.filter((k) => ortam[k] === undefined);
  if (eksik.length) throw new Error(`${ad}: tanimsiz ${eksik.join(", ")}`);
  return { ...s.varsayilanli, ...Object.fromEntries(s.zorunlu.map((k) => [k, ortam[k]])) };
}

const adlar = Object.keys(SERVIS);
const zorunlu = new Set(adlar.flatMap((a) => SERVIS[a].zorunlu));
const varsayilanli = new Set(adlar.flatMap((a) => Object.keys(SERVIS[a].varsayilanli)));
const tum = new Set([...zorunlu, ...varsayilanli]);
const tanim = adlar.reduce((t, a) => t + SERVIS[a].zorunlu.length + Object.keys(SERVIS[a].varsayilanli).length, 0);
const yalnizVars = [...varsayilanli].filter((k) => !zorunlu.has(k)).length;
console.log(`model: ${adlar.length} servis, ${tum.size} farkli degisken adi, ${tanim} tanim satiri; ` +
  `zorunlu ${zorunlu.size} ad, yalniz varsayilanli ${yalnizVars} ad`);

const dolu = Object.fromEntries([...tum].map((k) => [k, "deger"]));
const sayim = (ortam) => adlar.filter((a) => { try { baslat(a, ortam); return false; } catch { return true; } });
console.log(`bos ortamla kalkmayan servis: ${sayim({}).length}/${adlar.length}; ` +
  `dolu ortamla kalkmayan: ${sayim(dolu).length}/${adlar.length}`);
try { baslat("faturalama", { VERI_UCU: "x", VERI_PAROLASI: "y" }); } catch (e) { console.log(`ornek: ${e.message}`); }

console.log(`\n${"degisken".padEnd(17)}${"kac serviste".padStart(14)}${"cikarilinca dusen".padStart(19)}${"sinif".padStart(15)}`);
let dusen = 0, kirilgan = 0;
for (const k of [...tum].sort()) {
  const kac = adlar.filter((a) => SERVIS[a].zorunlu.includes(k) || k in SERVIS[a].varsayilanli).length;
  const d = sayim(Object.fromEntries(Object.entries(dolu).filter(([x]) => x !== k))).length;
  dusen += d;
  if (d > 0) kirilgan += 1;
  console.log(`${k.padEnd(17)}${String(kac).padStart(14)}${String(d).padStart(19)}${(zorunlu.has(k) ? "zorunlu" : "varsayilanli").padStart(15)}`);
}
console.log(`\ntoplam: ${tum.size} degiskenin ${kirilgan} tanesi tek basina en az bir servisi dusuruyor; ` +
  `tek tek cikarma toplaminda ${dusen} servis-dusmesi. Cikti degismedi, ${tum.size} ad ortamda kaldi.`);
model: 4 servis, 14 farkli degisken adi, 21 tanim satiri; zorunlu 9 ad, yalniz varsayilanli 5 ad
bos ortamla kalkmayan servis: 4/4; dolu ortamla kalkmayan: 0/4
ornek: faturalama: tanimsiz TARIFE_TABLOSU, SAAT_DILIMI

degisken           kac serviste  cikarilinca dusen          sinif
ES_ZAMAN                      1                  0   varsayilanli
GUNLUK_DUZEYI                 3                  0   varsayilanli
KURAL_SURUMU                  1                  1        zorunlu
OKUMA_KUYRUGU                 2                  2        zorunlu
SAAT_DILIMI                   1                  1        zorunlu
SAHA_UCU                      1                  1        zorunlu
SAPMA_ESIGI                   1                  0   varsayilanli
SAYAC_AG_UCU                  1                  1        zorunlu
TARIFE_TABLOSU                1                  1        zorunlu
TOPLAMA_ARALIGI               1                  1        zorunlu
VERI_PAROLASI                 3                  3        zorunlu
VERI_UCU                      3                  3        zorunlu
YENIDEN_DENEME                1                  0   varsayilanli
YUVARLAMA                     1                  0   varsayilanli

toplam: 14 degiskenin 9 tanesi tek basina en az bir servisi dusuruyor; tek tek cikarma toplaminda 14 servis-dusmesi. Cikti degismedi, 14 ad ortamda kaldi.

Ortamdan çıkarılamayan farkın sayısı burada okunuyor: 14 değişken adı, 21 tanım satırı. Dosya yolları ve kütüphane sürümleri çıktının içine alındı, bu 14 ad alınamadı. Dokuzu zorunludur ve tanımsız bırakıldığında servis hiç kalkmaz — boş ortamla dört servisin dördü de düşüyor. Beşinin varsayılanı vardır ve çıkarıldıklarında hiçbir servis düşmez; onlar farkı taşır ama kırılganlık üretmez.

Sağ sütun kırılganlığın nerede yoğunlaştığını gösteriyor. VERI_UCU ve VERI_PAROLASI üç serviste birden zorunludur; tek bir tanım unutulduğunda üç konteyner birden kalkmaz. Bu ikisi 14 adın yüzde 14’üdür ama tek tek çıkarma toplamındaki 14 servis-düşmesinin 6’sını, yani yüzde 43’ünü tek başına üretir. SAAT_DILIMI ise bir başka sınıftır: imaj saat dilimi verisini içine aldı ama hangi saat diliminde çalışılacağı kararını alamadı, çünkü o karar imaja değil koşuma aittir. Faturalama için bu tek değişken bir gün sınırının nereye düştüğünü belirler.

Yalıtımın bedeli burada yazım cinsindendir: 21 tanım satırı, her koşum yerinde bir kez. Bedelin karşılığı, aynı imajın dört ayrı yerde dört ayrı davranışla koşabilmesidir.

Özet

  • Kaynak sınırı aşıldığında üç karardan biri verilir ve üçü de aynı 6092 birimlik işi farklı yerlere dağıtır: bekletme kaybı 0 tutar ama birim başına 10,54 dönem gecikme yükler; öldürme gecikmeyi 2,15 döneme indirir ama 1968 birim kaybettirir ve 15 dönem kapalı geçirir; azaltma 1106 birim kayba karşılık gecikmeyi 0,33 döneme indirir. Öldürmenin kısa kuyruğu bir kazanç değildir: kuyruk kısaldı çünkü içindeki iş silindi ve konteyner 3 kez yeniden başladı.
  • Azaltma kararının sonucu iki konteynerde görünür: gecelik iş 1106 birim kaybederken hiç sınıra dayanmamış doğrulayıcı da 121 birim kaybediyor, çünkü kırpan şey konteynerin sınırı değil makinedir.
  • Sıkı sınır faturayı kendi konteynerine keser: 20 pay sınırında gecelik iş 200 dönemde ancak 3040 birim bitiriyor, 3052 birim kuyrukta kalıyor, bekleme 122,72 döneme çıkıyor; komşunun beklemesi 0,00’da kalıyor.
  • Gevşek sınır faturayı komşuya keser: sınırı 60’tan 100’e çıkarmak gecelik işin bitirdiği 6092 birimi hiç artırmıyor, yalnız beklemesini 2,49’a indiriyor; karşılığında doğrulayıcı 706 birim eksik iş bitiriyor ve beklemesi 0,12’den 10,38 döneme çıkıyor.
  • Yalıtımın delindiği yer: sınır bir tavandır, bir garanti değildir. Sınırların toplamı makine kapasitesini aştığında dağıtım kararı konteynerin dışında verilir; makine kırpması 0’dan 4215 birime çıkıyor.
  • İmaj dosya sistemi farkını içine aldı, 14 değişken adını alamadı. Dokuzu zorunludur, boş ortamla dört servisin dördü de kalkmaz; ikisi üç serviste birden zorunlu olduğu için tek tek çıkarma toplamındaki 14 servis-düşmesinin 6’sını üretir.

Sonraki Adım

Kaynak sınırı da ortam değişkeni de konteynerin dışından verilen ayarlardı; ikisi de konteyner başlamadan önce belliydi ve konteyner çalışırken değişmedi. Oysa çalışan bir konteyner boş durmaz: günlük yazar, önbellek doldurur, yarım kalmış toplu işin durumunu bir yere kaydeder, indirdiği dosyayı bir dizine koyar. Bu veri imajda yoktu, ortamdan da gelmedi — konteynerin kendisi üretti. Peki nereye yazıldı ve konteyner durduğunda ona ne oluyor? Sonraki ders yazılabilir katmanı gerçek dizinlerle kurar ve şunu sayar: bir koşum boyunca kaç bayt birikiyor, konteyner silindiğinde kaç kayıt gidiyor, bir baytlık değişiklik neden bir baytlık yer tutmuyor ve bu kaybın hangi veri sınıfında kabul edilebilir olduğu.

İ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