İçeriğe geç
academia.sh

Ders 07 / 12

Yazılım Malzeme Listesi

Bileşen envanteri bir soru kümesiyle ölçülür: on iki sorunun kaçı envanterin alanlarıyla cevaplanıyor, kaçı hiç cevaplanmıyor, cevaplananların kaçı eksik alanla veriliyor ve envanter ne sıklıkta üretilirse sorgu anında kaç kayıtta yanılıyor.

İçindekiler

Önceki konu her değişiklikte ne tarandığını ve neyin kaçtığını saydı. Bütün o denetimlerin ortak bir sınırı vardı: hepsi ekibin yazdığı koda bakıyordu. Üretime giden şeyin çoğu ise başka yerde üretilmiş bileşenlerden oluşur ve o bileşenlerin hiçbiri bir gözden geçirmeden, bir kural setinden ya da bir desen taramasından geçmemiştir.

Bu ders o katmanı bir envantere çevirir. Kurs boyunca ölçü kanıttır ve bu derste kanıtın birimi sorudur: envanterin cevaplayabildiği soru sayısı. Sürekli Tümleştirme ve Teslimat kursunda envanter bir hat çıktısı olarak ölçülmüştü — kapsamı ve üretim maliyeti sayılmıştı; o ölçüm tekrarlanmaz. Buradaki nesne envanterin yapısıdır: hangi alanı taşıyor, o alanlarla hangi soru cevaplanabiliyor, hangisi cevaplanamıyor.

TZ1. Kurgu bölgesel ölçüm ağı üç depodur — abone portalı, sayaç alım servisi ve ikisinin paylaştığı ortak kitaplık; bağımlılık ağacı node ile üretilir ve bir modeldir. TZ2. Envanter beş alan taşır: bileşen adı, sürüm, hangi depoya girdiği, lisans ve bağımlılık ilişkisi (bu bileşeni hangi bileşenin getirdiği). Envanter biçiminin adı geçmez; ölçülen şey bu beş alanın kendisidir. TZ3. Çözümleyici lisansı kayıtların yaklaşık altıda birinde, getiren kenarını sekizde birinde kaydetmez; alan vardır, değer yoktur. Üretecin tohumu 20260731 görünürdür.

Envanteri Üretmek

// olcum-agi/envanter.mjs — uc deponun bilesen envanteri uretilir (model)
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
mkdirSync(KOK, { recursive: true });
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(20260731);                                  // tohum gorunur
const sec = (d) => d[Math.floor(r() * d.length)];

// Kurgu bolgesel olcum agi: abone portali ve sayac alim servisi ortak kitapligi paylasir.
const DEPO = [["abone-portali", 11], ["sayac-alim", 9], ["ortak-kitaplik", 6]];
const HAVUZ = Array.from({ length: 70 }, (_, i) => `bilesen-${String(i + 1).padStart(2, "0")}`);
const SURUM = ["1.2", "1.9", "2.0", "2.4", "3.0", "3.6", "4.1"];
const LISANS = ["izin-veren", "izin-veren", "izin-veren", "zayif-karsilikli", "karsilikli"];

const kayit = [];
for (const [depo, dogrudanSayi] of DEPO) {
  const alinan = new Set();
  const yeni = () => { let a; do { a = sec(HAVUZ); } while (alinan.has(a)); alinan.add(a); return a; };
  const ekle = (ad, getiren) => {
    // Cozumleyici lisansi %18, getiren kenarini %12 oraninda kaydetmiyor: alan var, deger yok.
    kayit.push({ ad, surum: sec(SURUM), depo,
      lisans: r() < 0.18 ? "" : sec(LISANS),
      getiren: getiren === null ? depo : (r() < 0.12 ? "" : getiren) });
  };
  for (let i = 0; i < dogrudanSayi; i++) {
    const kok = yeni();
    ekle(kok, null);
    for (let k = 0, n = Math.floor(r() * 4); k < n; k++) {   // gecisli cocuklar
      const cocuk = yeni();
      ekle(cocuk, kok);
      if (r() < 0.35) ekle(yeni(), cocuk);                   // ikinci duzey
    }
  }
}
writeFileSync(join(KOK, "envanter.json"), JSON.stringify(kayit));

