İçeriğe geç
academia.sh

Ders 06 / 14

Bildirim Sistemi

Aynı iletinin birden çok yoldan gidebildiği vaka: kanal başına geçici ve kalıcı hata oranının ayrılması, tek kanalda üç denemenin teslim oranını eşiğin 3,01 puan altında bırakması, tek kanal ile paralel ve kademeli kanal sırasının teslim oranı, dış çağrı, yinelenen teslim ve gecikme cinsinden ölçülmesi ve paralel dağıtımın çağrı çarpanıyla elenmesi.

İçindekiler

Önceki vakada teslim tek bir yol üzerinden yürüdü ve başarısızlığa verilen tek yanıt aynı yolu yeniden denemekti. Bu vakada yol birden fazladır. Aynı bildirim uygulama itmesiyle, kısa iletiyle ya da e-postayla gidebilir; kanalların başarısızlık oranları birbirinden farklıdır ve birinin başarısızlığı ötekini denemek için gerekçedir.

Bu, yeniden deneme sorusunu değiştirir. Bir denemenin nereye harcanacağı artık “aynı yere mi, başka yere mi” sorusudur ve yanıtı iki sayıyı birden oynatır: toplam teslim oranını ve dışarıya yapılan çağrı sayısını.

Kısıtlar ve Kapsam

İşlevsel gereksinimler: bildirim isteğini kabul etmek, alıcının tercihine göre kanal seçmek, kanal başına biçimlendirmek, teslim durumunu kaydetmek, aynı olayın alıcıya ikinci kez gitmesini engellemek.

Tasarlanmayan işler: bildirim metninin yazımı ve şablon dili, çeviri, izin ekranı, alıcı kümelerinin seçilmesi ve kanal sağlayıcılarının kendi iç işleyişi.

İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: toplam teslim oranı 0,99’un altına inmez (kaynak: bildirimin bir işlemin tek habercisi olması), acil bildirimin ortalama teslim gecikmesi 5 saniyeyi geçmez (kaynak: alıcının işlemi yaptığı anda haberi beklemesi), bir olay için alıcıya giden teslim sayısı birdir (kaynak: aynı haberi iki kanaldan almanın alıcı için hata sayılması).

Varsayımlar ve Ölçek

Kod Varsayım Değer Gerekçe
BI1 günlük etkin alıcı 5.000.000 bildirim alabilir durumdaki hesap
BI2 alıcı başına günlük bildirim 6 işlem haberleri ve hatırlatmalar
BI3 tepe çarpanı 5 bildirimlerin gün içinde birkaç saate yığılması
BI4 kanal payı 0,55 / 0,25 / 0,20 itme, kısa ileti, e-posta tercih dağılımı
BI5 kanal başına geçici hata 0,05 / 0,02 / 0,01 anlık ulaşılamama, yeniden deneme düzeltir
BI6 kanal başına kalıcı hata 0,06 / 0,02 / 0,01 geçersiz adres, kapalı izin, yeniden deneme düzeltmez
BI7 bildirim kaydı 300 bayt olay kimliği, alıcı, kanal, durum, zaman damgası
BI8 deneme bütçesi 3 bildirim başına yapılacak en çok dış çağrı
BI9 denemeler arası bekleme 30 saniye geçici hatanın geçmesine bırakılan süre

BI5 ile BI6 ayrımı bu vakanın belirleyici varsayımıdır. Geçici hata aynı kanalda tekrar denendiğinde düzelir; kalıcı hata düzelmez ve aynı kanalda harcanan her deneme boşa gider.

// bildirim/olcek.mjs — BI varsayim tablosundan cikan kabaca buyukluk hesabi
export const BI = {
  gunlukAlici: 5_000_000,        // BI1
  aliciBasinaBildirim: 6,        // BI2
  tepeCarpani: 5,                // BI3
  pay: { itme: 0.55, "kisa-ileti": 0.25, "e-posta": 0.20 },        // BI4
  gecici: { itme: 0.05, "kisa-ileti": 0.02, "e-posta": 0.01 },     // BI5
  kalici: { itme: 0.06, "kisa-ileti": 0.02, "e-posta": 0.01 },     // BI6
  kayitBayt: 300,                // BI7
  butce: 3,                      // BI8
  bekleme: 30,                   // BI9
};
const GUN = 86_400;

const gunluk = BI.gunlukAlici * BI.aliciBasinaBildirim;
const tepe = (gunluk / GUN) * BI.tepeCarpani;
console.log(`gunluk bildirim        ${gunluk}`);
console.log(`tepe bildirim/s        ${tepe.toFixed(2)}`);
console.log(`gunluk veri artisi GB  ${((gunluk * BI.kayitBayt) / 1e9).toFixed(2)}`);

