İçeriğe geç
academia.sh

Ders 15 / 21

Geçici Dosya Sistemi

Konteynerin çalışırken ürettiği verinin akıbeti: yazılabilir katmanın gerçek dizinlerle kurulması, otuz gecelik bir koşum boyunca biriken baytın veri sınıflarına dağılımı, tek baytlık bir değişikliğin kopyala-yukarı ile tam dosya kadar yer tutması, konteyner silindiğinde giden kayıt sayısı ve kaybın hangi veri sınıfında kabul edilebilir olduğu.

İçindekiler

Önceki ders konteynere dışarıdan verilen iki ayarı ölçtü: kaynak sınırı ve ortam değişkenleri. İkisi de konteyner başlamadan önce belliydi. Oysa çalışan bir konteyner boş durmaz. Doğrulayıcı her okuma için bir günlük satırı yazar, hesapladığı tarife karşılığını önbelleğe koyar, gecelik koşumun nereye geldiğini bir yere kaydeder, operatörün elle girdiği düzeltmeyi bir dosyaya ekler. Bu verinin hiçbiri imajda yoktu ve ortamdan da gelmedi — konteynerin kendisi üretti.

Sorusu şudur: nereye yazıldı, ne kadar yer tuttu ve konteyner ortadan kalktığında ne oldu. Yanıt yalıtımın en sessiz bedelidir; çünkü hiçbir hata vermez, hiçbir sınır aşılmaz. Veri yazılır, okunur, bir süre kullanılır, sonra bir gün yoktur.

Yazılabilir Katman

İmaj salt okunurdur ve bu bir tercih değil, katman modelinin sonucudur: katmanlar içerik özetiyle adreslenir, içerik değişirse özet değişir, özet değişirse katman başka bir katman olur. Konteyner yazmaya ihtiyaç duyduğunda bu yığının üstüne yazılabilir bir katman eklenir. Okuma üstten alta doğru çözülür; yazma her zaman en üste düşer.

Aşağıdaki koşum bu düzeni gerçek dizinlerle kuruyor. Birleşim dosya sistemi bir modeldir — okuma çözümü ve kopyala-yukarı kuralı elle yazılmıştır — ama dizinler, dosyalar ve bayt sayıları gerçektir; hepsi diske yazılıp node:fs ile ölçülüyor.

  • CZ7 — İmajın alt katmanları dört dosyadan oluşuyor: uygulama kodu, kural kümesi, tarife tablosu, sayaç listesi. Boyutlar kurgudur, dosyalar gerçekten yazılıyor.
  • CZ8 — Gecelik koşum 480 sayaç okuması işliyor.
  • CZ9 — Her okuma bir günlük satırı; her dördüncü okuma bir önbellek yazımı; her altmışıncı okumada ilerleme yazımı; her kırkıncıda bir operatör düzeltmesi üretiyor.
  • CZ10 — Her gecenin sonunda tarife tablosunda tek baytlık bir değişiklik yapılıyor.
  • CZ11 — Konteyner 30 gece boyunca hiç yeniden başlatılmadan koşuyor, sonunda siliniyor. Silme yazılabilir katmanı kaldırıyor, alt katmanlara dokunmuyor.
// calisma/gecici-katman.mjs — yazilabilir katmanda birikenin ve konteyner silinince gidenin sayimi.
// MODEL: birlesim dosya sistemi node ile modelleniyor; dizinler, dosyalar ve bayt sayilari gercektir.
import { mkdirSync, writeFileSync, appendFileSync, copyFileSync, existsSync, statSync,
  readdirSync, rmSync } from "node:fs";
import { join, dirname } from "node:path";

const ALT = "kok/alt", UST = "kok/ust";                 // salt okunur imaj katmanlari / yazilabilir katman
const OKUMA = 480, GECE = 30, TOHUM = 20260801;         // CZ8: gecelik kosum, CZ11: konteynerin omru
rmSync("kok", { recursive: true, force: true });

const kur = (kok, yol, icerik) => { mkdirSync(join(kok, dirname(yol)), { recursive: true });
  writeFileSync(join(kok, yol), icerik); };
for (const [yol, n] of [["uygulama/dogrulayici.js", 12288], ["uygulama/kurallar.json", 49152],
  ["ayar/tarife.csv", 98304], ["veri/sayac-listesi.csv", 204800]]) kur(ALT, yol, "x".repeat(n));

