İçeriğe geç
academia.sh

Ders 11 / 20

Zamanlanmış Tetikleme

Saatin bir hizmete devredilmesinin getirdiği garanti türü: otuz gecelik toplu faturalama koşumunda aynı dakikada gelen iki tetiklemenin ürettiği yinelenen fatura sayısının ölçülmesi, etkisizlik anahtarının bunu kapatırken kaçırılan koşumu kapatmaması ve iş penceresinin tetikleme anından türetilmesiyle veriden türetilmesi arasındaki farkın saat kayması altında sayılması.

İçindekiler

Önceki dersteki akışın tamamı bir olayla başlıyordu: okuma geldi, doğrulama tetiklendi, zincir yürüdü. Ölçüm ağının işlerinin bir bölümünde ise böyle bir olay yoktur. Gece toplu faturalama, günlük mutabakat ve haftalık sayaç sağlık taraması hiçbir okumanın gelişine bağlı değildir; onları başlatan tek şey saattir.

Zamanlanmış işin kendisi yeni değildir; kaçırılan koşum ve üst üste binme politikaları daha önce ölçüldü ve o karşılaştırma burada tekrarlanmaz. Bu dersin sorusu dardır: saati bir hizmet tuttuğunda ne değişir?

Devredilen Karar ve Garantinin Türü

Kendi işlettiği bir zamanlayıcıda saat, belirli bir makinede çalışan belirli bir süreçtir. O makine ölürse tetikleme olmaz; iki makine birden çalışırsa tetikleme iki kez olur. İkisi de görülebilir, ikisi de düzeltilebilir — çünkü saati tutan şey elin altındadır.

Hizmet olarak alınan bir zamanlayıcıda bu sorumluluk devredilir. Karşılığında gelen kısıt bir cümleyle yazılır ve bu dersin ekseni odur: garanti en az bir kezdir. Hizmet tetiklemenin gerçekleşeceğini üstlenir, bir kez gerçekleşeceğini üstlenmez. Aynı dakikada iki tetikleme olabilir; bu bir arıza değil, ilan edilmiş davranıştır. İşin etkisiz olması bu yüzden iyi bir uygulama değil, hizmetin ön koşuludur.

Devredilenler sayılabilir: saati tutan sürecin ayakta kalması, o sürecin tek örnekli olması, makine değiştiğinde zamanlamanın kaybolmaması, tetikleme kaydının tutulması. Geriye kalan yüzey yine küçüktür — zamanlama ifadesi, saat dilimi, hedef, hedefe geçilecek girdi ve tetikleme başarısız olursa uygulanacak yeniden deneme politikası: beş kalem. Önceki derslerdeki kuyruk yapılandırmasıyla aynı büyüklükte bir yüzey, aynı türden bir kısıt.

Saat dilimi kalemi tek başına bir örnektir. Zamanlama yerel saate göre yazıldığında yılda iki gece 24 saat sürmez; biri 23, öteki 25 saattir. Günde bir kez koşan bir iş o gecelerin birinde iki kez, ötekinde hiç koşmayabilir. Yani ölçümdeki iki kusur — çift koşum ve kaçırılan koşum — saat diliminin kendisinden de doğar ve bunun tetiklemenin doğruluğuyla ilgisi yoktur.

İkinci kısıt tersidir. Tetikleme anında hedef ayakta değilse — işlev kısılmışsa, hesap sınırındaysa, dağıtım yarıdaysa — o koşum kaçırılır ve telafi edilmez. Zamanlayıcı yalnız “şimdi”yi bilir; geçmiş bir anı yeniden üretmez. İki kısıt birlikte tuhaf bir çift oluşturur: aynı iş bazen iki kez koşar, bazen hiç koşmaz.