console.log(`\n${"kanal".padEnd(11)}${"pay".padStart(6)}${"tepe cagri/s".padStart(14)}${"gecici".padStart(8)}` +
  `${"kalici".padStart(8)}${"3 denemeden sonra kalan".padStart(25)}`);
let karmaKalan = 0;
for (const k of Object.keys(BI.pay)) {
  const kalan = BI.kalici[k] + BI.gecici[k] ** BI.butce;
  karmaKalan += BI.pay[k] * kalan;
  console.log(`${k.padEnd(11)}${BI.pay[k].toFixed(2).padStart(6)}${(tepe * BI.pay[k]).toFixed(2).padStart(14)}` +
    `${BI.gecici[k].toFixed(2).padStart(8)}${BI.kalici[k].toFixed(2).padStart(8)}${kalan.toFixed(6).padStart(25)}`);
}
console.log(`\ntek kanalda ${BI.butce} deneme -> karma kalan hata ${karmaKalan.toFixed(6)}, ` +
  `teslim orani ${(1 - karmaKalan).toFixed(4)}`);
console.log(`esik 0.9900 -> tek kanal ${1 - karmaKalan >= 0.99 ? "gecer" : "gecmez"}; ` +
  `acigi ${((0.99 - (1 - karmaKalan)) * 100).toFixed(2)} puan`);
console.log(`ulasilamayan bildirim: tepe ${(tepe * karmaKalan).toFixed(2)}/s, ` +
  `gunde ${Math.round(gunluk * karmaKalan)}`);
console.log(`kademeli en kotu teslim = (${BI.butce} - 1) x ${BI.bekleme} s = ${(BI.butce - 1) * BI.bekleme} s`);
gunluk bildirim        30000000
tepe bildirim/s        1736.11
gunluk veri artisi GB  9.00

kanal         pay  tepe cagri/s  gecici  kalici  3 denemeden sonra kalan
itme         0.55        954.86    0.05    0.06                 0.060125
kisa-ileti   0.25        434.03    0.02    0.02                 0.020008
e-posta      0.20        347.22    0.01    0.01                 0.010001

tek kanalda 3 deneme -> karma kalan hata 0.040071, teslim orani 0.9599
esik 0.9900 -> tek kanal gecmez; acigi 3.01 puan
ulasilamayan bildirim: tepe 69.57/s, gunde 1202129
kademeli en kotu teslim = (3 - 1) x 30 s = 60 s

Bu sayılar hesap sınıfındadır. Üçü tasarımı belirliyor. Birincisi, tepe yük kanallara eşit dağılmıyor: itme kanalı saniyede 954,86 çağrı alırken e-posta 347,22 alıyor, yani en kalabalık kanal en yüksek kalıcı hata oranına sahip olan kanaldır. İkincisi, aynı kanalda üç deneme geçici hatayı pratikte siliyor (itmede 0,05 üzeri üç, yani 0,000125) ama kalıcı hataya dokunmuyor; kalan hata neredeyse tümüyle BI6’dan geliyor. Üçüncüsü, tek kanallı tasarımın teslim oranı 0,9599 çıkıyor ve 0,99 eşiğinin 3,01 puan altında kalıyor: günde 1.202.129 bildirim hiç ulaşmıyor. Eşik bir kanal sırası kararını zorunlu kılıyor.

Tasarım

Alma ucu ve kuyruk. Bildirim isteği eşzamansız kabul edilir; Uygulama Katmanı ve Servis Etkileşimi kursunun Mesaj Kuyrukları ve Rekabet Eden Tüketiciler dersleri buradaki yapıyı verir. Parametre öncelik sınıflarıdır: aynı kursun Öncelikli Kuyruk dersindeki iki sınıf kullanılır, acil bildirim ve hatırlatma, çünkü eşiklerden yalnız biri gecikme içerir.

Kanal başına bölme kalıbı. Dayanıklılık ve Güvenilirlik kursunun Bölme Kalıbı dersindeki kaynak havuzu kanal başına ayrılır ve havuz payları tepe çağrı hızına göre verilir: 954,86, 434,03 ve 347,22 çağrı/s. Ayrımın gerekçesi, bir kanalın yavaşlamasının öteki kanalların çağrılarını bekletmemesidir.