let kopyaBayt = 0, kopyaSayi = 0;
function ustEkle(yol, satir) {                          // ust katmanda yoksa ve altta varsa: kopyala-yukari
  const u = join(UST, yol);
  if (!existsSync(u) && existsSync(join(ALT, yol))) {
    mkdirSync(dirname(u), { recursive: true });
    copyFileSync(join(ALT, yol), u);
    kopyaBayt += statSync(u).size; kopyaSayi += 1;
  }
  mkdirSync(dirname(u), { recursive: true });
  appendFileSync(u, satir);
}

let s = TOHUM % 2147483647;
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;
const sayim = { gunluk: 0, onbellek: 0, durum: 0, kullanici: 0, ayar: 0 };
function gecelikKosum() {
  for (let i = 1; i <= OKUMA; i += 1) {
    const deger = (100 + rast() * 900).toFixed(3);
    ustEkle("gunluk/dogrulama.log", `okuma ${i} sayac ${1000 + i} deger ${deger} durum tamam\n`);
    sayim.gunluk += 1;
    if (i % 4 === 0) { kur(UST, `onbellek/tarife-${i / 4}.json`, `{"sayac":${1000 + i},"birim":"${deger}"}`); sayim.onbellek += 1; }
    if (i % 60 === 0) { kur(UST, "durum/ilerleme.json", `{"islenen":${i},"kalan":${OKUMA - i}}`); sayim.durum += 1; }
    if (i % 40 === 0) { ustEkle("veri/duzeltmeler.csv", `${1000 + i},operator,${deger}\n`); sayim.kullanici += 1; }
  }
  ustEkle("ayar/tarife.csv", "#");                      // tek baytlik degisiklik, tam dosya kopyalaniyor
  sayim.ayar += 1;
}

const olc = (kok, on = "") => { let b = 0, n = 0;
  const gez = (d) => { for (const e of readdirSync(d, { withFileTypes: true })) {
    const p = join(d, e.name);
    if (e.isDirectory()) gez(p); else if (p.includes(on)) { b += statSync(p).size; n += 1; } } };
  if (existsSync(kok)) gez(kok); return { bayt: b, dosya: n }; };

const a = olc(ALT);
const ON = { gunluk: "gunluk/", onbellek: "onbellek/", durum: "durum/", kullanici: "veri/", ayar: "ayar/" };
console.log(`model: ${OKUMA} okuma/gece, ${GECE} gece (tohum ${TOHUM}); alt katmanlar ` +
  `${a.bayt} bayt / ${a.dosya} dosya, hepsi salt okunur`);
console.log(`\n${"gece".padEnd(6)}${"toplam bayt".padStart(12)}${"dosya".padStart(7)}` +
  Object.keys(ON).map((k) => k.padStart(11)).join("") + `${"alt katmana orani".padStart(19)}`);
for (let g = 1; g <= GECE; g += 1) {
  gecelikKosum();
  if (![1, 2, 7, 30].includes(g)) continue;
  const u = olc(UST);
  console.log(`${String(g).padEnd(6)}${String(u.bayt).padStart(12)}${String(u.dosya).padStart(7)}` +
    Object.values(ON).map((o) => String(olc(UST, o).bayt).padStart(11)).join("") +
    `${(u.bayt / a.bayt).toFixed(2).padStart(19)}`);
}

const u = olc(UST);
console.log(`\nkopyala-yukari ${kopyaSayi} dosyada ${kopyaBayt} bayt; katmandaki payi ilk gece ` +
  `%${((100 * (kopyaBayt + 1)) / 124886).toFixed(1)}, otuzuncu gece %${((100 * kopyaBayt) / u.bayt).toFixed(1)}`);
rmSync(UST, { recursive: true, force: true });          // konteyner siliniyor
console.log(`konteyner silindi: kalan ${olc(ALT).bayt} bayt / ${olc(ALT).dosya} dosya (alt katmanlar), ` +
  `giden ${u.bayt} bayt / ${u.dosya} dosya`);
console.log(`giden kayit: ${sayim.gunluk} gunluk satiri, ${sayim.onbellek} onbellek yazimi, ` +
  `${sayim.durum} ilerleme yazimi, ${sayim.kullanici} operator duzeltmesi, ${sayim.ayar} ayar degisikligi`);
