İçeriğe geç
academia.sh

Ders 03 / 12

Zorlayıcı İtme Disiplini

Bin işlemelik bir tarihçede son işlemeyi değiştirmek 1, ilk işlemeyi değiştirmek 1000 kimlik değiştirir; ama iki durumda da yedi kopyanın yedisi yeniden eşitlemek zorundadır ve dayanaksız kalan yerel iş aynı kalır — bedel değişikliğin büyüklüğüyle değil kopya sayısıyla ölçeklenir.

İçindekiler

Önceki derste bedel bir kez sayıldı ve tek bir depoda kaldı: bin kimlik, beş bin altı yüz kırk nesne, sıfır ortak ata. Sayı, işlemin yerel yarısını anlatıyordu. Yeniden yazılmış bir tarihçenin bir işe yaraması için karşı tarafa da yazılması gerekir — ve karşı taraf onu olağan yolla kabul etmez.

Bu ders o reddin neden konduğunu, nasıl aşıldığını ve aşıldığında kimin ne kaybettiğini ele alır. Bir yordam dersi değildir: aşağıda hiçbir zorlayıcı itme satırı çalıştırılabilir biçimde yazılmaz. Ölçülen şey aracın nasıl çağrıldığı değil, çağrının kimin üstünde bıraktığı bedeldir.

Reddin Nedeni

Getirme, çekme ve itme arasındaki ayrım önceki kursta kuruldu ve burada tekrarlanmaz. Bir tanesi burada gerekli: itme, karşı taraftaki bir başvurunun gösterdiği yeri değiştirmeyi ister. Karşı taraf bu isteği yalnız bir koşulda kabul eder — istenen yeni uç, o anki ucu içeriyorsa. İçeriyorsa hiçbir şey kaybolmaz; karşı taraftaki her işleme yeni uçtan geriye doğru yürünerek bulunabilir.

Yeniden yazma tam olarak bu koşulu bozar. Yeni zincir eski zincirin işlemelerini içermez, onların yerine başka kimlikli işlemeler koyar. Karşı taraf isteği reddeder ve gerekçesini söyler.

# örnek döküm — çalıştırılmamıştır

$ git push kaynak olcum
 ! [rejected]        olcum -> olcum (non-fast-forward)
error: failed to push some refs to 'kaynak'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

Ret iletisi bir kusuru değil, bir kuralın işlediğini bildiriyor. Bu ret deponun tek yerleşik korumasıdır. Karşı taraftaki başvurunun gösterdiği zinciri yalnız büyüterek değiştirmeye izin verir; küçültmeye ya da başka bir zincirle değiştirmeye izin vermez.

Reddi Aşmak

Aracın bu reddi devre dışı bırakan bir seçeneği vardır ve adı zorlayıcı itmedir. Yaptığı iş tek cümleyle söylenebilir: karşı taraftaki başvuruyu, eski değerini hiç sormadan yeni zincirin ucuna taşır. Eski zincir silinmez; onu gösteren başvuru kalmadığı için erişilemez kalır ve karşı tarafın çöp toplama düzeni onu bir süre sonra temizler.

Bu satır burada tam biçimde yazılmaz. Nedeni, satırın kopyalanabilir olmasının kendisidir: paylaşılan bir dalda çalıştırıldığında başkalarının işini o dalın kaydından siler ve işlem geri alınamaz — geri alınabilirliği yalnız başkalarının elindeki kopyalar sağlar, ki bunun ne demek olduğu dersin sonunda ayrıca yazılır.

Aynı ailenin ikinci bir seçeneği, karşı taraftaki başvurunun beklenen değerini de isteğe ekler: uç beklenen yerdeyse yazar, değilse reddeder. Bu seçenek zorlayıcı itmeyi güvenli hâle getirmez; yalnız görmediğiniz bir işi ezmenizi engeller. Gördüğünüz ve yine de ezdiğiniz iş için hiçbir koruma yoktur.

Bedeli Kim Öder