Ölçüm kurgu bölgesel ölçüm ağının gece toplu faturalama işi üzerindedir.

  • OS19. Otuz gecelik koşum; her gece 1.200 abone faturalanır, beklenen toplam 36.000 fatura.
  • OS20. On birde bir gecede tetikleme aynı dakikada iki kez gelir, on üçte bir gecede hiç gelmez; günler sabittir, modelde rastgelelik yoktur.
  • OS21. Etkisizlik anahtarı dönem ile abone kimliğinden türetilir; anahtar varsa ikinci yazma atlanır.
  • OS22. Tetikleme anı hedeflenen andan −60 ile +60 birim arasında kayar; kayma güne göre belirlidir.
  • OS23. Gün 1.440 birimdir ve okumalar gün içine eşit dağılır.
  • OS24. Model üst üste binen koşumların iç kilitlenmesini kurmaz; yalnız yazılan fatura ve işlenen okuma sayılır.

Aynı Dakikada İki Tetikleme

// tetikleme.mjs — zamanlanmis tetikleme modeli: en az bir kez garantisi ve kacirilan kosum
// Model: gercek bir zamanlayici degil, garanti turunun fatura sayisina etkisinin sayilmasi.
const GUN = 30, ABONE = 1200;
const CIFT = (g) => g % 11 === 4;      // bu gecelerde tetikleme ayni dakikada iki kez geldi
const KACAN = (g) => g % 13 === 9;     // bu gecelerde tetikleme hic gelmedi

function kosum(anahtarVar) {
  const yazilan = new Set();
  let fatura = 0, yinelenen = 0, eksik = 0, tetikleme = 0;
  for (let g = 0; g < GUN; g++) {
    if (KACAN(g)) { eksik += ABONE; continue; }        // telafi yok: gun bir daha gelmez
    for (let t = 0; t < (CIFT(g) ? 2 : 1); t++) {
      tetikleme++;
      for (let a = 0; a < ABONE; a++) {
        const anahtar = `d${g}:a${a}`;                 // etkisizlik anahtari: donem + abone
        if (anahtarVar && yazilan.has(anahtar)) continue;
        yazilan.add(anahtar); fatura++;
        if (t > 0) yinelenen++;
      }
    }
  }
  return { tetikleme, fatura, yinelenen, eksik };
}

console.log("etkisizlik anahtari".padEnd(21) + "tetikleme".padStart(11) +
  "yazilan fatura".padStart(16) + "yinelenen".padStart(11) + "eksik".padStart(8));
for (const [ad, v] of [["yok", false], ["var", true]]) {
  const s = kosum(v);
  console.log(ad.padEnd(21) + String(s.tetikleme).padStart(11) + String(s.fatura).padStart(16) +
    String(s.yinelenen).padStart(11) + String(s.eksik).padStart(8));
}
console.log(`gun: ${GUN}, abone: ${ABONE}, beklenen fatura: ${GUN * ABONE}`);
node tetikleme.mjs
etkisizlik anahtari    tetikleme  yazilan fatura  yinelenen   eksik
yok                           31           37200       3600    2400
var                           31           33600          0    2400
gun: 30, abone: 1200, beklenen fatura: 36000

Tablo aynı otuz geceyi iki kez işler; tek fark, yazma sırasında etkisizlik anahtarının sorulup sorulmamasıdır. Otuz gece için 31 tetikleme geldi. Sayı otuz değil, çünkü iki gece hiç tetikleme olmadı ve üç gecede ikişer tetikleme geldi: 28 + 3. Tek başına bu sayı zararsız görünüyor; faturaya yansıması ilk satırdadır.

Etkisizlik anahtarı olmadan 3.600 yinelenen fatura yazıldı. Üç gecenin her birinde 1.200 abone iki kez faturalandı. Ölçüm ağında bunun anlamı şudur: üç bin altı yüz aboneye aynı dönem için ikinci bir fatura çıktı, ikinci bir borç kaydı oluştu ve — önceki derslerdeki zincir düşünülürse — muhtemelen ikinci bir bildirim gitti. Hiçbir yerde hata yoktur; iki koşum da başarıyla bitmiştir.

Yinelenen faturanın yanında ikinci bir sayı daha var: 3.600 fazladan işlev çağrısı. Abone başına bir çağrı sayıldığında üç gece boyunca yapılan iş iki katına çıkar, eşzamanlılık havuzundan iki kat yer tutulur ve faturalama penceresi uzar. Üstelik iki koşum aynı dakikada başladığı için üst üste binerler; aynı abonenin kaydına iki koşum aynı anda yazar. Yazmanın kendisi bir tekillik kısıtına dayanmıyorsa, tabloda iki satır birden oluşur ve ikisi de “başarılı” sayılır. Çift tetiklemenin bedeli bu yüzden yalnız yinelenen kayıt değil, aynı zamanda kayıt düzeyinde bir yarıştır.