const ALAN = ["ad", "surum", "depo", "lisans", "getiren"];
const dolu = (a) => kayit.filter((k) => k[a] !== "").length;
const dogrudan = kayit.filter((k) => DEPO.some(([d]) => d === k.getiren)).length;
console.log(`tohum 20260731; depo ${DEPO.length}, havuz ${HAVUZ.length} bilesen`);
console.log(`envanter kaydi ${kayit.length} -> dogrudan ${dogrudan}, ` +
  `gecisli ${kayit.length - dogrudan}; benzersiz bilesen ${new Set(kayit.map((k) => k.ad)).size}`);
console.log(`envanter alani ${ALAN.length}: ${ALAN.join(", ")}`);
for (const a of ALAN)
  console.log(`  ${a.padEnd(8)} dolu ${String(dolu(a)).padStart(3)}/${kayit.length}` +
    ` (%${(100 * dolu(a) / kayit.length).toFixed(0)})`);
tohum 20260731; depo 3, havuz 70 bilesen
envanter kaydi 73 -> dogrudan 26, gecisli 47; benzersiz bilesen 48
envanter alani 5: ad, surum, depo, lisans, getiren
  ad       dolu  73/73 (%100)
  surum    dolu  73/73 (%100)
  depo     dolu  73/73 (%100)
  lisans   dolu  64/73 (%88)
  getiren  dolu  67/73 (%92)

Üç depo 26 doğrudan bağımlılık bildirir; envanter 73 kayıt üretir. Aradaki 47 kayıt hiçbir ekibin seçmediği geçişli bileşenlerdir. Benzersiz bileşen sayısı 48’dir: aynı bileşen birden çok depoda geçer, her geçişi ayrı bir kayıttır.

Alanların doluluk oranı envanterin asıl ölçüsüdür. İlk üç alan her kayıtta doludur, çünkü çözümleyici onları bilmeden kaydı hiç üretemez. Son iki alan böyle değil: lisans 9 kayıtta, getiren 6 kayıtta boştur. Boş bir alan yokluk anlamına gelmez — bileşenin lisansı vardır, envanterde yazmıyordur. Bu ayrım bir sonraki bölümün tamamını belirler.

On İki Soru

Envanterin değeri üretildiği anda değil, sorulduğu anda ölçülür. Ölçünün birimi de sorudur: bir soru envanterin alanlarıyla cevaplanabiliyorsa envanter o soruyu kapsar, cevaplanamıyorsa soru envanterin dışındadır.

TZ4. Soru kümesi kurgudur ve on iki sorudan oluşur; her soru gereksindiği alanları bildirir. Bir soru, gereksindiği bütün alanlar envanterde varsa cevaplanır; alanların herhangi biri boş kayıt taşıyorsa cevap eksik sayılır.

// olcum-agi/sorular.mjs — envantere on iki soru sorulur: kaci cevaplaniyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const kayit = JSON.parse(readFileSync(join(KOK, "envanter.json")));
const ALAN = new Set(["ad", "surum", "depo", "lisans", "getiren"]);
const DEPO = ["abone-portali", "sayac-alim", "ortak-kitaplik"];
const H = "bilesen-19";                                  // orneklerde sorulan bilesen
const adlar = [...new Set(kayit.map((k) => k.ad))];
const farkli = (ad, alan) => new Set(kayit.filter((k) => k.ad === ad).map((k) => k[alan]));

const SORU = [
  ["bu bilesen bizde var mi", ["ad"], () => (adlar.includes(H) ? "evet" : "hayir")],
  ["hangi surumde", ["ad", "surum"], () => [...farkli(H, "surum")].sort().join(", ")],
  ["hangi urune girdi", ["ad", "depo"], () => farkli(H, "depo").size + " depo"],
  ["kim getirdi", ["ad", "getiren"], () => [...farkli(H, "getiren")].join(", ")],
  ["kisitli lisansli bilesen var mi", ["lisans"],
    () => kayit.filter((k) => k.lisans === "karsilikli").length + " kayit"],
  ["kac bilesen iki surumle kosuyor", ["ad", "surum"],
    () => adlar.filter((a) => farkli(a, "surum").size > 1).length + " bilesen"],
  ["kac bilesen iki depoda ortak", ["ad", "depo"],
    () => adlar.filter((a) => farkli(a, "depo").size > 1).length + " bilesen"],
  ["dogrudan mi gecisli mi", ["getiren"],
    () => kayit.filter((k) => DEPO.includes(k.getiren)).length + " dogrudan"],
  ["bu bilesen kosum aninda cagriliyor mu", ["cagrilir"], null],
  ["bu surum envantere ne zaman girdi", ["giris"], null],
  ["son yayini ne kadar eski, kac bakimcisi var", ["bakim"], null],
  ["bu envanter hangi yapi ciktisini tarif ediyor", ["yapi"], null],
];