Yeniden yazmanın bedeli iki ayrı kalemde durur. Birincisi yazanın kalemidir: yeniden yazılan zincirin nesneleri bir kez üretilir. İkincisi kopyaların kalemidir: aynı zinciri her kopya yeniden almak zorundadır. Ölçüm ikisini yan yana koyar ve değiştirilen işlemenin yerini dört kez değiştirir.

Ölçümün varsayımları:

  • YZ17 — Kurgu önceki derslerin kurgusudur ve değiştirilmez; ölçüm 1000 işlemelik tarihçe üzerinde yapılır.
  • YZ18 — Depo yedi kopyada yaşıyor. Kopya sayısı ölçümün girdisidir ve yeniden yazmayı yapan kişinin seçimi değildir.
  • YZ19 — Dal ucunun kimliği değiştiği anda her kopya yeniden eşitlemek zorundadır. Ölçüm eşitlemek zorunda olan kopyayı sayar; eşitleyip eşitlemedikleri ölçümün dışıdır.
  • YZ20 — Yazanın bedeli yeniden yazdığı zincirin nesne sayısıdır; kopyaların bedeli aynı sayının kopya adedi kadar katıdır. Birim dokunulan nesnedir.
  • YZ21 — Her kopyanın eski ucun üzerine yazdığı bir miktar yerel iş vardır (kurgudan gelir, sıfır ile üç işleme arası) ve bu iş henüz karşı tarafa yazılmamıştır.
  • YZ22 — Zorlayıcı itmeden sonra bu yerel işin dayanağı ortadan kalkar. Dayanaksız kalan iş, değiştirilen işlemenin yerinden bağımsızdır: uç değiştiği anda her yerel zincir dayanağını yitirir.
  • YZ23 — Kimlik ve kopya sayıları bir bedel değil, kümenin genişliğidir; bedel yalnız nesne sütununda okunur.
  • YZ24 — Çözünürlük işlemede 1/1000, kopyada 1/7’dir.

Ölçüm

"""Zorlayici itme: yeniden yazmanin bedelini yazan odemez.

Bolum 1 - dort konum: kimligi degisen, yazanin ve kopyalarin dokundugu nesne.
Bolum 2 - kopyalarin eski ucun uzerine yazdigi yerel is.
Bolum 3 - paylasim durumu: tek guvenli kosul hangisi.
"""
TOHUM = 20260814
DOSYALAR = ("olcum.py", "rapor.py", "kimlik.py", "ayar.py", "belge.md")
KOPYA = 7


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def tarihce(n, tohum=TOHUM):
    """n islemelik dogrusal tarihce; her isleme bir dosyaya dokunuyor."""
    r, liste = uretec(tohum), []
    for i in range(n):
        dosya = DOSYALAR[r(5)]
        ikili = r(11) == 0
        liste.append({"no": i + 1, "dosya": dosya, "ikili": ikili,
                      "nesne": 2 + (40 if ikili else 0)})
    return liste


def yeniden_yaz(t, konum):
    sonra = [x for x in t if x["no"] >= konum]
    return len(sonra), sum(x["nesne"] for x in sonra)


def yeniden_esitleyen(kopya_sayisi, konum, t):
    """Yeniden yazilan tarihceyi kimler yeniden esitlemek zorunda."""
    degisen, _ = yeniden_yaz(t, konum)
    return kopya_sayisi if degisen else 0


def kopyalar(sayi=KOPYA, tohum=TOHUM):
    """Her kopyanin eski ucun uzerine yazdigi yerel isleme sayisi."""
    r = uretec(tohum)
    return [{"no": i + 1, "yerel": r(4)} for i in range(sayi)]


T = tarihce(1000)
K = kopyalar()

print(f"{'değiştirilen yer':>17s} {'kimliği değişen':>16s}"
      f" {'yazanın nesnesi':>16s} {'eşitleyen kopya':>16s}"
      f" {'kopyaların nesnesi':>19s}")
for konum in (1000, 900, 500, 1):
    isl, nes = yeniden_yaz(T, konum)
    k = yeniden_esitleyen(KOPYA, konum, T)
    print(f"{konum:17d} {isl:16d} {nes:16d} {k:16d} {k * nes:19d}")

