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.