İkinci satır anahtarın ne yaptığını gösteriyor. Tetikleme sayısı yine 31’dir — anahtar tetiklemeyi tekilleştirmez, hizmet ikinci tetiklemeyi göndermeye devam eder. Değişen şey yazmadır: dönem ile abone kimliğinden türetilen anahtar zaten yazılmışsa ikinci yazma atlanır ve yinelenen fatura sıfıra iner. Bu, teslim semantiği ölçümünde kurulan ayrımın aynısıdır — tekilleştirilen şey teslim değil, etkidir.

Anahtarın neyden türetildiği burada belirleyicidir. Ölçümde anahtar dönem ile abone kimliğinden yapıldı; ikinci tetikleme aynı dönemi getirdiği için aynı anahtarı üretti ve yazma atlandı. Anahtar koşum kimliğinden türetilseydi — her tetikleme kendi kimliğiyle geldiği için — iki koşum iki ayrı anahtar üretir ve tablodaki ilk satıra geri dönülürdü. Anahtar, tekrarın hangi ölçüte göre “aynı iş” sayılacağını tanımlar; zamanlanmış işte bu ölçüt her zaman işin kapsamıdır, koşumun kendisi değil. Ölçüm ağında kapsam “hangi dönem, hangi abone”dir; aynı dönem iki kez faturalanamaz, çünkü ikinci fatura yeni bir olgu değil, birincinin kopyasıdır.

Anahtarın Kapatmadığı Sütun

Tablonun son sütunu iki satırda da aynıdır: 2.400 eksik fatura. Etkisizlik anahtarı bu sayıya dokunmaz, çünkü anahtar yalnız “bu iş daha önce yapıldı mı” sorusunu yanıtlar; hiç yapılmamış bir iş için sorulacak bir soru yoktur.

Eksik sütununun asıl güçlüğü fark edilme süresidir. Yinelenen fatura kendini gösterir: abone ikinci bildirimi alır, borç iki kez düşer, çağrı merkezine bir soru gelir. Eksik fatura hiçbir belirti üretmez — üretilmeyen fatura kimseyi rahatsız etmez. 2.400 fatura, otuz günlük dönemin yüzde 6,7’sidir ve bu fark ancak dönem sonu mutabakatında, gelir beklentisiyle karşılaştırma yapıldığında görünür. Ölçümdeki iki eksik gecenin maliyeti bu yüzden yalnız 2.400 fatura değil, o faturaların bir ay geç kesilmesidir.

Kaçırılan koşum, hizmetin garantisinin öteki yüzüdür ve düzeltilmesi tetikleme tarafında değil iş tanımı tarafındadır. Aradaki fark tek cümlede durur: iş “bugünü faturala” diye yazılırsa kaçan gün bir daha gelmez; “faturalanmamış dönemleri faturala” diye yazılırsa ertesi gece iki günü birden kapatır. İkinci biçim aynı zamanda çift tetiklemeye de dayanıklıdır, çünkü ikinci koşum faturalanmamış dönem bulamaz.

Bu biçimin bedeli iki kalemdir. Birincisi, işin artık kendi kapsamını bulması gerekir: her koşumda “hangi dönemler işlenmedi” sorgusu çalışır ve bu sorgu, koşum ne kadar seyrek kaçarsa o kadar sık boşuna koşar. İkincisi, kaçan gün sayısı arttıkça tek bir koşumun iş yükü katlanır — iki gün üst üste kaçarsa üçüncü gece 3.600 fatura yazılır ve bu, işlev süre sınırının içinde bitmek zorundadır. Kapsam tabanlı iş, kaçırma sorununu bir süre sınırı sorununa çevirir.