const bosSayisi = (alan) => kayit.filter((k) => k[alan] === "").length;
const S = [46, 13, 12, 24, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["soru", "gereken alan", "envanterde", "yanit", "tam mi"]);
let cevaplanan = 0, tamCevap = 0;
for (const [ad, gereken, calistir] of SORU) {
  const alanVar = gereken.every((a) => ALAN.has(a));
  const bos = alanVar ? Math.max(...gereken.map(bosSayisi)) : 0;
  if (alanVar) { cevaplanan++; if (bos === 0) tamCevap++; }
  satir([ad, gereken.join("+"), alanVar ? "var" : "YOK",
    alanVar ? calistir() : "-", alanVar ? (bos ? `${bos} kayit bos` : "tam") : "-"]);
}
console.log("");
console.log(`on iki sorunun ${cevaplanan}'i envanterin alanlariyla cevaplaniyor, ` +
  `${SORU.length - cevaplanan}'u alan olmadigi icin hic cevaplanmiyor`);
console.log(`cevaplanan ${cevaplanan} sorunun ${tamCevap}'i tam, ${cevaplanan - tamCevap}'i ` +
  `eksik alanla (lisans ${bosSayisi("lisans")} kayitta bos, getiren ${bosSayisi("getiren")} kayitta bos)`);
soru                                          gereken alan envanterde  yanit                   tam mi
bu bilesen bizde var mi                       ad           var         evet                    tam
hangi surumde                                 ad+surum     var         1.2, 3.6                tam
hangi urune girdi                             ad+depo      var         2 depo                  tam
kim getirdi                                   ad+getiren   var         bilesen-01, bilesen-23  6 kayit bos
kisitli lisansli bilesen var mi               lisans       var         10 kayit                9 kayit bos
kac bilesen iki surumle kosuyor               ad+surum     var         17 bilesen              tam
kac bilesen iki depoda ortak                  ad+depo      var         20 bilesen              tam
dogrudan mi gecisli mi                        getiren      var         26 dogrudan             6 kayit bos
bu bilesen kosum aninda cagriliyor mu         cagrilir     YOK         -                       -
bu surum envantere ne zaman girdi             giris        YOK         -                       -
son yayini ne kadar eski, kac bakimcisi var   bakim        YOK         -                       -
bu envanter hangi yapi ciktisini tarif ediyor yapi         YOK         -                       -

on iki sorunun 8'i envanterin alanlariyla cevaplaniyor, 4'u alan olmadigi icin hic cevaplanmiyor
cevaplanan 8 sorunun 5'i tam, 3'i eksik alanla (lisans 9 kayitta bos, getiren 6 kayitta bos)

İlk dört soru envanteri hakkında konuşulurken en sık sayılan dörttür ve dördü de cevaplanır: bileşen bizde var, iki sürümü koşuyor, iki depoya girmiş, iki ayrı bileşen getirmiş. Beş alan sekiz soruyu kapsar; envanterin kapsama oranı 8/12’dir.

Cevaplanmayan dördü aynı sınıftandır: hiçbiri envanterin yanlış cevapladığı soru değildir, dördü de envanterin sorulamayacağı sorudur. Bileşenin koşum anında çağrılıp çağrılmadığı bir çağrı çözümlemesi ister, sürümün envantere ne zaman girdiği bir tarih alanı, paketin bakım durumu bir dış kayıt, envanterin hangi yapı çıktısını tarif ettiği ise bir bağ. Bunlar eklenebilir alanlardır ve eklenmedikçe soru sorulamaz. Bir envanterin sınırı, taşıdığı alan kümesidir.

