İçeriğe geç
academia.sh

Ders 08 / 12

Geliştirici Portalı ve Servis Kataloğu

On iki katalog kaydından sahiplik ve bağımlılık grafiği kurulur; sahipsiz servis, öksüz bağımlılık ve kayıtlı olmayan servis sayılır. Beş olayda doğru nöbet listesine ulaşma süresi katalogla ve katalogsuz ölçülür, sonra kaydın kendi bedeli çıkarılır: kaç alan elle yazılıyor ve kaç kayıt gerçekle uyuşmuyor.

İçindekiler

Önceki ders beş servisi gerçek dosyalarla üretti; her birinin tanımında bir sahip satırı var. O satır yazıldığı gün doğruydu. Gece yarısı fatura hesaplayıcı durduğunda arayan kişinin sorduğu üç soru ise başkadır: bu servisin nöbetçisi kim, bu servis neye bağlı, ve bu servis durursa kim etkilenir. Üçüncü sorunun cevabı hiçbir servisin kendi dosyasında yazılı değildir, çünkü ters kenarı kimse kendi deposunda tutmaz.

Servis kataloğu, çalışan servislerin sahiplik ve bağımlılık kayıtlarını tek yerde tutan listedir; geliştirici portalı o listeyi sorulabilir hâle getiren yüzeydir. Bu dersin ölçtüğü şey portalın görünüşü değil, kataloğun cevapladığı sorular ve cevabın ne kadar doğru olduğudur.

Ölçüm bölgesel ölçüm ağı üzerindedir: su sayacı okumalarını toplayan, doğrulayan ve saha iş emri açan kurgu yazılım. Katalog kayıtları ve yer gerçeği node ile yazılmış bir modeldir.

PT13. Katalog on iki kayıt tutar. Kayıt beş alanlıdır: ad, sahip ekip, nöbet listesi, bağımlılık listesi, son elle güncellemeden bu yana geçen gün. PT14. Yer gerçeği son doksan günün koşum kayıtlarından çıkarılır — gözlenen çağrı kenarları ve hattı koşturan ekip — ve modelde ayrı bir yapı olarak durur. PT15. Kişi adı geçmez; ulaşılan şey bir nöbet listesidir. PT16. Beş olay ve her adımın dakikası önceden yazılıdır, rastgelelik yoktur. PT17. Sahip alanı iskeletin ürettiği servis tanımından okunabilir; nöbet listesi ile bağımlılık listesi elle yazılır. PT18. Sahipsiz servis, sahip alanı boş olan ya da var olmayan bir ekibi gösteren kayıttır. Öksüz bağımlılık, katalogda kaydı olmayan bir servise bağlanan kenardır.

Kayıttan Grafiğe

Katalog düz bir listedir; sorulabilir olan şey listenin kendisi değil, ondan kurulan grafiktir. Her kayıt bir düğüm, her bağımlılık bir kenar verir; sahiplik alanı düğümü bir ekibe bağlar. Grafiğin değeri iki yerden gelir: kenarın ters yönü ve kayıtla gerçeğin farkı. İkisi de tek tek kayıtlara bakarak görülemez.

// katalog.mjs — servis katalogu kayitlari ve kosumdan cikarilan yer gercegi. Model, elle yazildi.
// kayit: [ad, sahip ekip, nobet listesi, bagimliliklar, son elle guncellemenin uzerinden gecen gun]
export const KAYIT = [
  ["okuma-toplayici", "sayac-okuma", "nobet-sayac", ["olcum-deposu", "kimlik-ucu"], 9],
  ["okuma-cozumleyici", "sayac-okuma", "nobet-sayac", ["okuma-toplayici"], 21],
  ["dogrulama-akisi", "dogrulama", "nobet-dogrulama", ["okuma-toplayici", "kural-deposu"], 14],
  ["kural-deposu", "dogrulama", "nobet-dogrulama", [], 27],
  ["fatura-hesaplayici", "faturalama", "nobet-fatura", ["dogrulama-akisi", "tarife-servisi"], 24],
  ["tarife-servisi", "faturalama", "nobet-fatura", [], 210],
  ["fatura-yazici", "faturalama", "nobet-fatura", ["fatura-hesaplayici"], 33],
  ["saha-emir", "saha", "nobet-saha", ["dogrulama-akisi", "harita-ucu"], 150],
  ["harita-ucu", "", "", [], 260],
  ["rapor-derleyici", "raporlama", "nobet-rapor",
    ["olcum-deposu", "arsiv-deposu", "fatura-yazici"], 120],
  ["gosterge-ucu", "raporlama", "nobet-rapor", ["rapor-derleyici"], 19],
  ["olcum-deposu", "dagitilmis-ekip", "nobet-eski", [], 180]];