Üçüncü bir seçenek, koşumun gerçekleştiğini ayrı bir yerde işaretleyip eksik günleri arayan bir denetçi kurmaktır. Bu, kaçırmayı görünür kılar ama kendisi de zamanlanmış bir iştir ve aynı garantiye tabidir; denetçinin kaçırdığı koşumu bulacak bir üçüncü katman yoktur.

Üç seçenek arasındaki tercih, işin doğasına bakılarak yapılır. Faturalama gibi kapsamı veriden okunabilen işlerde ikinci biçim doğrudan uygulanır. Kapsamı olmayan işlerde — örneğin bir önbelleği ısıtan ya da geçici dosyaları silen bir koşumda — kaçan bir gecenin telafi edilecek bir karşılığı da yoktur; orada kaçırma bir kusur değil, kabul edilmiş bir durumdur. Ayrımı yapmanın ölçütü tek sorudur: koşum kaçtığında geriye eksik bir kayıt kalıyor mu? Kalıyorsa kapsam tabanlı yazım zorunludur; kalmıyorsa zamanlayıcının garantisi olduğu gibi kabul edilir.

Saat Kayması ve Pencerenin Kaynağı

Tetiklemenin bir de anı vardır. Hizmet “her gece belirli bir dakikada” der, ama gerçekleşen an o dakikanın etrafında kayar: sıra beklemesi, ölçekleme gecikmesi, saat eşitleme farkı. Kayma küçüktür; sorun kaymanın büyüklüğü değil, işin penceresini nereden aldığıdır. Aşağıdaki koşum aynı otuz geceyi iki pencere tanımıyla işler: biri pencereyi tetikleme anından geriye sayar, öteki bir önceki koşumun bıraktığı yerden başlatır. Kayma ikisinde de aynıdır.

// kayma.mjs — saat kaymasi: is penceresi tetikleme aninin mi, verinin mi uzerine kuruluyor
const GUN = 30, GUNLUK = 1200, DAKIKA = 1440;
const kayma = (g) => ((g * 37) % 121) - 60;      // -60..+60 birim, gune gore belirli
const tetik = (g) => (g + 1) * DAKIKA + kayma(g);

const OKUMA = [];                                 // okumalar gun icine esit dagilir
for (let g = 0; g < GUN; g++)
  for (let i = 0; i < GUNLUK; i++) OKUMA.push(g * DAKIKA + Math.floor((i * DAKIKA) / GUNLUK));

function kosum(kapsamli) {
  const sayac = new Map(OKUMA.map((z, i) => [i, 0]));
  let son = 0;
  for (let g = 0; g < GUN; g++) {
    const T = tetik(g);
    const bas = kapsamli ? son : T - DAKIKA;      // kapsamli: son islenen andan itibaren
    OKUMA.forEach((z, i) => { if (z >= bas && z < T) sayac.set(i, sayac.get(i) + 1); });
    son = T;
  }
  let hic = 0, cift = 0;
  for (const n of sayac.values()) { if (n === 0) hic++; else if (n > 1) cift += n - 1; }
  return { hic, cift };
}

console.log("pencere".padEnd(22) + "hic islenmeyen".padStart(16) + "cift islenen".padStart(14));
for (const [ad, k] of [["tetikleme anindan", false], ["son islenen andan", true]]) {
  const s = kosum(k);
  console.log(ad.padEnd(22) + String(s.hic).padStart(16) + String(s.cift).padStart(14));
}
console.log(`okuma: ${OKUMA.length}, kayma araligi: -60..+60 birim`);
node kayma.mjs
pencere                 hic islenmeyen  cift islenen
tetikleme anindan                  648           560
son islenen andan                    0             0
okuma: 36000, kayma araligi: -60..+60 birim

Pencere tetikleme anından geriye sayıldığında 36.000 okumanın 648’i hiç işlenmedi, 560’ı iki kez işlendi. Ne bir hata oluştu ne de tetikleme kaçtı; her gece iş koştu ve başarıyla bitti. Kayan tek şey pencerenin iki ucuydu. Bir gece erken, ertesi gece geç tetiklendiğinde aradaki dilim iki pencerede birden kalır; tersi durumda hiçbir pencereye girmez.