console.log(`iki konteyner ayni imajdan kossaydi: paylasimli ${a.bayt + 2 * u.bayt} bayt, ` +
  `paylasimsiz ${2 * (a.bayt + u.bayt)} bayt; alt katman paylasimi ${a.bayt} bayt kazandiriyor`);
rmSync("kok", { recursive: true, force: true });
model: 480 okuma/gece, 30 gece (tohum 20260801); alt katmanlar 364544 bayt / 4 dosya, hepsi salt okunur

gece   toplam bayt  dosya     gunluk   onbellek      durum  kullanici       ayar  alt katmana orani
1           124886    124      22452       3840         25        264      98305               0.34
2           147603    124      44904       3840         25        528      98306               0.40
7           261188    124     157164       3840         25       1848      98311               0.72
30          783679    124     673560       3840         25       7920      98334               2.15

kopyala-yukari 1 dosyada 98304 bayt; katmandaki payi ilk gece %78.7, otuzuncu gece %12.5
konteyner silindi: kalan 364544 bayt / 4 dosya (alt katmanlar), giden 783679 bayt / 124 dosya
giden kayit: 14400 gunluk satiri, 3600 onbellek yazimi, 240 ilerleme yazimi, 360 operator duzeltmesi, 30 ayar degisikligi
iki konteyner ayni imajdan kossaydi: paylasimli 1931902 bayt, paylasimsiz 2296446 bayt; alt katman paylasimi 364544 bayt kazandiriyor

Bu sayılar ölçüm sınıfındadır.

Kopyala-Yukarı: Bir Baytın 98.304 Bayt Etmesi

Birinci satır ilk bakışta yanlış görünüyor: ilk gecenin ayar sütunu 98.305 bayt. Değiştirilen şey tek bir bayttı.

Nedeni yazma kuralındadır. Yazılabilir katman alt katmandaki dosyayı değiştiremez, çünkü alt katman salt okunurdur ve özetiyle adreslenmiştir. Bir alt katman dosyasına yazılacaksa dosyanın tamamı önce üst katmana kopyalanır, sonra kopya değiştirilir. Buna kopyala-yukarı denir ve bedeli değişikliğin boyutuyla değil, dosyanın boyutuyla ölçülür.

İlk gecenin bileşimi bu yüzden çarpıktır. Koşumun ürettiği gerçek yeni veri 26.582 bayttır — katmanın toplamından kopyalanan 98.304 bayt düşüldüğünde kalan. Katmanda duran 124.886 bayt, yani 4,7 katı. Farkın tamamı tek bir kopyalanmış dosyadır ve o gece katmanın yüzde 78,7’sini kaplar. Yalıtımın bedeli burada nettir: dosyayı yerinde değiştirme hakkından vazgeçmenin karşılığı, tek baytlık bir değişiklik için 98.304 bayt yer ve o kadar kopyalama işidir.

Bedel bir kez ödenir. İkinci gecenin ayar sütunu 98.306’dır: dosya artık üst katmandadır, ikinci bayt onun sonuna yazılır ve hiçbir şey kopyalanmaz. Pratik sonuç şudur — yazılabilir katmanda hangi dosyaya ilk kez dokunulduğu, sonradan ne kadar yazıldığından daha pahalıdır.

Otuz Gecede Katmanın Şekli Değişiyor

Tablonun satırları aşağı inildikçe katmanın bileşimi tersine dönüyor. Otuzuncu gecede toplam 783.679 bayt; ayar sütunu hâlâ 98.334’te duruyor ve payı yüzde 78,7’den yüzde 12,5’e inmiş. Yerini alan gunluk sütunudur: 673.560 bayt, katmanın yüzde 86’sı. Günlük her gece 22.452 bayt eklediği için doğrusal büyüyor; kopyala-yukarı ise bir kez ödendiği için sabit kalıyor.

Sağ sütun asıl olguyu veriyor. Yazılabilir katmanın alt katmanlara oranı ilk gece 0,34 iken otuzuncu gecede 2,15’e çıkıyor. Otuz gece sonunda konteynerin diskte tuttuğu yerin üçte ikisinden fazlası imajın değil, koşumun ürettiği veridir. “Konteyner hafiftir” cümlesinin karşılığı burada bir sayıdır ve ilk gün doğru olan sayı otuzuncu gün yanlıştır.