print()
print("kopyaların eski uç üzerine yazdığı yerel işleme:",
      [x["yerel"] for x in K], "toplam", sum(x["yerel"] for x in K))
print()
print(f"{'paylaşım durumu':<16s} {'kopya':>6s} {'eşitleyen':>10s}"
      f" {'dayanaksız (son işleme)':>24s} {'dayanaksız (ilk işleme)':>24s}")
for ad, sayi in (("paylaşılmamış", 0), ("üç kopya", 3), ("yedi kopya", 7)):
    dayanaksiz = sum(x["yerel"] for x in K[:sayi])
    print(f"{ad:<16s} {sayi:6d} {yeniden_esitleyen(sayi, 1000, T):10d}"
          f" {dayanaksiz:24d} {dayanaksiz:24d}")
 değiştirilen yer  kimliği değişen  yazanın nesnesi  eşitleyen kopya  kopyaların nesnesi
             1000                1                2                7                  14
              900              101              602                7                4214
              500              501             3002                7               21014
                1             1000             5640                7               39480

kopyaların eski uç üzerine yazdığı yerel işleme: [0, 0, 3, 2, 2, 2, 0] toplam 9

paylaşım durumu   kopya  eşitleyen  dayanaksız (son işleme)  dayanaksız (ilk işleme)
paylaşılmamış         0          0                        0                        0
üç kopya              3          3                        3                        3
yedi kopya            7          7                        9                        9

Bin Kat Fark, Sıfır Kat Fark

Üst tablonun sol yarısı beklenen şeyi veriyor. Son işlemeyi değiştirmek 1 kimlik değiştiriyor ve yazana 2 nesneye mal oluyor; ilk işlemeyi değiştirmek 1000 kimlik değiştiriyor ve 5640 nesne demek. Aradaki fark bin kat. Bir yazım hatasını düzeltmekle deponun tamamını dönüştürmek arasında, yazanın gördüğü bedel bakımından, uçurum var.

Sağ yarı bu uçurumu görmüyor. Eşitlemek zorunda olan kopya sayısı dört satırda da 7. Yedi kopyanın yedisi, ne kadar küçük bir değişiklik olursa olsun, dal ucunun yeni kimliğini almak ve kendi durumunu ona göre düzeltmek zorunda. Fark bin kat değil, sıfır kat. Yazanın gördüğü uçurum, kopyaların gördüğü tabloda hiç yoktur.

Kopyaların nesne sütunu bunun mutlak karşılığını veriyor: son işlemeyi değiştirmenin karşılığı 14 nesne, ilk işlemeyi değiştirmenin karşılığı 39480. İki sayı da yazanın sütunundakinin yedi katı — ve yedi, yazanın belirlediği bir sayı değil. Kursun üçüncü iddiası buradadır: bedel değişikliğin büyüklüğüyle değil, kopya sayısıyla ölçeklenir ve o sayı yeniden yazmayı yapanın elinde değildir. Yeniden yazan kişi kendi kaleminde en ucuz satırı seçebilir; kopyaların kalemi seçime bağlı değildir.

Alt tablo bunu en sert biçimde söylüyor. Kopyaların eski uç üzerine yazdığı yerel iş toplam 9 işleme — yedi kopyanın üçünün elinde hiç yerel iş yok, dördünün elinde var. Zorlayıcı itmeden sonra dayanaksız kalan iş 9 ve bu sayı iki sütunda da aynı: son işlemeyi değiştirseniz de ilk işlemeyi değiştirseniz de dokuz işleme dayanağını yitiriyor. Uç değişti mi değişmedi mi — sorunun tamamı bu; ne kadar değiştiği kimseyi ilgilendirmiyor.

Tabloda tek bir güvenli satır var: paylaşılmamış. Kopya sayısı sıfır olduğunda eşitleyen sıfır, dayanaksız kalan sıfır. Ölçümün ürettiği ölçüt budur ve tek ölçüt budur.