Asıl tehlikeli sütun sonuncusudur. Cevaplanan sekiz sorunun üçü eksik alanla cevaplanır ve eksiklik cevabın biçiminde görünmez. “Kısıtlı lisanslı bileşen var mı” sorusunun yanıtı 10 kayıttır; ama lisans alanı 9 kayıtta boştur, yani gerçek sayı 10 ile 19 arasındadır. Envanter “10” der, bir aralık değil. Aynı biçimde “kim getirdi” sorusu iki ad döndürür, oysa altı kayıtta getiren kenarı hiç yazılmamıştır — o altı bileşen envanterde durur ve zincirleri kopuktur. Boş alan bir eksiklik olarak değil, küçük bir sayı olarak görünür.

Envanter Ne Zaman Üretildi

Dördüncü cevaplanmayan soru bir sonraki ölçüyü açar: envanter hangi yapı çıktısını tarif ediyor? Bağ yoksa envanterin doğruluğu tarife değil, tazeliğe kalır.

TZ5. Yirmi yapılık dizi kurgudur; her yapı iki ile beş arasında kaydı değiştirir, dörtte biri bileşeni düşürür, kalanı sürüm yükseltir. Üretim başına 41 saniyelik hat süresi veri olarak verilmiştir, ölçülmüş değildir; tohum 551903 görünürdür.

// olcum-agi/tazelik.mjs — envanter ne siklikta uretilirse sorgu ani kac kayitta yaniliyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const baslangic = JSON.parse(readFileSync(join(KOK, "envanter.json")));
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(551903);                                // tohum gorunur
const SURUM = ["1.2", "1.9", "2.0", "2.4", "3.0", "3.6", "4.1"];
const anahtar = (k) => `${k.depo}|${k.ad}`;

// Kurgu yapi dizisi: yirmi yapi. Her yapi birkac kaydi degistirir; degisim de kurgudur.
const YAPI = 20, URETIM_SANIYE = 41;                     // sure veridir, olculmus degil
const durum = new Map(baslangic.map((k) => [anahtar(k), { ...k }]));
const gecmis = [];
for (let y = 1; y <= YAPI; y++) {
  const kayitlar = [...durum.values()];
  for (let i = 0, n = 2 + Math.floor(r() * 4); i < n; i++) {
    const k = kayitlar[Math.floor(r() * kayitlar.length)];
    if (r() < 0.25) durum.delete(anahtar(k));            // bilesen dusuruldu
    else k.surum = SURUM[Math.floor(r() * SURUM.length)];
  }
  gecmis.push(new Map([...durum].map(([a, k]) => [a, { ...k }])));
}

// Uc uretim sikligi. Sorgu her yapidan sonra gelir; envanter en son uretilen surumdur.
const SIKLIK = [["her yapida", 1], ["bes yapida bir", 5], ["yirmi yapida bir", 20]];
const S = [20, 12, 16, 18, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["uretim sikligi", "uretim", "hat suresi", "yanlis kayit", "en kotu an"]);
for (const [ad, adim] of SIKLIK) {
  let toplamYanlis = 0, enKotu = 0, uretim = 0;
  for (let y = 0; y < YAPI; y++) {
    if (y % adim === 0) uretim++;
    const envanter = gecmis[Math.floor(y / adim) * adim];  // elde duran envanter
    const simdi = gecmis[y];
    let yanlis = 0;
    for (const [a, k] of simdi) if (!envanter.has(a) || envanter.get(a).surum !== k.surum) yanlis++;
    for (const a of envanter.keys()) if (!simdi.has(a)) yanlis++;   // artik olmayan kayit
    toplamYanlis += yanlis;
    enKotu = Math.max(enKotu, yanlis);
  }
  satir([ad, uretim, `${uretim * URETIM_SANIYE} s`,
    `ortalama ${(toplamYanlis / YAPI).toFixed(1)}`, `${enKotu} kayit`]);
}
console.log("");
console.log(`yirmi yapida kayit ${baslangic.length} -> ${gecmis[YAPI - 1].size}; ` +
  `uretim basina ${URETIM_SANIYE} s veri olarak verildi, tohum 551903`);