İki sütun hiç büyümüyor ve nedenleri ayrıdır. onbellek 3.840 baytta sabit, çünkü aynı 120 tarife anahtarı her gece üzerine yazılıyor. durum 25 baytta sabit, çünkü ilerleme dosyası her yazımda üzerine yazılıyor — 240 yazımın yalnız sonuncusu duruyor. Dosya sayısının otuz gece boyunca 124’te kalması da bundandır: koşum yeni dosya açmıyor, var olanları büyütüyor.

Konteyner Silindiğinde Giden

Silme işleminden sonra iki sayı kalıyor: alt katmanlarda 364.544 bayt / 4 dosya duruyor, üst katmandan 783.679 bayt / 124 dosya gitti. Alt katman dokunulmamıştır — imaj neyse odur. Giden şey konteynerin kendi ürettiğidir ve kayıt cinsinden dökümü şudur: 14.400 günlük satırı, 3.600 önbellek yazımı, 240 ilerleme yazımı, 360 operatör düzeltmesi, 30 ayar değişikliği.

Kayıt sayılarıyla bayt sayıları arasındaki uçurum kendi başına bir bilgidir. Önbellek 3.600 kez yazıldı ama katmanda 120 dosya ve 3.840 bayt bıraktı: yazımların yüzde 97’si kendinden sonraki yazımla örtüldü. Yazılabilir katman bir günlük değil, bir anlık görüntüdür — ne yazıldığını değil, en son ne yazıldığını tutar. Bu yüzden silinen şey de yazılanların toplamı değil, son hâlleridir.

İlerleme satırı ayrıca okunmalıdır: katmanda 25 bayt duruyordu ama yazım sayısı 240. Gecelik koşumun nereye kadar geldiği bilgisi her seferinde üzerine yazıldı ve konteynerle birlikte gitti. Yeni bir konteyner o geceyi sıfırdan başlatır; 480 okumanın tamamı yeniden işlenir.

Kaybın hangi sınıfta kabul edilebilir olduğu ölçüm ağının işletme kuralıdır (CZ12) ve otuz gecelik sayılarla şu tabloyu verir:

Veri sınıfı Kayıt Bayt Kayıp kabul edilir mi Gerekçe
Günlük 14.400 satır 673.560 Koşullu Satırlar dışarı akıtılıyorsa evet; tek kopya konteynerdeyse arıza incelemesi de gider
Önbellek 3.600 yazım 3.840 Evet Yeniden üretilebilir; bedeli ilk isteklerin yavaşlaması
İş durumu 240 yazım 25 Hayır Yarım koşum baştan başlar, 480 okuma yeniden işlenir
Kullanıcı verisi 360 düzeltme 7.920 Hayır Yeniden üretilemez; operatörün elle girdiği tek kaynak
Ayar kopyası 30 değişiklik 98.334 Evet İmaja ya da ayar kaynağına geri döner

Tablonun okunma yönü terstir: kabul edilemez iki sınıf katmanın yalnız yüzde 1’ini, 7.945 baytı tutuyor. Kalan yüzde 99 yeniden üretilebilir veridir. Sorun büyüklük değil, karışım: 7.945 bayt kurtarılamaz veri, 775.734 bayt kurtarılabilir verinin içinde duruyor ve ikisi aynı anda siliniyor.

Ortamdan Çıkarılan Fark

Bu kaybın bir de karşı tarafı vardır ve kursun ölçüsü onu sormayı gerektiriyor: geçici katman ortamdan hangi farkı çıkarıyor.

Çıkardığı fark koşumlar arası artıktır. Her gece silinip yeniden yaratılan bir konteyner her gece aynı 364.544 baytlık, dört dosyalık başlangıçtan koşar; yazılabilir katmanı hiçbir zaman 124.886 baytı geçmez ve iki gecenin başlangıç durumu arasındaki fark tam olarak sıfır bayttır. Yarım kalmış bir günlük, şişmiş bir önbellek, bir önceki gecenin elle düzeltilmiş ayarı sonraki geceye taşınmaz. Bir konteynerin “durumsuz” sayılması bu ölçüdür: koşumlar arası fark sıfır.

Otuz gece yaşayan konteyner bu farkı çıkarmıyor. Otuzuncu geceye, katmanı alt katmanların 2,15 katına çıkmış olarak giriyor ve o gecenin davranışı önceki yirmi dokuz gecenin artığına bağlı. Sorunun aranacağı yer artık yalnız imaj değil, o katmanın içeriğidir — yani ortamdan çıkarıldığı sanılan fark geri gelmiştir.