Ara satırlar bir ayrıntıyı da gösteriyor. Dokuz yüzüncü işlemeden yeniden yazmak 101 kimlik değiştiriyor ama 602 nesneye dokunuyor; iki yüz iki değil. Fark, o aralığa düşen ikili varlıklardan geliyor — kimlik saymakla nesne saymak aynı sonucu vermiyor ve bu dersin birimi nesne. Kopyaların sütunu da bu yüzden kimlik sayısını değil nesne sayısını yediyle çarpıyor.

Kopya Ne Yapmak Zorunda

“Yeniden eşitlemek” tek bir işlem değil, bir karardır ve kararı kopyanın sahibi verir. İki yol vardır ve ikisi de bedelsiz değildir.

Birinci yol, yerel dalı yeni uca sıfırlamaktır. Yerel iş varsa bu yol onu bırakır; işlemeler erişilemez kalır ve ancak o kopyanın kendi referans günlüğünden bulunur. Yerel iş yoksa yol temizdir ve ölçümdeki üç kopya için durum budur.

İkinci yol, yerel işi yeni zincirin üstüne yeniden temellendirmektir. Hiçbir şey kaybolmaz, ama yerel işlemeler de yeni kimlik alır ve bu kursun ilk dersindeki bedel bu kez kopyanın kaleminde yazılır. Dört kopya için ödenen budur: dokuz işlemenin dokuzu yeniden yazılır.

Üçüncü bir yol, en azından bedelsiz bir üçüncü yol, yoktur. Yerel işi olduğu gibi karşı tarafa yazmayı denemek, aynı reddi bu kez kopyanın karşısına çıkarır — ve o noktada kopyanın sahibi de zorlayıcı itme düğmesine bakıyor olur. Bir zorlayıcı itmenin ikincisini davet etmesi, dersin başlığındaki disiplinin neden bir kişinin değil ekibin disiplini olduğunu gösterir.

Kimin Haberi Olmadan

İşlemin en can sıkıcı yanı sayılarda görünmez. Zorlayıcı itme tarihçeye yazılmaz. Yeni tarihçede, birinin bir şeyi ezdiğini söyleyen hiçbir işleme, hiçbir alan yoktur. Önceki kursun altıncı sorusu çakışma kararının kayda geçmediğini gösteriyordu; burada kayda geçmeyen şey, kaydın kendisinin değiştirilmiş olmasıdır.

Kopyalar da anında öğrenmez. Bir kopya karşı tarafa bakmadığı sürece elindeki eski zinciri geçerli sanmaya devam eder, üstüne işleme yazar ve dayanaksız işini büyütür. Öğrenme anı bir sonraki getirmedir. O ana kadar geçen sürede yapılan her yeni işleme, ezilmiş bir uca eklenen bir işlemedir.

Öğrenme anının kendisi de yanıltıcı olabilir. Getirmenin ardından çekme çağrılırsa araç iki zinciri birleştirmeyi dener ve genellikle başarır: eski zincir ile yeni zincir aynı değişiklikleri taşıdığı için birleşme sessizce tamamlanır. Sonuç, aynı değişikliklerin iki ayrı kimlikle iki kez bulunduğu bir tarihçedir. Bunu fark etmenin tek yolu tarihçeye bakmaktır; hiçbir uyarı çıkmaz. Ezilen dalın yeniden ezilmesi de çoğu kez böyle başlar — kopyalardan biri eski zinciri geri getirir, yeniden yazan kişi bunu görür ve işlemi tekrarlar.

Bu üç gerçek birlikte, işlemi bir araç sorunu olmaktan çıkarıp bir bildirim sorunu hâline getirir. Araç kimseyi uyarmaz, tarihçe kimseye söylemez; söyleyecek olan yeniden yazmayı yapan kişidir. Duyurunun içeriği de bellidir: hangi dal, hangi an, ve kopyaların hangi yolu izlemesi bekleniyor.

Disiplin

Ölçütler ölçümden çıkar ve dördü de kısa yazılabilir.

Birincisi: zorlayıcı itme yalnız paylaşılmamış dallarda uygulanır. Bir dalın paylaşılmamış olması, “kimse ondan çalışmıyor” varsayımı değil, kontrol edilebilir bir durumdur.