export const GERCEK_BAG = {                         // son doksan gunde gozlenen cagri kenarlari
  "okuma-toplayici": ["olcum-deposu", "kimlik-ucu"], "okuma-cozumleyici": ["okuma-toplayici"],
  "dogrulama-akisi": ["okuma-toplayici", "kural-deposu"], "kural-deposu": [],
  "fatura-hesaplayici": ["dogrulama-akisi", "tarife-servisi", "kimlik-ucu"], "tarife-servisi": [],
  "fatura-yazici": ["fatura-hesaplayici"], "saha-emir": ["dogrulama-akisi"],
  "harita-ucu": ["kimlik-ucu"], "rapor-derleyici": ["olcum-deposu", "fatura-yazici"],
  "gosterge-ucu": ["rapor-derleyici"], "olcum-deposu": [], "kimlik-ucu": [] };

export const GERCEK_SAHIP = {                       // son doksan gunde hatti kosturan ekip
  "okuma-toplayici": "sayac-okuma", "okuma-cozumleyici": "sayac-okuma",
  "dogrulama-akisi": "dogrulama", "kural-deposu": "dogrulama", "fatura-hesaplayici": "faturalama",
  "tarife-servisi": "raporlama", "fatura-yazici": "faturalama", "saha-emir": "saha",
  "harita-ucu": "saha", "rapor-derleyici": "raporlama", "gosterge-ucu": "raporlama",
  "olcum-deposu": "sayac-okuma", "kimlik-ucu": "dogrulama" };

export const COK_EKIP = ["olcum-deposu", "harita-ucu"];   // birden cok ekibin dokundugu servis
export const EKIP = ["sayac-okuma", "dogrulama", "faturalama", "saha", "raporlama"];
// graf.mjs — katalog kayitlarindan sahiplik ve bagimlilik grafigi kurulur, gercekle karsilastirilir.
import { KAYIT, GERCEK_BAG, GERCEK_SAHIP, EKIP } from "./katalog.mjs";

const kayitli = new Set(KAYIT.map((k) => k[0]));
const ters = {};                                    // kim bana bagli: ters kenari katalog kuruyor
for (const [ad, , , bag] of KAYIT) for (const b of bag) (ters[b] ??= []).push(ad);

const kayitsiz = Object.keys(GERCEK_BAG).filter((s) => !kayitli.has(s));
const sahipsiz = KAYIT.filter(([, s]) => !s || !EKIP.includes(s));
const oksuz = KAYIT.flatMap(([ad, , , bag]) => bag.filter((b) => !kayitli.has(b)).map((b) => [ad, b]));
console.log(`katalog ${KAYIT.length} kayit, ${KAYIT.reduce((a, k) => a + k[3].length, 0)} bagimlilik` +
  ` kenari, ${Object.keys(ters).length} servis icin ters kenar cikarilabiliyor`);
console.log(`gercekte kosan ama hic kayitli olmayan servis ${kayitsiz.length}: ${kayitsiz.join(", ")}`);
console.log(`sahipsiz servis ${sahipsiz.length}: ` +
  sahipsiz.map(([a, s]) => `${a} (${s || "alan bos"})`).join(", "));