Bedeli tablodaki iki satırdır. Koşumlar arası farkı sıfıra indiren şey, aynı anda 240 ilerleme yazımını ve 360 operatör düzeltmesini de siler. Durumsuzluk bedava değildir: koşum arası artığı sıfırlamanın karşılığı, artığın içindeki 7.945 baytlık kurtarılamaz veriyi de sıfırlamaktır. Sonraki dersin çözdüğü sorun tam olarak budur.

Yalıtımın Delindiği Yer

İki delik vardır ve biri kasıtlıdır.

Kasıtlı olan alt katman paylaşımıdır. Aynı imajdan iki konteyner koşarsa toplam disk 2.296.446 bayt değil, 1.931.902 bayttır; alt katmanların 364.544 baytı bir kez tutulur ve iki konteynere aynı anda görünür. Yalıtım burada bilerek delinmiştir, çünkü delik olmasa aynı içerik iki kez saklanırdı. Salt okunur olduğu için paylaşmanın davranışa etkisi yoktur — ama kaynak tektir.

İkinci delik kasıtsızdır ve adı yanlış anlaşılmadan gelir. Geçicilik bir dosya sistemi özelliği değil, bir yaşam döngüsü sözüdür. Yazılabilir katman bellekte değil, ana makinenin diskinde bir dizindir. Konteyner durdurulduğunda o dizin durmayı sürdürür; 783.679 bayt yerinde kalır ve konteyner yeniden başlatılırsa geri gelir. Veri ancak konteyner silindiğinde gider. Bunun iki sonucu vardır: durdurulmuş konteyner, gittiğini sandığı veriyi hâlâ diskte tutar; ve hiç silinmeyen bir konteyner, geçici sandığı katmanı süresiz büyütür — otuz gecede 2,15 katına.

Özet

  • Yazılabilir katman salt okunur alt katmanların üstüne eklenen ayrı bir birimdir; okuma üstten alta çözülür, yazma her zaman en üste düşer.
  • Kopyala-yukarı bedeli dosya boyutuyla ölçülür: tarife tablosundaki tek baytlık değişiklik 98.304 bayt kopyalattı ve ilk gece katmanın yüzde 78,7’sini kapladı. O gece üretilen gerçek yeni veri 26.582 bayt, katmanda duran 124.886 bayt. Bedel bir kez ödenir: ikinci gece kopyalama yok.
  • Otuz gecede katmanın şekli dönüyor: toplam 783.679 bayta çıkıyor, günlük payı yüzde 86’ya yükselirken kopyala-yukarı payı yüzde 12,5’e iniyor. Yazılabilir katmanın alt katmanlara oranı 0,34’ten 2,15’e çıkıyor.
  • Konteyner silindiğinde 14.400 günlük satırı, 3.600 önbellek yazımı, 240 ilerleme yazımı, 360 operatör düzeltmesi ve 30 ayar değişikliği gidiyor; alt katmanın 364.544 baytı kalıyor.
  • Kaybın kabul edilebilirliği veri sınıfına bağlıdır ve karışım tehlikelidir: kabul edilemez iki sınıf katmanın yalnız yüzde 1’ini, 7.945 baytı tutuyor ama kalan 775.734 baytla aynı anda siliniyor.
  • Yalıtımın delindiği yer: alt katman iki konteyner arasında paylaşılır ve 364.544 bayt bir kez tutulur; ayrıca geçicilik bir dosya sistemi özelliği değildir — yazılabilir katman ana makinenin diskinde bir dizindir ve konteyner durdurulduğunda değil, silindiğinde gider.

Sonraki Adım

Kayıp iki sınıfta kabul edilemezdi: iş durumu ve kullanıcı verisi. İkisi toplam 7.945 bayt tutuyor ve ikisi de yazılabilir katmanın içinde durduğu için siliniyor. Çözüm bellidir — o iki dosyayı katmanın dışına, konteynerin yaşam döngüsüne bağlı olmayan bir yere koymak. Ama katmanın dışı ana makinenin dosya sistemidir ve oraya açılan her yol yalıtımdan bir şey götürür. Sonraki ders iki bağlama biçimini gerçek dizinlerle kurar ve şunu sayar: konteyner silindiğinde kaç bayt kalıyor, konteyner içindeki kullanıcı kimliği dizinin sahibiyle uyuşmadığında kaç dosya yazılamıyor ve iki biçimden hangisi makine değiştiğinde kırılıyor.

İ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