648 okuma, 36.000’in yüzde 1,8’idir ve bu oran yanıltıcıdır — kayıp rastgele dağılmaz. Kaçan dilim her zaman gün sınırının etrafındadır, yani hep aynı saatlerde okuma gönderen sayaçlara denk gelir. Sabit saatte rapor eden bir sayaç filosunda bu, her ay aynı abone kümesinin eksik faturalanması demektir; yüzde 1,8’lik bir genel kayıp değil, bir avuç abonede yüzde yüze yakın bir kayıptır. Çift işlenen 560 okuma da aynı kümededir ve iki hata birbirini gizler: toplam tüketim rakamına bakan bir denetim, 648 eksikle 560 fazlanın birbirini kısmen götürdüğünü görüp sapmayı küçük sanır.

İkinci satır aynı kaymayla sıfır üretiyor. Fark tek satırdır: pencere T - 1440 yerine son işlenen andan başlıyor. Pencere artık saatten değil, işin kendi ilerlemesinden türetiliyor; kayma pencerenin sonunu oynatsa da başlangıcı bir önceki koşumun bıraktığı yerdir, dolayısıyla ne boşluk ne örtüşme kalır.

Buradan dersin kuralı çıkar: zamanlanmış tetikleme işin ne zaman başlayacağını söyler, neyi işleyeceğini söylememelidir. Neyi işleyeceği veriden okunur. Bu ayrım kurulduğunda üç kısıt birden kapanır — çift tetikleme yeni iş bulamaz, kaçırılan koşum ertesi koşumda kapanır ve saat kayması pencereyi bozmaz. Bedeli, her koşumun kendi ilerleme işaretini kalıcı olarak yazması ve bu yazmanın işle aynı sınırda kesinleşmesi gereğidir; işaret işten önce yazılırsa kaçan dilim sessizce atlanır, sonra yazılırsa aynı dilim ikinci kez işlenir.

Özet

  • Zamanlanmış tetikleme hizmet olarak alındığında garanti en az bir kezdir: aynı dakikada iki tetikleme olabilir ve bu bir arıza değil, ilan edilmiş davranıştır.
  • Otuz gecede 31 tetikleme geldi; etkisizlik anahtarı olmadan 3.600 yinelenen fatura yazıldı ve iki koşum da hatasız bitti.
  • Anahtar eklendiğinde tetikleme sayısı yine 31 kaldı ama yinelenen fatura sıfırlandı: tekilleştirilen şey tetikleme değil, yazmadır.
  • Kaçırılan koşum telafi edilmez; 2.400 eksik fatura iki satırda da aynı kaldı. Bunu kapatan şey anahtar değil, işin “bugünü faturala” yerine “faturalanmamış dönemleri faturala” diye yazılmasıdır.
  • Pencere tetikleme anından türetildiğinde 36.000 okumanın 648’i hiç işlenmedi, 560’ı iki kez işlendi; pencere son işlenen andan türetildiğinde aynı kaymayla iki sayı da sıfır oldu. Kayıp rastgele de değildir: kaçan dilim hep gün sınırındadır, yani hep aynı saatte rapor eden sayaçlara denk gelir ve toplam üzerinden bakan bir denetimde 648 eksikle 560 fazla birbirini kısmen götürür.
  • Zamanlanmış tetikleme işin ne zaman başlayacağını söyler; neyi işleyeceğini veriden okumak çift tetiklemeyi, kaçırılan koşumu ve saat kaymasını aynı anda kapatır.

Sonraki Adım

Buraya kadar işlevleri uyandıran her şey içeriden geldi: bir kural, bir kuyruk, bir akış adımı, bir saat. Ölçüm ağının bir de dışarıya bakan yüzü var — saha ekiplerinin uygulaması, şube ekranları ve abonelerin kendi tüketimini sorguladığı arayüz. Bu istekler doğrudan işleve ulaşamaz; arada kimliği doğrulayan, yolu eşleyen ve kısan bir katman durur. Sonraki ders bu katmanı hizmet olarak ölçer: yapılandırma kalemi sayısı, istek başına ücret, yetkilendirmenin geçitte yapılmasının işlev çağrısı sayısına etkisi ve geçidin soğuk başlangıç konusunda ne yapıp ne yapmadığı.

İ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