Ders 01 / 21
Sürekli Tümleştirme
Birleştirme aralığının taranması: aralık büyüdükçe çatışan dosya, çözüm turu ve çözümde doğan hata nasıl büyüyor, sıklığın kendi bedeli kaç koşum ve kaç çalıştırıcı dakikası tutuyor, birleştirme kapısı neyi yalnız kendisi yakalıyor ve kaç temiz değişikliği boşuna durduruyor.
İçindekiler
Önceki kurs bir küme bıraktı: istenen durum bir yerde yazılı, uzlaştırıcı onu sürekli uyguluyor, gerçek durum sapınca geri çekiliyor. Kapanışta duran soru şuydu — o istenen durumu kim, neye bakarak ve hangi denetimden geçirerek değiştiriyor? Bu kurs o yolu kurar. Değişikliğin geliştiricinin makinesinden üretime giden hattı üzerinde duran, geçmesine izin veren ya da vermeyen her adım bir kapıdır ve bu kursun ölçü birimi kapıdır.
Yoldaki ilk kapı en eskisidir: bir değişikliğin ana dala girmesi. Sürekli tümleştirme (continuous integration), her geliştiricinin işini ana dala sık aralıklarla döndürmesi ve her dönüşte otomatik bir denetimden geçirmesidir. “Sık” bir sayı değil, bir eksendir; bu ders o ekseni tarar. Taranan büyüklük birleştirme aralığıdır: bir dalın ana daldan ayrı yaşadığı süre. Aralık büyüdükçe iki şey birlikte büyür — çatışan dosya sayısı ve çatışmayı çözerken doğan hata. Aralık küçüldükçe başka bir şey büyür — koşum sayısı ve çalıştırıcı zamanı. Sıklığın bedava olmadığı yer burasıdır. Süreç Modelleri konusunda ölçülen hata bulma gecikmesi burada yeniden ölçülmez; bu dersin ölçtüğü şey çatışmanın kendisidir.
Depo Modeli
Ölçüm, bölgesel ölçüm ağının deposu üzerinde yapılır: su sayacı okumalarını toplayan servis, okumaları doğrulayan servis, faturalama ve iş emri servisleri. Bu ağ kurgudur; aşağıdaki depo da bir modeldir, gerçek bir depo taranmamıştır. Süreler model dakikasıdır ve ölçümün koştuğu makineden bağımsızdır.
HT1 — depo dört servise bölünmüş 120 dosyadır; sekiz geliştirici yirmi iş günü çalışır, gün 480 model dakikasıdır. Her servisin ilk üç dosyası arayüz dosyasıdır: başka servisin beklentisini taşır.
HT2 — bir geliştirici saatte 0,5 dosyaya dokunur. Toplam iş aralıktan bağımsızdır: her tarama satırında 640 dosya dokunuşu vardır, değişen yalnızca bu işin kaç birleştirmeye bölündüğüdür.
HT3 — dosyaların beşte biri sıcaktır ve sekiz kat sık dokunulur; geliştirici kendi servisini yüzde 75 tercih eder. Üreteç kendi yazılmıştır, tohum görünürdür.
HT4 — çatışan dosya başına çözüm 20 model dakikasıdır. Çözüm sürerken ana dal ilerler; o sırada aynı dosyalara dokunan yeni bir birleşme olursa yeni bir tur açılır, en çok altı tur.
HT5 — çatışan dosya başına çözümde hata doğma olasılığı 0,09’dur. Çözümde doğan bir hata ile kapının kaçırdığı bir arayüz uyuşmazlığının sonradan bulunup düzeltilmesi 180 model dakikasıdır.
HT6 — bir hat koşumu 14 model dakikası çalıştırıcı zamanı harcar; koşumların yüzde ikisi kararsız kırmızı verir ve temiz bir değişikliği boşuna durdurup bir koşum daha ister.
// depo.mjs — kurgu bolgesel olcum agi deposunun modeli: servis, dosya, gelistirici // Tohumlu ureteg: 32 bitlik dogrusal esleme; disaridan kutuphane kullanilmaz. export const uretec = (tohum) => () => { tohum = (tohum * 1664525 + 1013904223) >>> 0; return tohum / 4294967296; }; // HT1: dort servis, servis basina dosya sayisi; ilk uc dosya arayuz dosyasidir. export const servisler = [ { ad: 'toplayici', dosya: 38 }, { ad: 'dogrulayici', dosya: 26 }, { ad: 'faturalama', dosya: 31 }, { ad: 'is-emri', dosya: 25 }, ]; export const dosyalar = servisler.flatMap((s, si) => Array.from({ length: s.dosya }, (_, i) => ({ ad: `${s.ad}/${i}`, servis: si, arayuz: i < 3, // HT3: dosyalarin bese biri sicaktir ve sekiz kat sik dokunulur. agirlik: i % 5 === 0 ? 8 : 1, }))); // Agirliga gore dosya secimi: gelistirici kendi servisini yuzde 75 tercih eder. export const dosyaSec = (r, gelistirici) => { const kendi = r() < 0.75; const havuz = kendi ? dosyalar.filter((d) => d.servis === gelistirici % 4) : dosyalar; const top = havuz.reduce((a, d) => a + d.agirlik, 0); let x = r() * top; for (const d of havuz) { x -= d.agirlik; if (x <= 0) return d; } return havuz[havuz.length - 1]; }; export const GELISTIRICI = 8; export const GUN = 20, GUN_DK = 480; // HT1: yirmi is gunu, gunde 480 model dakikasi export const HIZ = 0.5; // HT2: saatte 0,5 dosya dokunusu export const COZUM_DK = 20; // HT4: catisan dosya basina cozum suresi export const HATA_OLASILIK = 0.09; // HT5: cozumde hata dogma olasiligi export const SONRAKI_DK = 180; // HT5: gec bulunan bir hatanin bedeli export const KARARSIZ = 0.02; // HT6: kosumlarin yuzde ikisi yanlis tutar export const KOSUM_DK = 14; // HT6: bir hat kosumunun calistirici zamani
Aralık Taraması
Tarama altı aralık için aynı işi koşturur: iki saat, dört saat, bir gün, iki gün, dört gün ve on iş günü. Her satır aynı tohumla başlar, yani karşılaştırılan şey aralıktır.
// aralik.mjs — birlestirme araligi tarandiginda catisma ve kapinin bedeli import * as D from './depo.mjs'; const kos = (saat, tohum = 7391) => { const r = D.uretec(tohum); // her satir ayni tohumla baslar const T = saat * 60, ufuk = D.GUN * D.GUN_DK, n = D.HIZ * saat; const govde = [], olay = []; // govde: birlesmis dosyalar {t, dosya, ...} for (let dev = 0; dev < D.GELISTIRICI; dev++) { for (let k = 0; k < ufuk / T; k++) olay.push({ dev, bas: k * T + dev * (T / D.GELISTIRICI) }); } olay.sort((a, b) => a.bas - b.bas); let birlestirme = 0, catisan = 0, tur = 0, hata = 0, anlamsal = 0, yanlis = 0, kosum = 0, cozumDk = 0; for (const o of olay) { const kume = new Set(), arayuz = new Set(); while (kume.size < n) { // dal ayri n dosyaya dokunur const d = D.dosyaSec(r, o.dev); if (kume.has(d.ad)) continue; kume.add(d.ad); if (d.arayuz) arayuz.add(d.servis); } birlestirme++; let t = o.bas + T, pencere = o.bas, adim = 0; while (true) { const rakip = govde.filter((g) => g.t > pencere && g.t <= t && g.dev !== o.dev); const c = new Set(rakip.filter((g) => kume.has(g.dosya)).map((g) => g.dosya)).size; kosum++; if (r() < D.KARARSIZ) { yanlis++; kosum++; } // kararsiz kirmizi: temiz degisiklik durur if (adim === 0) { // metinsel catismasi olmayan arayuz uyusmazligi anlamsal += new Set(rakip.filter((g) => g.arayuz && arayuz.has(g.servis) && !kume.has(g.dosya)) .map((g) => g.dosya)).size; } if (c === 0 || adim >= 5) break; catisan += c; tur++; for (let i = 0; i < c; i++) if (r() < D.HATA_OLASILIK) hata++; const sure = c * D.COZUM_DK; cozumDk += sure; pencere = t; t += sure; adim++; } for (const ad of kume) { const d = D.dosyalar.find((x) => x.ad === ad); govde.push({ t, dosya: ad, dev: o.dev, servis: d.servis, arayuz: d.arayuz }); } } const sonrakiBedel = (hata + anlamsal) * D.SONRAKI_DK; return { saat, n, birlestirme, sonrakiBedel, catisan, tur, hata, anlamsal, yanlis, kosum, cozumDk, kosumDk: kosum * D.KOSUM_DK, dokunus: govde.length }; }; const s = (x, w) => String(x).padStart(w); const satir = [2, 4, 8, 16, 32, 80].map((h) => kos(h)); const f = (x, w) => String(x.toFixed(2)).padStart(w); console.log(`${'aralik'.padEnd(9)}${s('dal dosya', 10)}${s('birlestirme', 12)}${s('catisan', 9)}${s('catisan/blt', 12)}${s('tur/blt', 9)}${s('cozum hatasi', 14)}${s('kacan anlamsal', 16)}`); for (const x of satir) { console.log(`${(x.saat + ' sa').padEnd(9)}${s(x.n, 10)}${s(x.birlestirme, 12)}${s(x.catisan, 9)}` + `${f(x.catisan / x.birlestirme, 12)}${f(x.tur / x.birlestirme, 9)}${s(x.hata, 14)}${s(x.anlamsal, 16)}`); } console.log(`\n${'aralik'.padEnd(9)}${s('kosum', 7)}${s('calistirici dk', 15)}${s('cozum dk', 10)}${s('gec bulma dk', 14)}${s('toplam dk', 11)}${s('yanlis tutma', 14)}`); for (const x of satir) { console.log(`${(x.saat + ' sa').padEnd(9)}${s(x.kosum, 7)}${s(x.kosumDk, 15)}${s(x.cozumDk, 10)}${s(x.sonrakiBedel, 14)}` + `${s(x.kosumDk + x.cozumDk + x.sonrakiBedel, 11)}${s(x.yanlis, 14)}`); } const enIyi = satir.reduce((a, x) => (x.kosumDk + x.cozumDk + x.sonrakiBedel < a.kosumDk + a.cozumDk + a.sonrakiBedel ? x : a)); console.log(`\ndokunulan dosya her satirda ${satir.map((x) => x.dokunus).join('/')} (is sabit); ` + `tohum 7391 icin en dusuk toplam ${enIyi.saat} saatlik aralikta`); const en = (tohum) => [2, 4, 8, 16, 32, 80].map((h) => kos(h, tohum)) .reduce((a, x) => (x.kosumDk + x.cozumDk + x.sonrakiBedel < a.kosumDk + a.cozumDk + a.sonrakiBedel ? x : a)); console.log('baska tohumlar: ' + [20260801, 4517, 66049, 131].map((s) => `${s} -> ${en(s).saat} sa`).join(', '));
aralik dal dosya birlestirme catisan catisan/blt tur/blt cozum hatasi kacan anlamsal 2 sa 1 640 74 0.12 0.12 12 3 4 sa 2 320 90 0.28 0.27 4 10 8 sa 4 160 205 1.28 0.81 22 17 16 sa 8 80 320 4.00 0.97 31 26 32 sa 16 40 484 12.10 1.52 41 32 80 sa 40 16 654 40.88 1.63 55 43 aralik kosum calistirici dk cozum dk gec bulma dk toplam dk yanlis tutma 2 sa 731 10234 1480 2700 14414 17 4 sa 407 5698 1800 2520 10018 2 8 sa 297 4158 4100 7020 15278 8 16 sa 163 2282 6400 10260 18942 5 32 sa 103 1442 9680 13140 24262 2 80 sa 43 602 13080 17640 31322 1 dokunulan dosya her satirda 640/640/640/640/640/640 (is sabit); tohum 7391 icin en dusuk toplam 4 saatlik aralikta baska tohumlar: 20260801 -> 4 sa, 4517 -> 8 sa, 66049 -> 4 sa, 131 -> 4 sa
Çatışma Doğrusal Büyümüyor
İlk tablonun asıl satırı catisan/blt sütunudur. Dal ömrü iki saatten kırk kat büyüdüğünde
birleştirme başına çatışan dosya 0,12’den 40,88’e çıkar — kat sayısı 340’tır. Büyüme dal
başına dosya sayısıyla orantılı değildir, çünkü çatışma iki kümenin kesişimidir: hem
kendi dalı, hem de o sırada ana dala giren toplam iş büyür. İki uzun dal aynı sıcak dosyalara
dokunma olasılığını ikisi birden artırır.
tur/blt sütunu ilk bakışta bu büyümeye eşlik etmez: 0,12’den 1,63’e çıkar ve orada
doyar. Sebebi ölçümün içindedir — bir çözüm turu, çözüm sürerken ana dalın kımıldamasıyla
açılır. Dallar uzun olduğunda birleşmeler seyrektir, yani çözüm sürerken ana dal genellikle
kımıldamaz ve tek tur yeter. Uzun dalın bedeli tur sayısında değil, turun uzunluğunda
görünür: birleştirme başına çözüm süresi iki saatlik dalda 2,3 model dakikası, on iş günlük
dalda 818 model dakikasıdır.
Çözümde doğan hata da aralıkla büyür: 12’den 55’e. Bu hatalar birleştirmenin kendi ürettiği kusurlardır; kimse yanlış kod yazmamıştır, iki doğru değişikliğin elle uzlaştırılması sırasında doğmuşlardır. Dört saatlik satırdaki 4 değeri komşularının altında kalır; bu sayılar küçük olduğu için tohumun gürültüsüdür, eğilim değildir.
Sıklığın Kendi Bedeli
İkinci tablo ters yönü verir. İki saatlik aralıkta hat 731 kez koşar ve 10 234 model dakikası
çalıştırıcı zamanı harcar; on iş günlük aralıkta 43 koşum ve 602 dakika. Sık birleştirme
makineye on yedi kat daha pahalıdır. Aynı sütunla birlikte okunması gereken sayı yanlis tutmadır: kararsız kırmızı koşum başına sabit olasılıktadır, dolayısıyla koşum arttıkça
yanlış durdurma da artar — 17’ye karşı 1. Sık birleştiren bir ekipte kapının haksız
yere durdurduğu değişiklik sayısı, seyrek birleştiren bir ekiptekinden on yedi kat fazladır
ve kapıyı atlama isteği tam buradan doğar.
Toplam sütunu üç kalemi birleştirir: çalıştırıcı zamanı, çözüm süresi ve geç bulunan hatanın bedeli. Eğri iki uçtan da yükselir ve en düşük değer dört saatte, 10 018 dakikadadır. Beş tohumun dördü aynı aralığı, biri sekiz saati seçer. Sonuç “ne kadar sık, o kadar iyi” değildir: iki saatlik aralıkta çalıştırıcı zamanı ve yanlış tutma toplamı 4 000 dakikadan fazla şişirir, on iş günlük aralıkta çözüm ve geç bulma 30 000 dakikaya çıkar. Aralığın optimumu, koşumun bedeli ile çatışmanın bedelinin kesiştiği yerdedir; koşum ucuzladıkça optimum sola kayar, bu da sonraki derslerin hat süresini kısaltma çabasının asıl gerekçesidir.
Birleştirme Kapısının Üç Sayısı
Bu kapı ana dala giriş kapısıdır ve üç sayıyla yazılır. Tuttuğu: dört saatlik aralıkta 90 çatışan dosya birleştirme anında görünür hâle gelir. Bunun yalnız bu kapıya ait olan kısmı tamamıdır — metinsel çatışmayı hattaki hiçbir sonraki kapı göremez. Derleme kapısı kötü çözülmüş bir birleşmeyi ancak “bozuk” diye bildirir, hangi iki değişikliğin çakıştığını söyleyemez; çatışma bilgisi birleştirme anında vardır, sonra kaybolur.
Kaçırdığı: arayüz uyuşmazlıkları. Bunlar iki geliştiricinin farklı dosyalara dokunduğu, yani metinsel çatışması olmayan, ama birinin beklentisiyle ötekinin verdiği biçimin ayrıştığı durumlardır. Dört saatlik aralıkta 10, on iş günlük aralıkta 43 tanesi kapıdan geçer. Çözümde doğan hatalar da kapıdan geçer, çünkü kapı çözümün kendisini denetlemez. Yanlış tuttuğu: dört saatlik aralıkta 2, iki saatlik aralıkta 17 temiz değişiklik. Eklediği süre: koşum başına 14 dakika çalıştırıcı zamanı ve çatışan dosya başına 20 dakika insan zamanı; dört saatlik aralıkta değişiklik başına 23 dakika.
Bu dört sayı birlikte okunmadığında kapı savunulamaz. Yalnız çalıştırıcı zamanına bakan bir değerlendirme sık birleştirmeyi pahalı bulur ve aralığı büyütür; yalnız çatışma sayısına bakan bir değerlendirme aralığı sıfıra indirmek ister. İkisi de yanlıştır, çünkü kapının değeri tuttuğu ile kaçırdığının farkındadır ve bu fark aralığa bağlıdır. Aynı kapı iki saatlik aralıkta 74 çatışma tutup 3 uyuşmazlık kaçırır, on iş günlük aralıkta 654 tutup 43 kaçırır: daha çok tutmak, daha iyi tuttuğu anlamına gelmez — tuttuğu şeyin çoğunu aralığın kendisi üretmiştir.
Kaçırdığı iki kalem, bu kapının tek başına yetmediğini söyler ve hattın geri kalanının gerekçesidir: arayüz uyuşmazlığını yakalayacak olan sözleşme sınaması, çözümde doğan hatayı yakalayacak olan test kapısıdır. Kapıların sırası ve neyi yakaladığı, kursun ilerleyen derslerinin konusudur.
Özet
- Birleştirme aralığı iki saatten on iş gününe çıktığında birleştirme başına çatışan dosya 0,12’den 40,88’e, çözümde doğan hata 12’den 55’e, kapının kaçırdığı arayüz uyuşmazlığı 3’ten 43’e çıktı.
- Çözüm turu sayısı 1,63’te doydu; uzun dalın bedeli tur sayısında değil tur uzunluğundadır — birleştirme başına çözüm süresi 2,3 dakikadan 818 model dakikasına çıktı.
- Sıklığın kendi bedeli sayıldı: iki saatlik aralık 731 koşum ve 10 234 çalıştırıcı dakikası, on iş günlük aralık 43 koşum ve 602 dakika ister.
- Yanlış tutma koşum sayısıyla orantılıdır: 17’ye karşı 1; kapıyı atlama isteği bu sayıdan doğar.
- Üç kalem toplandığında en düşük değer dört saatlik aralıktadır (10 018 dakika) ve beş tohumun dördü aynı yeri gösterir; koşum ucuzladıkça optimum daha sık birleştirmeye kayar.
Sonraki Adım
Bu dersin kapısı ana dala giriştedir ve kararı tek bir sorunun cevabıdır: bu değişiklik girsin mi? Ana dal yeşile döndükten sonra ne olacağı sorusuna hiç dokunulmadı. Yeşil bir ana dal, üretime konabilecek bir şey midir, yoksa yalnızca üretime konmaya aday bir şey mi? İki cevap iki ayrı düzen kurar ve aralarındaki fark tek bir kapıda toplanır: yeşil koşumla üretim arasında duran insan onayı. Sonraki ders aynı hattı iki kipte koşturur ve o onayın yakaladığı gerçek kusuru, beklettiği süreyle birlikte sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.