Kanal başına devre kesici. Aynı kursun Devre Kesici dersindeki eşik burada bir sayaç seçimidir: devre kesici yalnız geçici hataları sayar. Kalıcı hata alıcıya özgüdür ve kanalın sağlığı hakkında bilgi taşımaz; kalıcı hatalar sayaca girseydi itme kanalının devresi yüzde 6’lık bir taban yüzünden sürekli açılırdı.

Deneme bütçesinin yerleşimi. Yeniden Deneme ve Fırtına Riski dersindeki deneme bütçesi 3’tür ve bu vakanın kararı bütçenin nereye harcandığıdır: aynı kanala mı, sıradaki kanala mı. Ölçüm bölümü bu kararı sayıya çeviriyor.

Tekillik. Dayanıklılık ve Güvenilirlik kursunun Etkisiz İşlemler dersindeki tekillik anahtarının kapsamı burada (olay kimliği, alıcı) ikilisidir; kanal kapsama girmez, çünkü aynı olayın iki kanaldan gitmesi tam olarak engellenmek istenen şeydir.

Bilerek kullanılmayan kalıp. Sıralı konvoy (Uygulama Katmanı ve Servis Etkileşimi, Kuyruklar ve İş Akışları) bu tasarımda yoktur: iki bildirim arasında sıra kısıtı yoktur, her biri bağımsız bir olayın habercisidir ve şerit koymak koşutluğu bedelsiz yere keserdi.

Kanal Sırası Ölçülüyor

Ölçüm süreç içi bir modeldir: gerçek kanal, dış servis ya da ağ yoktur; her deneme bir olasılık, tur soyut bir adımdır ve bir tur BI9 ile 30 saniyedir. Model bir dakikalık tepe penceresini alır: 104.167 bildirim. Üç düzen aynı 3 çağrılık bütçeyi farklı yerlere harcar — tek kanal üçünü de tercih edilen kanala, paralel üçünü aynı anda üç kanala, kademeli birini tercih edilen kanala ve kalanları sırayla ötekilere.

// bildirim/dagitim.mjs — kanal sirasinin surec ici modeli. Gercek kanal, dis servis ya da ag
// yoktur: her deneme bir olasilik, tur soyut bir adimdir (BI9: tur basina 30 s bekleme).
const KANAL = ["itme", "kisa-ileti", "e-posta"];
const PAY = { itme: 0.55, "kisa-ileti": 0.25, "e-posta": 0.20 };        // BI4
const GECICI = { itme: 0.05, "kisa-ileti": 0.02, "e-posta": 0.01 };     // BI5
const KALICI = { itme: 0.06, "kisa-ileti": 0.02, "e-posta": 0.01 };     // BI6
const BUTCE = 3, BEKLEME = 30;                                          // BI8, BI9
const BILDIRIM = 104_167;                    // hesap: tepe 1736,11 bildirim/s x 60 s penceresi

