Ders 16 / 20
Telafi Edici İşlemler
Geri alınamayan adımların dengelenmesi: yirmi dört sıralamadan yalnız ikisinin bağımlılıklara uyması, geri alınamaz adımın sona taşınamadığı durum, telafi çağrısının kendi arızasının günde 2,979 işi askıda bırakması ve ara durum penceresinin geri sarmadan değil askıda kalan işten gelmesi.
İçindekiler
Önceki ders yinelemeyi durdurdu: tekillik anahtarı aynı işin ikinci kez yapılmasını engelledi. Tatbikatın bıraktığı ikinci sorun yerinde duruyor. Gün sonu ücretlendirme iş akışı üçüncü adımda düştüğünde ilk iki adımın etkisi kesinleşmiş kalır; bunlar yinelenmiş değil, yarım kalmış etkilerdir ve tekillik anahtarı onlara hiçbir şey söylemez.
Telafi edici işlem (compensating transaction), tamamlanmış bir adımın etkisini ters yönde bir işlemle dengeleyen adımdır — Veri Erişim Katmanı kursundaki telafi adımı ile aynı kavramdır, orada saga düzeni içinde kurulmuş ve telafinin kendisinin de başarısız olabileceği ölçülmüştü; burada mekanik tekrarlanmaz. Adın benzerliği yanıltmasın: Eşzamansız İşleme kursundaki telafi politikası (catch-up policy) geri kalmış bir tüketicinin nasıl yetişeceğine dair ayrı bir karardır. Bu dersin üç sorusu vardır: telafi edilemeyen adım sıralamada nereye konabilir, telafi çağrısı başarısız olduğunda kaç iş askıda kalır ve geri sarma penceresinde ara durum ne kadar süre görünür.
Sıralama Serbest Değil
Gün sonu ücretlendirme dört adımdır: dönemi kapatmak, fatura satırını yazmak, tahsilat kaydını açmak ve satıcıya bildirim göndermek. İkisi geri alınamaz. Dönem kapatma geri alınamaz, çünkü kapanan dönemin yeniden açılması arada gelen taşıyıcı olaylarını kapsam dışına düşürür ve aynı gün iki kez faturalanabilir hale gelir. Satıcı bildirimi geri alınamaz, çünkü gönderilmiş bir bildirim ancak ikinci bir bildirimle düzeltilebilir.
Kural bilinir: telafi edilemeyen adım sıralamanın sonuna konur. Sorun, adımların bağımlılık taşımasıdır. Fatura satırı dönem kapanmadan yazılamaz, tahsilat kaydı fatura satırı olmadan açılamaz, bildirim fatura satırından önce gönderilemez. Kaç sıralamanın hem bağımlılıklara uyup hem kuralı sağladığı sayılabilir bir sorudur.
// telafi/akis.mjs — gun sonu ucretlendirme is akisinin surec ici modeli. // Servis, ag ve depo yoktur; adimlar birer nesne, geri sarma bir dongudur. // Telafi edici islem, M16/K04'un telafi adimiyla ayni seydir; mekanigi orada kuruldu. export const ADIM = [ { ad: "donem-kapat", telafi: null, once: [] }, // geri alinamaz { ad: "fatura-satiri", telafi: "satiri-iptal-et", once: ["donem-kapat"] }, { ad: "tahsilat-kaydi", telafi: "kaydi-geri-al", once: ["fatura-satiri"] }, { ad: "satici-bildirimi", telafi: null, once: ["fatura-satiri"] }, // geri alinamaz ]; export function* dizilisler(adimlar) { // butun siralamalar if (adimlar.length === 0) { yield []; return; } for (let i = 0; i < adimlar.length; i += 1) for (const k of dizilisler(adimlar.filter((_, j) => j !== i))) yield [adimlar[i], ...k]; } export const gecerli = (d) => d.every((a, i) => a.once.every((o) => d.slice(0, i).some((x) => x.ad === o))); // Bir kosum: k. adimda hata cikar, tamamlananlar tersten telafi edilir. // telafiHata: telafi cagrisinin basarisizlik olasiligi; deneme: telafi deneme sayisi. export function kosum(dizilis, k, { telafiHata, deneme, rnd }) { const tamam = dizilis.slice(0, k); const r = { telafisiz: 0, telafiCagrisi: 0, askida: 0, tur: 0 }; for (const a of [...tamam].reverse()) { r.tur += 1; if (a.telafi === null) { r.telafisiz += 1; continue; } let oldu = false; for (let d = 0; d < deneme && !oldu; d += 1) { r.telafiCagrisi += 1; if (d > 0) r.tur += 1; oldu = rnd() >= telafiHata; } if (!oldu) r.askida += 1; } return r; } export function uretec(tohum) { // 32 bitlik dogrusal esitlikli uretec, tohum acik let s = tohum >>> 0; return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; }; }
DD5 — telafi çağrısının başarısızlık olasılığı 0,05. Gerekçesi: telafi de bir uzak çağrıdır ve bir arızanın hemen ardından koşar; hedef servis o anda hâlâ bozuk olabilir, bu yüzden olağan çağrıdan daha yüksek bir hata payı taşır. Duyarlılığı deneme sayısı taranarak verilir. Sayı bir varsayımdır ve K01’in tablosuna eklenmez.
// telafi/olc.mjs — gecerli siralamalarda telafisiz kalan adim ve geri sarma turu import { ADIM, dizilisler, gecerli, kosum, uretec } from "./akis.mjs"; const D = [...dizilisler(ADIM)]; const G = D.filter(gecerli); console.log(`${D.length} siralamanin ${G.length} tanesi bagimliliklara uyuyor`); console.log(`geri alinamaz adim: ${ADIM.filter((a) => a.telafi === null).map((a) => a.ad).join(", ")}`); const yok = () => 1; // telafi hatasiz kosum: rnd() = 1 >= hata console.log(`\n${"siralama".padEnd(58)}${"telafisiz".padStart(10)}${"tur".padStart(6)}`); const ozet = []; for (const d of G) { let telafisiz = 0, tur = 0; for (let k = 1; k <= ADIM.length; k += 1) { // k. adimda hata const r = kosum(d, k - 1, { telafiHata: 0, deneme: 1, rnd: yok }); telafisiz += r.telafisiz; tur += r.tur; } ozet.push([d.map((a) => a.ad).join(" > "), telafisiz, tur]); console.log(`${ozet.at(-1)[0].padEnd(58)}${String(telafisiz).padStart(10)}${String(tur).padStart(6)}`); } const en = G[ozet.findIndex((o) => o[1] === Math.min(...ozet.map((x) => x[1])))]; console.log(`\nsecilen siralama: ${en.map((a) => a.ad).join(" > ")}`); console.log(`${"hatanin ciktigi adim".padEnd(24)}${"tamamlanan".padStart(12)}${"telafi edilen".padStart(15)}` + `${"telafisiz".padStart(11)}${"geri sarma turu".padStart(17)}`); for (let k = 1; k <= ADIM.length; k += 1) { const r = kosum(en, k - 1, { telafiHata: 0, deneme: 1, rnd: yok }); console.log(`${`${k}. (${en[k - 1].ad})`.padEnd(24)}${String(k - 1).padStart(12)}` + `${String(r.telafiCagrisi).padStart(15)}${String(r.telafisiz).padStart(11)}${String(r.tur).padStart(17)}`); } // DD5: telafi cagrisinin basarisizlik olasiligi 0,05. Deneme sayisi taranir. const N = 100_000, HATA = 0.05; console.log(`\n${N} kosum, DD5 telafi hatasi ${HATA}; hata adimi esit dagilimla secilir`); console.log(`${"telafi denemesi".padStart(16)}${"askida is".padStart(12)}${"oran".padStart(10)}` + `${"telafi cagrisi/is".padStart(19)}${"ort. geri sarma turu".padStart(22)}`); for (const deneme of [1, 2, 3]) { const rnd = uretec(20260730); let askida = 0, cagri = 0, tur = 0; for (let i = 0; i < N; i += 1) { const k = i % ADIM.length; // hata adimi esit dagilimla const r = kosum(en, k, { telafiHata: HATA, deneme, rnd }); askida += r.askida > 0 ? 1 : 0; cagri += r.telafiCagrisi; tur += r.tur; } console.log(`${String(deneme).padStart(16)}${String(askida).padStart(12)}` + `${(askida / N).toFixed(5).padStart(10)}${(cagri / N).toFixed(3).padStart(19)}` + `${(tur / N).toFixed(3).padStart(22)}`); }
24 siralamanin 2 tanesi bagimliliklara uyuyor
geri alinamaz adim: donem-kapat, satici-bildirimi
siralama telafisiz tur
donem-kapat > fatura-satiri > tahsilat-kaydi > satici-bildirimi 3 6
donem-kapat > fatura-satiri > satici-bildirimi > tahsilat-kaydi 4 6
secilen siralama: donem-kapat > fatura-satiri > tahsilat-kaydi > satici-bildirimi
hatanin ciktigi adim tamamlanan telafi edilen telafisiz geri sarma turu
1. (donem-kapat) 0 0 0 0
2. (fatura-satiri) 1 0 1 1
3. (tahsilat-kaydi) 2 1 1 2
4. (satici-bildirimi) 3 2 1 3
100000 kosum, DD5 telafi hatasi 0.05; hata adimi esit dagilimla secilir
telafi denemesi askida is oran telafi cagrisi/is ort. geri sarma turu
1 3724 0.03724 0.750 1.500
2 214 0.00214 0.788 1.538
3 8 0.00008 0.790 1.540
Bu sayılar ölçüm sınıfındadır: süreç içi bir modelden ve tohumu 20260730 olan bir üreteçten gelirler, aynı tohumla yeniden üretilirler.
Kural Ancak Bağımlılığın İzin Verdiği Kadar Uygulanır
Yirmi dört sıralamanın yirmi ikisi bağımlılık ihlal ediyor; geriye iki sıralama kalıyor ve ikisi
de donem-kapat ile başlıyor. Bu, kuralın sınırıdır: geri alınamaz iki adımdan biri sona
taşınamaz, çünkü öteki üç adım ona bağımlıdır. Bildirim sona alınabilir, dönem kapatma
alınamaz.
Alt tablo bunun bedelini adım adım gösteriyor. Hata birinci adımda çıkarsa hiçbir şey tamamlanmamıştır; telafisiz kalan adım 0. Hata ikinci, üçüncü ya da dördüncü adımda çıkarsa dönem kapalıdır ve kapalı kalır: her üç durumda da telafisiz adım 1. Sıralamayı değiştirmek bu sayıyı düşürmüyor, yalnız ikinci geri alınamaz adımın konumunu oynatıyor — bildirim üçüncü sıraya alındığında toplam telafisiz adım 3’ten 4’e çıkıyor.
Buradan çıkan tasarım kuralı sıralamayla ilgili değil. Sona taşınamayan bir geri alınamaz adım varsa onu geri alınabilir kılmak tek çıkıştır: dönem kapatma bir silme değil, bir bayrak yazması olarak tanımlanırsa telafisi vardır. Sıralama kuralı bir yerleştirme aracıdır, adım tasarımının yerini tutmaz.
Askıda Kalan İş ve Ara Durum Penceresi
Alt blok telafinin kendi arızasını sayıyor. Tek denemede işlerin yüzde 3,724’ü askıda kalıyor: telafi çağrısı düştü, adımın etkisi duruyor ve iş ne tamamlanmış ne geri alınmış durumda. İkinci deneme oranı 0,00214’e, üçüncü 0,00008’e indiriyor. Karşılığında iş başına telafi çağrısı 0,750’den 0,790’a, geri sarma turu 1,500’den 1,540’a çıkıyor — yani ikinci deneme yüzde 5 daha fazla çağrıya karşılık askıda kalan işi on yedi kat azaltıyor.
Ara durum penceresi, geri sarma sürerken fatura satırının “kesilmiş” görünmesidir. Bu pencerenin uzunluğu turla ölçülür; tura süre vermek için bir çevrim katsayısı gerekir.
DD6 — bir geri sarma turu 0,25 saniye. Gerekçesi: tur bir uzak çağrı ile onun kesinleşmesidir. Duyarlılığı dört katıyla verilir. DD7 — askıda kalan iş 4 saat içinde kapatılır, yani K01’in V10 toplu penceresi kadar; ücretlendirme işinin penceresi budur.
// telafi/bedel.mjs — olculen oranlarin K01 olceginde gunluk sayilara cevrilmesi const FATURA = 4000; // K01 hesabi: gunluk fatura satiri const KK2 = 0.02; // M19/K03 koreografi dersinin varsayimi: is akisi hata orani const TUR = 0.25, ASKIDA_SAAT = 4; // DD6 tur suresi, DD7 askida isin kapatilma suresi const hatali = FATURA * KK2; console.log(`gunluk is akisi ${FATURA}, KK2 = ${KK2} -> hatali ${hatali}/gun`); console.log(`\n${"siralama".padEnd(22)}${"telafisiz adim/gun".padStart(20)}${"yilda".padStart(9)}`); for (const [ad, oran] of [["bildirim sonda", 3 / 4], ["bildirim ucuncu", 4 / 4]]) console.log(`${ad.padEnd(22)}${(hatali * oran).toFixed(1).padStart(20)}` + `${(hatali * oran * 365).toFixed(0).padStart(9)}`); // olc.mjs olcumleri: askida is orani ve ortalama geri sarma turu, telafi denemesine gore const OLCUM = [[1, 0.03724, 1.500], [2, 0.00214, 1.538], [3, 0.00008, 1.540]]; console.log(`\n${"deneme".padStart(7)}${"askida is/gun".padStart(15)}${"yilda".padStart(8)}` + `${"geri sarma s".padStart(14)}${"ara durum s/gun".padStart(17)}${"askidan gelen saat/gun".padStart(24)}`); for (const [deneme, oran, tur] of OLCUM) { const askida = hatali * oran, sarma = tur * TUR; console.log(`${String(deneme).padStart(7)}${askida.toFixed(3).padStart(15)}` + `${(askida * 365).toFixed(0).padStart(8)}${sarma.toFixed(3).padStart(14)}` + `${(hatali * sarma).toFixed(1).padStart(17)}${(askida * ASKIDA_SAAT).toFixed(3).padStart(24)}`); } const [, o1, t1] = OLCUM[0]; const kat = (t) => (hatali * o1 * ASKIDA_SAAT * 3600) / (hatali * t1 * t); console.log(`\ntek denemede askidan gelen / geri sarmadan gelen = ${kat(TUR).toFixed(0)} kat`); console.log(`DD6 duyarliligi: tur suresi ${TUR} s yerine ${TUR * 4} s ise ara durum ` + `${(hatali * t1 * TUR * 4).toFixed(1)} s/gun, oran ${kat(TUR * 4).toFixed(0)} kat`); console.log(`\narizasiz gunun bedeli: 4 adimin 2'si telafi islevi tasiyor, gunde ` + `${(FATURA * 2).toLocaleString("tr-TR")} yazilmis telafi yolu; kosulan telafi cagrisi ` + `${(hatali * 0.75).toFixed(0)}/gun, is akisi basina ${(0.75 * KK2).toFixed(4)}`);
gunluk is akisi 4000, KK2 = 0.02 -> hatali 80/gun
siralama telafisiz adim/gun yilda
bildirim sonda 60.0 21900
bildirim ucuncu 80.0 29200
deneme askida is/gun yilda geri sarma s ara durum s/gun askidan gelen saat/gun
1 2.979 1087 0.375 30.0 11.917
2 0.171 62 0.385 30.8 0.685
3 0.006 2 0.385 30.8 0.026
tek denemede askidan gelen / geri sarmadan gelen = 1430 kat
DD6 duyarliligi: tur suresi 0.25 s yerine 1 s ise ara durum 120.0 s/gun, oran 358 kat
arizasiz gunun bedeli: 4 adimin 2'si telafi islevi tasiyor, gunde 8.000 yazilmis telafi yolu; kosulan telafi cagrisi 60/gun, is akisi basina 0.0150
İki Günün Sayısı
Arızasız günün bedeli kod yolundadır. Dört adımın ikisi bir telafi işlevi taşımak zorundadır ve bu işlevler her iş akışı tanımında durur: günde 4000 iş akışı için 8000 yazılmış telafi yolu. Bunların koşulma sayısı çok küçüktür — iş akışı başına 0,0150, günde 60 çağrı. Bedel çalışma zamanı yükü değil, sınanmayan koddur: yılda 21.900 kez yürüyen bir yolun yanında, yüzde 1,5’lik bir olasılıkla yürüyen bir yol taşınıyor. Ayrıca dönem kapatmanın geri alınamaz olması arızasız günde de bir kısıt üretir; kapanan dönem üzerinde düzeltme yapılamaz.
Arızalı günün kazancı iki sayıdır. KK2 = 0,02 ile günde 80 iş akışı hata veriyor. Sıralama kuralı uygulandığında telafisiz kalan adım günde 60, yılda 21.900; bildirim üçüncü sıraya alınırsa 80 ve 29.200. Sıralama tek başına yılda 7300 telafisiz adım fark ettiriyor. Telafi denemesi bir arttırıldığında askıda kalan iş günde 2,979’dan 0,171’e, yılda 1087’den 62’ye iniyor.
Ara durum penceresinin iki kaynağı var ve büyüklükleri karşılaştırılamaz. Geri sarmanın kendisi günde toplam 30,0 saniye ara durum üretiyor. Askıda kalan işler ise günde 11,917 saat — 1430 kat. DD6 dört katına çıkarılsa bile oran 358 kat kalıyor, yani sonuç tur süresi varsayımına duyarlı değil. Ara durum penceresini geri sarmanın hızı değil, telafinin kendi arızası belirler; pencereyi kısaltmak isteyen taraf tur süresini değil, telafi deneme sayısını oynatmalıdır.
Özet
- Yirmi dört sıralamanın yalnız ikisi bağımlılıklara uyuyor ve ikisi de geri alınamaz
donem-kapatadımıyla başlıyor: sona taşıma kuralı bu adıma uygulanamıyor. - Hata ikinci, üçüncü ya da dördüncü adımda çıktığında telafisiz kalan adım her durumda 1; sona taşınamayan bir adımı kurtaran şey sıralama değil, adımı geri alınabilir tasarlamaktır.
- Bildirimin yeri yılda 7300 telafisiz adım fark ettiriyor: sonda 60/gün ve 21.900/yıl, üçüncü sırada 80/gün ve 29.200/yıl.
- Telafi kendi arızasını taşır: DD5 = 0,05 ile tek denemede işlerin yüzde 3,724’ü askıda kalıyor; ikinci deneme oranı 0,00214’e indiriyor ve iş başına çağrıyı 0,750’den 0,790’a çıkarıyor.
- Ara durum penceresinin ezici kısmı geri sarmadan değil askıda kalan işten geliyor: günde 30,0 saniyeye karşı 11,917 saat, 1430 kat; DD6 dört katına çıksa bile 358 kat.
- Arızasız günün bedeli 8000 yazılmış telafi yoludur ve bunların yalnız 60’ı günde koşar; taşınan şey çalışma zamanı yükü değil, seyrek yürüyen koddur.
Sonraki Adım
Telafi, iş akışının adımlarını doğru tuttu. Aynı iş akışının taşıdığı veri hiç sorulmadı. Taşıyıcı bir teslimi bildirirken yanına teslim kanıtını da koyar: alıcının imzası ya da teslim noktasının görüntüsü. Bu ek, durum olayının kaydını K01’in 220 baytından çok daha büyük bir şeye çevirir ve o şey kuyruğun içinden geçer, her tüketiciye kopyalanır, her yeniden teslimde yeniden taşınır. Sonraki ders büyük yükün iletiden çıkarılmasını ele alır: iletinin boyutu, kuyruk üzerinden geçen bayt, yükün ayrı depoda ne kadar yaşayacağı ve yük silindikten sonra elde kalan başvurunun kaç kez sarktığı.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.