uretim sikligi      uretim      hat suresi      yanlis kayit      en kotu an
her yapida          20          820 s           ortalama 0.0      0 kayit
bes yapida bir      4           164 s           ortalama 6.2      17 kayit
yirmi yapida bir    1           41 s            ortalama 22.1     41 kayit

yirmi yapida kayit 73 -> 56; uretim basina 41 s veri olarak verildi, tohum 551903

Her yapıda üretilen envanter hiçbir sorguda yanılmaz ve hattan 820 saniye alır. Beş yapıda bir üretilen envanter 164 saniye alır ve ortalama 6,2 kayıtta yanılır; en kötü anda 17 kayıt yanlıştır. Yirmi yapıda bir üretilen envanter 41 saniyeye iner ve ortalama 22,1 kayıtta yanılır — elli altı kayıtlık bir kümede.

Üç satır aynı kapının üç ayarıdır ve hattan alınan süreyle yanlış cevap sayısı ters orantılı değil, basamaklıdır: sıklık beşte bire indiğinde süre beşte bire iner, yanlış kayıt ise sıfırdan 6,2’ye çıkar. İlk basamak en pahalı olanıdır, çünkü sıfırdan farklı her sayı envanterin verdiği cevabı bir kestirime çevirir.

Son satır ölçünün kendi sınırını da yazar: yirmi yapıda kayıt sayısı 73’ten 56’ya iner. Eski bir envanter yalnız eksik değildir, fazladır da — artık kullanılmayan on yedi bileşeni koşuyormuş gibi gösterir. Bu iki hata birbirini götürmez; ikisi de aynı sütunda sayılır ve ikisi de aynı sonucu verir: envanterin verdiği sayı, tarif ettiğini iddia ettiği şeyi tarif etmez.

Özet

  • Üç depo 26 doğrudan bağımlılık bildirir, envanter 73 kayıt ve 48 benzersiz bileşen üretir; 47 kayıt hiçbir ekibin seçmediği geçişli bileşendir.
  • Envanterin beş alanından üçü her kayıtta doludur; lisans 9, getiren 6 kayıtta boştur. Boş alan yokluk değil, kaydedilmemiş değerdir.
  • On iki sorunun 8’i envanterin alanlarıyla cevaplanır, 4’ü hiç cevaplanmaz: çağrılma, envantere giriş tarihi, paketin bakım durumu ve envanterin hangi yapı çıktısına ait olduğu. Bir envanterin sınırı, taşıdığı alan kümesidir.
  • Cevaplanan 8 sorunun 3’ü eksik alanla cevaplanır ve eksiklik cevabın biçiminde görünmez: “10 kayıt” yanıtının gerçeği 10 ile 19 arasındadır.
  • Her yapıda üretim 820 saniye alır ve hiç yanılmaz; beş yapıda bir üretim 164 saniye alır, ortalama 6,2 kayıtta yanılır; yirmi yapıda bir üretim 41 saniyeye iner, ortalama 22,1 kayıtta yanılır. Eski envanter yalnız eksik değil, düşürülen on yedi bileşen kadar da fazladır.

Sonraki Adım

Envanter üretildi, kapsamı sayıldı ve tazeliğinin bedeli yazıldı. Cevaplanmayan dördüncü soru ise yerinde duruyor: bu envanter hangi yapı çıktısını tarif ediyor? Envanter ayrı bir dosyadır; yanına başka bir envanter konabilir, bir kaydı değiştirilebilir, bambaşka bir çıktıyla eşleştirilebilir. 73 kaydın hiçbiri, tarif ettiği baytların gerçekten o baytlar olduğunu söylemez.

Sonraki ders bu bağı gerçek bir anahtar çiftiyle kurar ve tek bir soruyu ikiye ayırır: imza neyi kanıtlar, neyi kanıtlamaz? Doğrulaması geçen bir çıktı için kaç ayrı iddia ileri sürülebilir ve bunların kaçı imzanın gerçekten dayandığı iddiadır — ve o anahtar bir kez ele geçtiğinde geriye dönük olarak kaç çıktı şüpheli hale gelir?

İ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