console.log(`oksuz bagimlilik ${oksuz.length}: ` + oksuz.map(([a, b]) =>
  `${a} -> ${b} [${b in GERCEK_BAG ? "servis var, kayit yok" : "servis yok, kayit eski"}]`).join("; "));
const etkilenenKayitsiz = Object.entries(GERCEK_BAG)
  .filter(([, b]) => b.some((x) => kayitsiz.includes(x))).length;
console.log(`kayitsiz servise gercekte baglanan ${etkilenenKayitsiz} servis var;` +
  ` portalin bu ${etkilenenKayitsiz} servis icin verdigi bagimlilik cevabi eksik`);

let alan = 0, kayitUyusmaz = 0, bagFark = 0, sahipToplam = 0;   // kaydin eskimesi: kayit ile gercek
const yas = { genc: [0, 0], eski: [0, 0] };
for (const [ad, sahip, , bag, gun] of KAYIT) {
  const g = GERCEK_BAG[ad] ?? [];
  const eksik = g.filter((b) => !bag.includes(b)).length;
  const fazla = bag.filter((b) => !g.includes(b)).length;
  const sahipFark = GERCEK_SAHIP[ad] !== sahip ? 1 : 0;
  const fark = eksik + fazla + sahipFark;
  alan += fark; bagFark += eksik + fazla; sahipToplam += sahipFark;
  kayitUyusmaz += fark > 0 ? 1 : 0;
  const kova = gun <= 30 ? "genc" : "eski";
  yas[kova][0]++; yas[kova][1] += fark > 0 ? 1 : 0;
}
const elle = KAYIT.length * 2, uretilen = KAYIT.length * 2;     // nobetci ve bagimlilik elle yazilir
console.log(`\nkayit alani ${elle + uretilen}: ${uretilen} tanesi iskeletin urettigi servis` +
  ` tanimindan okunuyor, ${elle} tanesi (nobet listesi, bagimlilik) elle yaziliyor`);
console.log(`gercekle uyusmayan kayit ${kayitUyusmaz}/${KAYIT.length}, uyusmayan alan ${alan}`);
console.log(`kayit yasi <= 30 gun: ${yas.genc[1]}/${yas.genc[0]} uyusmuyor` +
  `   > 30 gun: ${yas.eski[1]}/${yas.eski[0]} uyusmuyor`);
console.log(`uyusmazligin kaynagi: bagimlilik ${bagFark}, sahiplik ${sahipToplam};` +
  ` ikisi de kosumdan cikarilabilir, nobet listesi cikarilamaz`);

const etkilenen = (s) => {                          // ters kenarda gezinti
  const yigin = [s], sonuc = new Set();
  while (yigin.length) for (const u of ters[yigin.pop()] ?? [])
    if (!sonuc.has(u)) { sonuc.add(u); yigin.push(u); }
  return [...sonuc];
};
console.log(`\n"fatura-hesaplayici durursa kim etkilenir" — katalogla ${etkilenen("fatura-hesaplayici")
  .length} servis (${etkilenen("fatura-hesaplayici").join(", ")}), 1 grafik gezintisi`);
console.log(`ayni soru katalogsuz: ${KAYIT.length} servisin bagimlilik listesi elle taranir,` +
  ` ters kenar hicbir dosyada yazili degil`);
katalog 12 kayit, 14 bagimlilik kenari, 11 servis icin ters kenar cikarilabiliyor
gercekte kosan ama hic kayitli olmayan servis 1: kimlik-ucu
sahipsiz servis 2: harita-ucu (alan bos), olcum-deposu (dagitilmis-ekip)
oksuz bagimlilik 2: okuma-toplayici -> kimlik-ucu [servis var, kayit yok]; rapor-derleyici -> arsiv-deposu [servis yok, kayit eski]
kayitsiz servise gercekte baglanan 3 servis var; portalin bu 3 servis icin verdigi bagimlilik cevabi eksik