function uretec(tohum) {                     // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

export function kos({ duzen, tohum, carpan = 1, dusen = null }) {
  const rnd = uretec(tohum);
  const s = { teslim: 0, disCagri: 0, yinelenen: 0, turToplam: 0 };
  for (let i = 0; i < BILDIRIM; i += 1) {
    let u = rnd(), tercih = KANAL[KANAL.length - 1];
    for (const k of KANAL) { if (u < PAY[k]) { tercih = k; break; } u -= PAY[k]; }
    const kalici = {};
    for (const k of KANAL) kalici[k] = k === dusen || rnd() < Math.min(1, KALICI[k] * carpan);
    const sira = duzen === "tek kanal" ? [tercih, tercih, tercih]
      : [tercih, ...KANAL.filter((k) => k !== tercih)];

    let ulasan = 0, ilkTur = null;
    for (let t = 0; t < BUTCE; t += 1) {
      const k = sira[t];
      s.disCagri += 1;
      const basarili = kalici[k] === false && rnd() >= Math.min(1, GECICI[k] * carpan);
      if (basarili) {
        ulasan += 1;
        if (ilkTur === null) ilkTur = duzen === "paralel" ? 0 : t;
        if (duzen !== "paralel") break;
      }
    }
    if (ulasan > 0) { s.teslim += 1; s.turToplam += ilkTur; s.yinelenen += ulasan - 1; }
  }
  s.oran = s.teslim / BILDIRIM;
  return s;
}

const DUZEN = ["tek kanal", "paralel", "kademeli"];
console.log(`model: ${BILDIRIM} bildirim, ${KANAL.length} kanal, deneme butcesi ${BUTCE}, tohum 20260730`);
console.log(`\n${"duzen".padEnd(11)}${"teslim orani".padStart(14)}${"dis cagri".padStart(11)}` +
  `${"cagri/bildirim".padStart(16)}${"yinelenen".padStart(11)}${"ort. teslim s".padStart(15)}`);
const R = {};
for (const d of DUZEN) {
  const r = (R[d] = kos({ duzen: d, tohum: 20260730 }));
  console.log(`${d.padEnd(11)}${r.oran.toFixed(4).padStart(14)}${String(r.disCagri).padStart(11)}` +
    `${(r.disCagri / BILDIRIM).toFixed(3).padStart(16)}${String(r.yinelenen).padStart(11)}` +
    `${((r.turToplam / r.teslim) * BEKLEME).toFixed(2).padStart(15)}`);
}

console.log(`\n${"hata carpani".padEnd(13)}${DUZEN.map((d) => `${d} orani`.padStart(19)).join("")}`);
for (const c of [1, 2, 3]) {
  const satir = DUZEN.map((d) => kos({ duzen: d, tohum: 20260730, carpan: c }).oran.toFixed(4).padStart(19));
  console.log(`${`x${c}`.padEnd(13)}${satir.join("")}`);
}

console.log(`\n${"itme kanali tumden dustu".padEnd(25)}${DUZEN.map((d) => d.padStart(12)).join("")}`);
const dusuk = DUZEN.map((d) => kos({ duzen: d, tohum: 20260730, dusen: "itme" }));
console.log(`${"teslim orani".padEnd(25)}${dusuk.map((r) => r.oran.toFixed(4).padStart(12)).join("")}`);
console.log(`${"dis cagri".padEnd(25)}${dusuk.map((r) => String(r.disCagri).padStart(12)).join("")}`);

const tek = R["tek kanal"], par = R["paralel"], kad = R["kademeli"];
console.log(`\nkademeli - tek kanal teslim orani farki = ${((kad.oran - tek.oran) * 100).toFixed(2)} puan`);
console.log(`paralelin bedeli: dis cagri x${(par.disCagri / kad.disCagri).toFixed(2)}, ` +
  `${par.yinelenen} yinelenen teslim, bildirim basina ${(par.yinelenen / BILDIRIM).toFixed(2)} fazladan`);
console.log(`paralelin kazanci: ort. teslim ${((par.turToplam / par.teslim) * BEKLEME).toFixed(2)} s, ` +
  `kademelide ${((kad.turToplam / kad.teslim) * BEKLEME).toFixed(2)} s`);
model: 104167 bildirim, 3 kanal, deneme butcesi 3, tohum 20260730

duzen        teslim orani  dis cagri  cagri/bildirim  yinelenen  ort. teslim s
tek kanal          0.9590     116305           1.117          0           1.08
paralel            0.9999     312501           3.000     190861           0.00
kademeli           0.9999     112357           1.079          0           2.35

hata carpani     tek kanal orani      paralel orani     kademeli orani
x1                        0.9590             0.9999             0.9999
x2                        0.9199             0.9993             0.9994
x3                        0.8790             0.9979             0.9979

itme kanali tumden dustu    tek kanal     paralel    kademeli
teslim orani                   0.4443      0.9993      0.9991
dis cagri                      220661      312501      166806

kademeli - tek kanal teslim orani farki = 4.09 puan
paralelin bedeli: dis cagri x2.78, 190861 yinelenen teslim, bildirim basina 1.83 fazladan
paralelin kazanci: ort. teslim 0.00 s, kademelide 2.35 s

Bu sayılar ölçüm sınıfındadır; tohum 20260730 ile yeniden üretilir.

İlk tablo bütçenin yerleşimini fiyatlandırıyor. Tek kanal 0,9590 teslim oranı veriyor ve hesapla bulunan 0,9599 ile aynı büyüklükte; kademeli aynı üç çağrılık bütçeyle 0,9999’a çıkıyor, fark 4,09 puan. Dikkat çekici olan ikinci sütundur: kademeli düzen bunu daha az dış çağrıyla yapıyor, bildirim başına 1,079 çağrı, tek kanalın 1,117 çağrısının altında. Kalıcı hataya çarpan bir bildirim tek kanallı düzende üç çağrıyı da boşa harcar; kademeli düzende ikinci çağrı işi bitirir. Yeniden denemenin yeri, sayısından önemlidir.

İkinci tablo hata oranlarının duyarlılığını veriyor. Bütün kanalların hata oranları üç katına çıktığında tek kanal 0,8790’a düşüyor, kademeli 0,9979’da kalıyor. Kanal çeşitliliğinin kazancı hata oranı büyüdükçe artıyor: fark 4,09 puandan 11,89 puana çıkıyor.

Elenen Alternatif: Paralel Dağıtım

Paralel düzen teslim oranında kademeliyle aynı sonucu veriyor (0,9999) ve gecikmede onu geçiyor: ortalama teslim 0,00 saniye, kademelide 2,35 saniye. Buna karşın iki sayıyla eleniyor.

Birincisi, dış çağrı sayısı kademelinin 2,78 katıdır ve bildirim başına 3,000 çağrıda sabittir; kanal başarısızlığı düşse bile azalmaz, çünkü paralel düzen sonucu beklemeden hepsini gönderir. İkincisi ve belirleyicisi, 190.861 yinelenen teslim üretir: bildirim başına 1,83 fazladan teslim. Bu doğrudan işlevsel olmayan gereksinimin ihlalidir — alıcı aynı haberi ortalama üç kanaldan birden alır.

Alternatifin kazandığı da yazılır: paralel düzen 60 saniyelik en kötü teslim penceresini sıfırlar. Gecikme eşiği 5 saniye yerine 1 saniye olsaydı ve “tek teslim” gereksinimi kalksaydı seçim tersine dönerdi; iki kanalın aynı anda denendiği bir orta yol da bu eşikte savunulabilir olurdu.

Arıza Davranışı ve Feda Edilen

Arıza senaryosu bir kanalın tümden durmasıdır: itme kanalı bütün alıcılar için başarısız oluyor. Üçüncü tablo sonucu veriyor. Tek kanallı düzen 0,4443’e düşüyor — bildirimlerin yarıdan fazlası hiç ulaşmıyor — ve dış çağrısı 220.661’e, yani normalin 1,90 katına çıkıyor, çünkü boşa giden denemeler bütçeyi sonuna kadar kullanıyor. Kademeli düzen 0,9991’de kalıyor ve dış çağrısı 166.806’ya çıkıyor. Kanal başına devre kesicinin görevi burada görünür: itme kanalının devresi açıldığında sıra doğrudan ikinci kanaldan başlar ve arızanın eklediği 54.449 boşa çağrı (166.806 eksi 112.357) yapılmaz.

Feda edilen tek cümlededir: kademeli sıra, teslim oranını gecikmeyle satın alır — ilk kanalı kalıcı hataya çarpan alıcı bildirimi 30 saniye, ikincisi de başarısızsa 60 saniye geç alır.

Özet

  • Kanal hatası ikiye ayrılmadan tasarım yapılamaz: geçici hata üç denemede siliniyor (0,05 üzeri üç), kalıcı hata aynı kanalda hiç azalmıyor ve kalan hatanın tamamına yakınını o oluşturuyor.
  • Tek kanallı düzen 0,9599 hesaplanmış, 0,9590 ölçülmüş teslim oranı veriyor; 0,99 eşiğinin 3,01 puan altında ve günde 1.202.129 bildirim ulaşmıyor.
  • Kademeli sıra aynı üç çağrılık bütçeyle 0,9999 veriyor ve bunu daha az dış çağrıyla yapıyor: bildirim başına 1,079 çağrı, tek kanalın 1,117 çağrısına karşı.
  • Hata oranları üç katına çıktığında tek kanal 0,8790’a iniyor, kademeli 0,9979’da kalıyor; çeşitliliğin kazancı 4,09 puandan 11,89 puana çıkıyor.
  • Paralel dağıtım aynı teslim oranını veriyor ama 2,78 kat dış çağrı ve bildirim başına 1,83 fazladan teslim üretiyor; tek teslim gereksinimini ihlal ettiği için eleniyor.
  • İtme kanalı tümden düştüğünde tek kanal 0,4443’e iniyor, kademeli 0,9991’de kalıyor; bedeli bildirimin 30 ya da 60 saniye gecikmesidir.

Sonraki Adım

Bu iki vakada da sistemin işi bir isteği karşılamaktı ve başarısızlık kötü bir sonuçtu. Sonraki vaka tersini tasarlar: işi isteği reddetmek olan bir bileşen. Reddetme kararı paylaşılan bir sayaca bakarak verilir ve sayaç dağıtıktır; iki soru buradan çıkar. Sayacın tuttuğu pencere hangi biçimde tanımlanırsa sınırın üstüne çıkılabilir, ve eşzamanlı okuyup yazan düğümler sayaçta ne kadar sapma üretir. İkisi de sayılabilir.

İ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