İkincisi: ana dal, yayım dalı ve etiketlerin gösterdiği zincirler yeniden yazılmaz. Bir değişikliği geri almak gerektiğinde yol yeniden yazmak değil, tersini yapan yeni bir işleme yazmaktır — bu yol Sürüm Kontrolüne Giriş kursunda devirme adıyla kuruldu ve tarihçeyi hiç değiştirmediği için hiçbir kopyayı ilgilendirmez.

Üçüncüsü: paylaşılan bir inceleme dalının yeniden yazılması gerekiyorsa işlem önceden duyurulur, sonrasında da duyurulur. Beklenen değeri isteğe ekleyen seçenek kullanılır; o seçenek görülmemiş işi korur, görülmüş işi korumaz.

Dördüncüsü: koruma araca değil, karşı tarafa konur. Depo sunucusunda dal koruma yetenekleri bulunur ve ileri sarma dışındaki güncellemeleri reddedecek biçimde ayarlanabilir. Bir kuralın araç tarafında değil sunucu tarafında durması, onu unutulabilir olmaktan çıkarır.

Dört ölçütün pratikte hangi noktada çöktüğü de söylenmelidir. Çoğu zaman kural bilinir ve uygulanır; kaza, bir dalın paylaşılmamış sanılmasından doğar. Bir dalın adı kişiseldir, kimsenin ondan çalışmadığı varsayılır, oysa biri onu bir inceleme sırasında getirmiştir. Varsayımı sınamanın yolu karşı tarafa bakmaktır, kendi belleğine değil; dalın karşı taraftaki durumu getirilirse varsayım bir ölçüme dönüşür.

Her kayıp yolunun bir kurtarma yolu vardır ve burada üçü sayılabilir. Karşı taraftaki eski zincir başvurusuz kaldığında kurtarma kopyalardadır: eski ucu hâlâ gösteren bir kopya onu yeni bir dal olarak geri yazabilir; ezilen tarafın yedeği, bedeli ödeyen taraftır. Bir kopyanın dayanaksız kalan yerel işi, o kopyanın kendi referans günlüğünden bulunup yeni zincirin üstüne taşınabilir. Yeniden yazmayı yapan kişinin kaybettiği eski uç, yedek dal ya da yerel referans günlüğünden okunur. Üç yolun ikisi aynı düzeneğe dayanıyor ve o düzeneğin bir ömrü var.

Özet

  • Olağan itme yalnız karşı taraftaki ucu içeren bir zinciri kabul eder; yeniden yazma bu koşulu bozduğu için reddedilir ve ret deponun tek yerleşik korumasıdır.
  • Zorlayıcı itme reddi kaldırır: başvuruyu eski değerini sormadan taşır. Satır burada tam biçimde yazılmaz; paylaşılan bir dalda geri alınamaz.
  • Bin işlemelik tarihçede son işlemeyi değiştirmek 1 kimlik ve 2 nesne, ilk işlemeyi değiştirmek 1000 kimlik ve 5640 nesne demektir — ama yedi kopyanın yedisi dört durumda da yeniden eşitlemek zorundadır.
  • Kopyaların ödediği 14 ile 39480 nesne arasında değişir ve her zaman yazanın ödediğinin yedi katıdır; dayanaksız kalan 9 işleme ise değiştirilen yerden bağımsızdır. Bedeli yazan ödemez.
  • Ölçümün verdiği tek güvenli satır paylaşılmamış dallardır: sıfır kopya, sıfır dayanaksız iş.
  • İşlem tarihçeye yazılmaz ve kopyalar bir sonraki getirmeye kadar öğrenmez; bildirim aracın değil, yeniden yazan kişinin işidir.

Sonraki Adım

Bu dersin kurtarma yollarından ikisi aynı yere bakıyordu: dal ucunun eski değerini tutan yerel kayda. O kayıt sınırsız değildir. Yeniden yazılmış bir tarihçede eski zincirin işlemeleri erişilemez durumda durur, bir süre kurtarılabilir kalır, sonra temizlenir. Sonraki ders o pencereyi ölçer: yeniden yazmadan sonra kaç işleme erişilebilir kalır, kaçı yalnız kayıttan bulunabilir, ve kurtarma penceresi hangi anda kapanı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