kayit alani 48: 24 tanesi iskeletin urettigi servis tanimindan okunuyor, 24 tanesi (nobet listesi, bagimlilik) elle yaziliyor
gercekle uyusmayan kayit 6/12, uyusmayan alan 7
kayit yasi <= 30 gun: 1/6 uyusmuyor   > 30 gun: 5/6 uyusmuyor
uyusmazligin kaynagi: bagimlilik 4, sahiplik 3; ikisi de kosumdan cikarilabilir, nobet listesi cikarilamaz

"fatura-hesaplayici durursa kim etkilenir" — katalogla 3 servis (fatura-yazici, rapor-derleyici, gosterge-ucu), 1 grafik gezintisi
ayni soru katalogsuz: 12 servisin bagimlilik listesi elle taranir, ters kenar hicbir dosyada yazili degil

Grafiğin kurulması üç boşluğu bir arada gösterir. Sahipsiz servis iki tanedir ve ikisi farklı biçimde sahipsizdir: harita ucunun sahip alanı boştur, ölçüm deposunun sahibi ise artık var olmayan bir ekiptir. İkincisi daha tehlikelidir, çünkü kayıt dolu görünür ve hiçbir boşluk taraması onu yakalamaz. Öksüz bağımlılık da iki tanedir ve nedenleri birbirinin tersidir: kimlik ucu gerçekten koşan ama katalogda hiç kaydı olmayan bir servistir, arşiv deposu ise kayıtta duran ama gerçekte olmayan bir servistir. Biri kaydın eksikliğidir, öteki kaydın eskimesi.

Ters kenar kataloğun tek başına verdiği cevaptır. Fatura hesaplayıcı durduğunda etkilenen üç servis tek bir grafik gezintisiyle çıkar; aynı soruyu katalogsuz cevaplamak on iki servisin bağımlılık listesini tek tek taramayı gerektirir, çünkü hiçbir servis kendisine kimin bağlı olduğunu bilmez.

Bir Olayda Doğru Nöbet Listesi

// olay.mjs — bes olayda dogru nobet listesine ulasma suresi: katalogla ve katalogsuz.
import { KAYIT, GERCEK_SAHIP, COK_EKIP } from "./katalog.mjs";

const ADIM = { depo: 6, ilkMesaj: 8, ekipKanali: 15, nobetBul: 12, katalogOku: 2, cagri: 3,
  yanlisListe: 10, gozdenGecirme: 4 };              // dakika, onceden yazili; rastgelelik yok
const OLAY = ["fatura-hesaplayici", "harita-ucu", "tarife-servisi", "saha-emir", "olcum-deposu"];

const katalogsuz = (s) => ADIM.depo + ADIM.ilkMesaj + ADIM.nobetBul +
  (COK_EKIP.includes(s) ? ADIM.ekipKanali : 0);     // depo gecmisi, mesaj, gerekirse ekip kanali
const katalogla = (s) => {
  const k = KAYIT.find(([a]) => a === s);
  if (!k || !k[2]) return [ADIM.katalogOku + katalogsuz(s), k ? "nobet alani bos" : "kayit yok"];
  if (GERCEK_SAHIP[s] !== k[1])
    return [ADIM.katalogOku + ADIM.yanlisListe + katalogsuz(s), "kayit yanlis listeye yonlendirdi"];
  return [ADIM.katalogOku + ADIM.cagri, "kayit guncel"];
};

console.log("olay                 katalogsuz  katalogla  fark  durum");
let a = 0, b = 0, kazanan = 0;
for (const s of OLAY) {
  const x = katalogsuz(s), [y, not] = katalogla(s);
  a += x; b += y; kazanan += y < x ? 1 : 0;
  console.log(`${s.padEnd(20)}${String(x).padStart(8)} dk${String(y).padStart(8)} dk` +
    `${String(y - x).padStart(6)}  ${not}`);
}
const guncel = OLAY.length * (ADIM.katalogOku + ADIM.cagri);
console.log(`toplam ${a} dk -> ${b} dk   ortalama ${(a / OLAY.length).toFixed(1)} dk -> ` +
  `${(b / OLAY.length).toFixed(1)} dk   sureyi kisaltan olay ${kazanan}/${OLAY.length}`);
console.log(`butun kayitlar guncel olsaydi: ${guncel} dk, ortalama ` +
  `${(guncel / OLAY.length).toFixed(1)} dk; su anki kazanc ${a - b} dk, olabilecek ${a - guncel} dk`);

const bakim = KAYIT.length * ADIM.gozdenGecirme;    // aylik gozden gecirme: kayit basina dort dakika
const olayBasi = (a - guncel) / OLAY.length;
console.log(`\naylik gozden gecirme ${bakim} dk (${KAYIT.length} kayit x ${ADIM.gozdenGecirme} dk);` +
  ` guncel kaydin olay basina kazanci ${olayBasi} dk`);
console.log(`esik: ayda ${Math.ceil(bakim / olayBasi)} olaydan sonra gozden gecirme kendini oduyor;` +
  ` ${Math.ceil(bakim / olayBasi) - 1} olayda odemiyor`);
olay                 katalogsuz  katalogla  fark  durum
fatura-hesaplayici        26 dk       5 dk   -21  kayit guncel
harita-ucu                41 dk      43 dk     2  nobet alani bos
tarife-servisi            26 dk      38 dk    12  kayit yanlis listeye yonlendirdi
saha-emir                 26 dk       5 dk   -21  kayit guncel
olcum-deposu              41 dk      53 dk    12  kayit yanlis listeye yonlendirdi
toplam 160 dk -> 144 dk   ortalama 32.0 dk -> 28.8 dk   sureyi kisaltan olay 2/5
butun kayitlar guncel olsaydi: 25 dk, ortalama 5.0 dk; su anki kazanc 16 dk, olabilecek 135 dk

aylik gozden gecirme 48 dk (12 kayit x 4 dk); guncel kaydin olay basina kazanci 27 dk
esik: ayda 2 olaydan sonra gozden gecirme kendini oduyor; 1 olayda odemiyor

Katalogun kazandırdığı yer nettir: kaydı güncel olan iki serviste ulaşma süresi yirmi altı dakikadan beş dakikaya iner. Kazandırmadığı yer daha öğreticidir. Beş olayın üçünde katalog süreyi uzatmıştır. Nöbet alanı boş olan kayıt iki dakika okuma masrafı ekler ve hiçbir şey söylemez; yanlış sahip yazan iki kayıt ise on iki dakika ekler, çünkü arayan kişi önce yanlış nöbet listesine yazar, yanıt bekler ve ancak sonra katalogsuz yola döner. Yanlış kayıt, kayıtsızlıktan pahalıdır — kayıtsızlık kimseyi yanıltmaz.

Toplamda ortalama 32 dakikadan 28,8 dakikaya iner; ölçülen kazanç on altı dakikadır. Bütün kayıtlar gerçekle uyuşsaydı aynı beş olay yirmi beş dakikada biterdi ve kazanç yüz otuz beş dakika olurdu. Kataloğun sunduğu değerin yüzde on ikisi alınmaktadır ve eksik olan şey portal değil, kaydın doğruluğudur.

Kaydın Kendi Bedeli

Kayıt bedeli iki sayıda toplanır. Kırk sekiz kayıt alanının yarısı iskeletin ürettiği servis tanımından okunur ve bedelsizdir; öteki yarısı — nöbet listesi ve bağımlılık listesi — elle yazılır. Elle yazılan alan eskir: on iki kaydın altısı gerçekle uyuşmaz ve yedi alan yanlıştır. Eskime yaşla birlikte gider; otuz günden genç altı kaydın biri, otuz günden eski altı kaydın beşi gerçekle uyuşmamaktadır.

Bakımın hesabı da buradan çıkar. Aylık gözden geçirme kayıt başına dört dakikadan kırk sekiz dakikadır; güncel kaydın olay başına kazancı yirmi yedi dakikadır. Eşik ayda iki olaydır: ayda tek olay yaşayan bir ağda gözden geçirme kendini ödemez, iki olaydan sonra öder. Ölçüm ağı gibi az sayıda servisin olduğu bir yerde bu eşik yakındır ve katalog bakımının kâğıt üstünde iyi görünüp pratikte yapılmamasının nedeni tam olarak budur.

Kataloğun gizlediği katman şudur: her ekip artık öteki ekiplerin depolarını, kanal geçmişini ve dağıtım kayıtlarını okumak zorunda değildir, tek bir kaydı okur. Sızma da tam bu noktadadır — kayıt tek kaynak gibi davranır ama tek kaynak değildir, ve sızdığında bunu belli etmez. Dışına çıkanlar sayılıdır: kayıtsız tek servise gerçekte üç servis bağlanmakta, yani portalın o üç servis için verdiği bağımlılık cevabı eksik kalmaktadır. Bu üç servisin ekipleri kataloğa bakıp yanlış bir bütünlük görürler; katalogsuz çalışan bir ekip en azından eksik baktığını bilir.

Üçüncü bir yol daha vardır ve sayıyı temelden değiştirir. Yedi uyuşmazlığın dördü bağımlılık, üçü sahiplik kaynaklıdır; bağımlılık kenarları koşum kayıtlarından, sahiplik ise hat koşumlarından çıkarılabilir. İkisi de çıkarılırsa uyuşmazlık sıfırlanır ve elle yazılan alan yirmi dörtten on ikiye iner. Geriye kalan tek alan nöbet listesidir ve o hiçbir koşumdan çıkarılamaz, çünkü nöbet bir gözlem değil, bir karardır.

Özet

  • On iki kayıttan kurulan grafik üç boşluk gösterdi: 2 sahipsiz servis, 2 öksüz bağımlılık ve gerçekte koşup katalogda hiç kaydı olmayan 1 servis.
  • İki sahipsizlik ve iki öksüzlük farklı türdendi: biri boş alan, öteki var olmayan ekip; biri kaydın eksikliği, öteki kaydın eskimesi.
  • Ters kenar kataloğun tek başına verdiği cevaptı: fatura hesaplayıcı durunca etkilenen 3 servis tek gezintiyle çıktı, aynı soru katalogsuz 12 bağımlılık listesinin taranmasını gerektirirdi.
  • Beş olayda ulaşma süresi ortalama 32 dakikadan 28,8 dakikaya indi, ama 5 olayın 3’ünde katalog süreyi uzattı; yanlış kayıt 12 dakika, boş alan 2 dakika ekledi.
  • Kayıtlar güncel olsaydı aynı beş olay 25 dakikada biterdi: ölçülen kazanç 16 dakika, olabilecek kazanç 135 dakika.
  • 48 kayıt alanının 24’ü elle yazılıyor ve 6/12 kayıt gerçekle uyuşmuyor; 7 uyuşmazlığın 7’si koşumdan çıkarılarak kapanabilir, kapanamayan tek alan nöbet listesidir.

Sonraki Adım

Katalog kimin sorumlu olduğunu söyler, ama kimsenin uymak zorunda olduğu bir kural koymaz. Ölçüm ağının kişisel veri taşıyan üç servisi için saklama süresi, kütük maskeleme ve imzalı paket zorunludur; bu kurallar bugün bir belgede yazılıdır ve her ekibin okuyup uygulaması beklenir. Bir sonraki ders aynı kural kümesini iki biçimde karşılaştırır: belge olarak yayımlamak ve hatta gömmek. Ölçülen şey uyum oranı, ihlal sayısı ve ekiplerin harcadığı zamandır — ve asıl sayı kuralı hiç görmeden uyan ekip sayısıdır.

İ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