İçeriğe geç
academia.sh

Ders 07 / 15

Görev Kuyrukları ve Arka Plan İşleri

Uzun bir işin istek yolundan çıkarılmasının ölçülmesi: rapor işinin süresinin giriş kursunun sayılarından türetilmesi, aynı yuva havuzunu paylaşan kısa ve uzun işlerde kısa isteğin kuyruk kuyruğunun sayılması, ayrı bir görev kuyruğuna alındığında hangi ölçünün düzeldiği ve ölçekleme biriminin kopyadan işçiye geçmesi.

İçindekiler

Önceki dersin bütün olayları birbirinin eşiydi: her durum olayı aynı büyüklükte, aynı işi yapan, milisaniyeler süren bir kayıttı. Birikme hesabı da buna dayanıyordu — kapasite tek bir sayıyla ifade edilebilir, çünkü işler tek tiptir. Aynı sistemde bu varsayımın tutmadığı bir iş var: satıcının istediği dönem raporu binlerce gönderi kaydını tarar ve saniyelerce sürer. Böyle bir iş isteğin içinde çalıştırıldığında yalnız kendi süresini harcamaz, arkasındaki kısa istekleri de bekletir.

Bu ders o işi istek yolundan çıkarır. Görev kuyruğu (task queue) mesaj kuyruğunun aynı mekaniğidir; ayrıldığı yer içeriğidir — kuyruğa yazılan şey bir olay bildirimi değil, birinin yapılmasını istediği bir iştir. İşçi süreçleri, çekme tabanlı dağıtım, gözetim, zarif kapanma ve uzun işin parçalara bölünüp ilerlemesinin yazılması Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda kuruldu; hiçbiri yeniden anlatılmaz. Burada ölçülen tek şey ayırmanın kapasite karşılığıdır: kısa isteğin beklemesi ne kadar düzelir ve tasarımın ölçekleme birimi ne olur.

İşin Boyu Nereden Geliyor

Uzun işin süresi uydurulmaz, K01’in sayılarından çıkar. Kabaca Büyüklük Hesabı dersi günde 4000 fatura satırı buluyordu ve her satır bir satıcının bir günüydü; satıcı sayısı 4000’dir. Günde 400.000 gönderi oluştuğuna göre satıcı başına günlük gönderi 100’dür. Ücretlendirme dönemi V11 gereği 30 gün olduğuna göre bir dönem raporu 3000 gönderi kaydı tarar. Aynı ders tarama hızını da veriyor: 833,33 kayıt/s, yani kayıt başına 1,20 ms. Rapor işi böylece 3,60 saniye sürer. Bu sayıların hepsi hesap sınıfındadır.

Karşılaştırılan kısa iş takip sorgusudur ve tek bir kayıt okur, yani bir tarama biriminde biter. Aradaki oran doğrudan 3000’dir. Modelin tur birimi de buradan seçilmiştir: bir tur 1,20 ms’dir ve model içindeki hiçbir süre ölçülmez, turdan çevrilir.

İki varsayım gerekir ve ikisi de bu kursun kendi varsayımıdır; K01’in tablosuna eklenmez.

K2 — sabah penceresi: 30 dakika. Gerekçe: K01’in V10 satırı “satıcı raporu sabah ister” diyor; rapor talepleri iş gününün açılışına sıkışır.

K3 — pencerede rapor isteyen satıcı oranı: 0,50. Gerekçe: dönem raporu ayın belirli günlerinde toplu olarak istenir ve o günlerde satıcıların yarısının uğradığı varsayılır. Duyarlılığı aşağıdaki tabloda 0,25 satırlarıyla ölçülüyor.

Okuma yükü K01 ile K02’den gelir: tepe okuma 416,67 istek/s, kopya sayısı 4 — Trafik Katmanı kursunun oturum yapışkanlığı dersi bu sayıyı 3’ten 4’e çıkarmıştı. Kopya başına düşen okuma 104,17 istek/s’dir.

Aynı Havuzda İki İş

Düzenek tek bir kopyayı süreç içi bir modelle kurar. Kopyanın belirli sayıda yuvası vardır; her yuva bir anda tek bir isteği tutar, boşalınca kuyruğun başındakini alır. Varışlar sabit tohumlu iki bağımsız üreteçten gelir, dolayısıyla bütün tasarımlar aynı istek dizisiyle karşılaşır. Model olduğu için sonuçlar belirlenimlidir ve makineden bağımsızdır.

// gorev/havuz.mjs — uzun isin istek yolunda kalmasi ile gorev kuyruguna alinmasinin
// karsilastirilmasi. MODEL: tur = 1,20 ms (K01: toplu is tarama 833,33 kayit/s), varislar
// sabit tohumlu bir uretecten gelir. Sonuclar belirlenimlidir; sure olculmez, turden cevrilir.
const TUR_MS = 1.2;                // K01: kayit basina 1,20 ms
const OKUMA_TEPE = 416.67;         // K01: tepe okuma istek/s
const KOPYA = 4;                   // K02 oturum yapiskanligi dersi: 4 kopya
const SATICI = 4000;               // K01: gunluk fatura satiri = satici sayisi
const RAPOR_KAYIT = 3000;          // K01: satici basina 100 gonderi/gun x V11 = 30 gun
const K2_PENCERE_DK = 30;          // bu kursun varsayimi K2: sabah penceresi
const K3_ORAN = 0.5;               // bu kursun varsayimi K3: pencerede rapor isteyen satici orani
const TOHUM = 20260730;

const TUR = Math.round((K2_PENCERE_DK * 60 * 1000) / TUR_MS);
const takipP = (OKUMA_TEPE / KOPYA) * (TUR_MS / 1000);
const raporSayisi = (oran) => (SATICI * oran) / KOPYA;

function uretec(tohum) {           // splitmix32: iki akis icin bagimsiz varis dizisi
  let a = tohum >>> 0;
  return () => {
    a = (a + 0x9e3779b9) >>> 0;
    let z = Math.imul(a ^ (a >>> 16), 0x21f0aaad) >>> 0;
    z = Math.imul(z ^ (z >>> 15), 0x735a2d97) >>> 0;
    return ((z ^ (z >>> 15)) >>> 0) / 4294967296;
  };
}

function kosum({ yuva, ayri, isci, oran = K3_ORAN }) {
  const raporP = raporSayisi(oran) / TUR;
  const rastTakip = uretec(TOHUM), rastRapor = uretec(TOHUM + 1);
  const meshul = new Array(yuva).fill(0), raporMeshul = new Array(isci).fill(0);
  const kuyruk = [], gorev = [], bekleme = [], raporSure = [];
  let gelenRapor = 0;
  for (let t = 0; t < TUR; t += 1) {
    if (rastTakip() < takipP) kuyruk.push({ t, sure: 1 });
    if (rastRapor() < raporP) { gelenRapor += 1; (ayri ? gorev : kuyruk).push({ t, sure: RAPOR_KAYIT, rapor: true }); }
    for (let i = 0; i < yuva; i += 1) {
      if (meshul[i] > 0) meshul[i] -= 1;
      if (meshul[i] === 0 && kuyruk.length > 0) {
        const is = kuyruk.shift();
        if (is.rapor) raporSure.push(t - is.t + is.sure); else bekleme.push(t - is.t);
        meshul[i] = is.sure;
      }
    }
    for (let i = 0; i < isci; i += 1) {
      if (raporMeshul[i] > 0) raporMeshul[i] -= 1;
      if (raporMeshul[i] === 0 && gorev.length > 0) {
        const is = gorev.shift();
        raporSure.push(t - is.t + is.sure);
        raporMeshul[i] = is.sure;
      }
    }
  }
  const d = [...bekleme].sort((a, b) => a - b);
  const y = (p) => d[Math.min(d.length - 1, Math.floor((p / 100) * d.length))];
  return { takip: bekleme.length, p50: y(50), p99: y(99), enUzun: d[d.length - 1],
    geciken: bekleme.filter((b) => b * TUR_MS > 100).length,
    gelenRapor, biten: raporSure.length,
    raporOrt: raporSure.reduce((a, b) => a + b, 0) / raporSure.length };
}

console.log(`tur = ${TUR_MS} ms, pencere ${K2_PENCERE_DK} dk = ${TUR} tur, ${KOPYA} kopya`);
console.log(`kopya basina takip ${(OKUMA_TEPE / KOPYA).toFixed(2)} istek/s, ` +
  `K3 = ${K3_ORAN} -> kopya basina beklenen rapor ${raporSayisi(K3_ORAN)}`);
console.log(`rapor isi = ${RAPOR_KAYIT} kayit = ${(RAPOR_KAYIT * TUR_MS / 1000).toFixed(2)} s\n`);

const satir = (ad, r) => console.log(`${ad.padEnd(24)} ${String(r.takip).padStart(7)} ` +
  `${(r.p50 * TUR_MS).toFixed(2).padStart(9)} ${(r.p99 * TUR_MS).toFixed(2).padStart(9)} ` +
  `${(r.enUzun * TUR_MS).toFixed(0).padStart(11)} ${String(r.geciken).padStart(9)} ` +
  `${(r.geciken / r.takip).toFixed(4).padStart(6)} ${`${r.biten}/${r.gelenRapor}`.padStart(9)} ` +
  `${(r.raporOrt * TUR_MS / 1000).toFixed(2).padStart(9)}`);

console.log("tasarim                    takip  p50 (ms)  p99 (ms)  en uzun(ms)  >100 ms     oran  biten/gelen  rapor (s)");
for (const yuva of [2, 4, 8]) satir(`paylasilan y=${yuva}`, kosum({ yuva, ayri: false, isci: 0 }));
for (const yuva of [2, 4]) satir(`paylasilan y=${yuva}, K3=0.25`, kosum({ yuva, ayri: false, isci: 0, oran: 0.25 }));
for (const yuva of [2, 4]) satir(`ayrilmis y=${yuva}, isci=2`, kosum({ yuva, ayri: true, isci: 2 }));

const enAz = (uret) => { for (let y = 1; y <= 12; y += 1) if (uret(y).geciken === 0) return y; return 0; };
const payYuva = enAz((yuva) => kosum({ yuva, ayri: false, isci: 0 }));
const ayriYuva = enAz((yuva) => kosum({ yuva, ayri: true, isci: 2 }));
const raporSaniye = raporSayisi(K3_ORAN) * KOPYA * (RAPOR_KAYIT * TUR_MS / 1000);
const isci = raporSaniye / (K2_PENCERE_DK * 60);
console.log(`\n100 ms ustu takip beklemesini sifirlayan en kucuk yuva: paylasilan ${payYuva}, ayrilmis ${ayriYuva}`);
console.log(`olcekleme birimi -> paylasilan ${payYuva * KOPYA} yuva (hepsi okuma yolunda), ` +
  `ayrilmis ${ayriYuva * KOPYA} yuva + gorev isciligi`);
console.log(`sistem geneli rapor isi = ${raporSaniye.toFixed(0)} isci-saniye / ` +
  `${K2_PENCERE_DK * 60} s = ${isci.toFixed(2)} isci (doluluk 1,00)`);
tur = 1.2 ms, pencere 30 dk = 1500000 tur, 4 kopya
kopya basina takip 104.17 istek/s, K3 = 0.5 -> kopya basina beklenen rapor 500
rapor isi = 3000 kayit = 3.60 s

tasarim                    takip  p50 (ms)  p99 (ms)  en uzun(ms)  >100 ms     oran  biten/gelen  rapor (s)
paylasilan y=2            187459      0.00   5887.20        9398     54392 0.2902   457/457      4.24
paylasilan y=4            187459      0.00    634.80        2808      3088 0.0165   457/457      3.61
paylasilan y=8            187459      0.00      0.00           0         0 0.0000   457/457      3.60
paylasilan y=2, K3=0.25   187459      0.00   2766.00        4984     15204 0.0811   223/223      3.77
paylasilan y=4, K3=0.25   187459      0.00      0.00        1240       293 0.0016   223/223      3.60
ayrilmis y=2, isci=2      187459      0.00      0.00           0         0 0.0000   457/457      4.14
ayrilmis y=4, isci=2      187459      0.00      0.00           0         0 0.0000   457/457      4.14

100 ms ustu takip beklemesini sifirlayan en kucuk yuva: paylasilan 7, ayrilmis 1
olcekleme birimi -> paylasilan 28 yuva (hepsi okuma yolunda), ayrilmis 4 yuva + gorev isciligi
sistem geneli rapor isi = 7200 isci-saniye / 1800 s = 4.00 isci (doluluk 1,00)

Otuz dakikada 187.459 takip isteği ve beklenen 500 rapora karşılık tohumun ürettiği 457 rapor geldi; sayı Bernoulli varışının bir gerçekleşmesidir ve bütün satırlarda aynıdır.

Doluluk Düşük, Kuyruk Uzun

Paylaşılan havuzun sayıları ilk bakışta tutarsız görünür. İki yuvalı bir kopyada rapor işi yuvaların yarısından azını kullanıyor: 457 rapor × 3,60 saniye = 1645 yuva-saniyesi, otuz dakikalık pencerede iki yuvanın verdiği 3600 yuva-saniyesinin yüzde 46’sı. Takip akışı ise yüzde 12,5 istiyor. Toplam doluluk yüzde 60’ın altında, yani havuz kapasite olarak yeterlidir.

Buna rağmen takip isteklerinin yüzde 29,02’si 100 milisaniyeden uzun bekliyor, p99 beklemesi 5887 ms ve en uzun bekleme 9398 ms. Neden yeterli kapasitenin bu tabloyu üretebildiği ortadadır: kuyruk sırayla boşalır ve önündeki iş 3000 tur sürüyorsa arkasındaki bir turluk iş de 3000 tur bekler. İki rapor aynı anda çalıştığında iki yuvanın ikisi de kapanır ve o süre boyunca gelen her takip isteği sıraya girer. Ortalama doluluk düşükken kuyruğun uzayabilmesi, işlerin süresinin dağılımından gelir; tek bir doluluk sayısı bu dağılımı saklar.

Yuva sayısı bunu düzeltir ama pahalıya düzeltir. Dört yuvada oran yüzde 1,65’e, p99 634,80 ms’ye iner; sekiz yuvada sıfırlanır. Aradaki eşik ölçülmüştür: 100 milisaniye üstü beklemeyi sıfırlayan en küçük yuva sayısı yedidir. Kısa isteğin tek başına ihtiyacı bir yuvadır — ayrılmış tasarımda ölçülen sayı budur. Yani altı yuva yalnız uzun işin arkasında sıra beklenmesin diye tutuluyor.

K3’ün duyarlılığı aynı yönü gösteriyor. Rapor isteyen satıcı oranı yarıya indiğinde iki yuvada etkilenen istek oranı yüzde 29,02’den yüzde 8,11’e, dört yuvada yüzde 1,65’ten yüzde 0,16’ya düşüyor — ama sıfırlanmıyor. Talebin azalması sorunun sıklığını azaltır, yapısını değiştirmez; tek bir uzun iş bile arkasındaki isteği kendi süresi kadar bekletir.

Ölçekleme Birimi

Ayrılmış tasarımda rapor işi kuyruğa yazılır ve çağıran bir iş kimliğiyle döner. Takip isteklerinin ölçüleri her yuva sayısında aynı çıkıyor: p99 sıfır, en uzun bekleme sıfır, 100 milisaniye üstü bekleyen istek yok. Uzun iş yoldan çekildiğinde kısa isteğin kuyruğu da kayboluyor.

Ödenen bedel rapor tarafındadır ve ölçülmüştür: iki işçiyle ortalama rapor süresi 3,60 saniye yerine 4,14 saniye. Rapor artık kendi kuyruğunda sıra bekliyor. Fark yüzde 15’tir ve istenirse işçi eklenerek kapatılır — çünkü işçi sayısı artık takip akışından bağımsız bir sayıdır.

Asıl sonuç son üç satırdadır. Paylaşılan tasarımda takip kuyruğunu sıfırlamak için kopya başına yedi yuva gerekiyor; dört kopyada 28 yuva. Bu yuvaların hepsi okuma yolundadır: her biri takip isteği alabilecek biçimde yapılandırılmış, okuma trafiğinin dağıtıldığı kopyalarda durur. Ayrılmış tasarımda takip için kopya başına bir yuva yetiyor — dört kopyada 4 yuva — ve rapor işi ayrı bir işçiliğe geçiyor. O işçiliğin büyüklüğü de aritmetiktir: sistem genelinde 2000 rapor × 3,60 saniye = 7200 işçi-saniyesi, otuz dakikalık pencerede 4,00 işçi eder ve bu sayı doluluk 1,00 demektir, yani gerçek bir tasarım pay bırakır.

Ayırmanın kazancı bir sayı değil, bir bağımsızlıktır. Paylaşılan tasarımda rapor talebi arttığında büyütülecek şey kopyadır; kopya büyütmek okuma yolunu da büyütür, oysa okuma yolunun kapasiteye ihtiyacı yoktu. Ayrılmış tasarımda rapor talebi işçi sayısını, okuma talebi kopya sayısını oynatır ve iki sayı birbirine bakmaz. Giriş kursunun tepe okuma hızı 416,67 istek/s değişmedi; değişen, o hızı karşılayan kopyaların ne kadar büyük olmak zorunda olduğudur — kopya başına yedi yuvadan bire.

Özet

  • Uzun işin boyu K01’den türetilir: satıcı başına 100 gönderi/gün × 30 gün = 3000 kayıt, tarama hızı 833,33 kayıt/s ile 3,60 saniye; takip isteği aynı ölçüde tek kayıttır.
  • Paylaşılan havuzda doluluk yüzde 60’ın altındayken bile takip isteklerinin yüzde 29,02’si 100 ms’den uzun bekliyor, p99 5887 ms: tek bir doluluk sayısı iş sürelerinin dağılımını saklar.
  • Kuyruğu sıfırlamanın bedeli yuvadır: 100 ms üstü beklemeyi sıfırlayan en küçük yuva sayısı paylaşılan tasarımda yedi, ayrılmış tasarımda birdir.
  • K3 varsayımı yarıya indiğinde etkilenen istek oranı yüzde 29,02’den 8,11’e düşüyor ama sıfırlanmıyor; talep sorunun sıklığını değiştirir, yapısını değiştirmez.
  • Ayrılmış tasarımda takip beklemesi her yuva sayısında sıfır; ödenen bedel iki işçiyle rapor süresinin 3,60 saniyeden 4,14 saniyeye çıkmasıdır.
  • Ölçekleme birimi ayrışıyor: paylaşılan tasarım dört kopyada 28 yuva ister, ayrılmış tasarım 4 yuva artı sistem genelinde 4,00 işçilik ister ve iki sayı birbirinden bağımsız oynar.

Sonraki Adım

Son satırdaki “4,00 işçi” bir kapasite değil bir alt sınırdır; doluluk bire eşit olduğunda kuyruk boşalmaz. Sayının kendisi de bir soruyu açık bırakıyor: işçi eklemek gerçekten toplama mı karşılık gelir. Önceki dersin birikme tablosunda tüketici kapasitesi tek bir sayıydı ve o sayının nereden geldiği sorulmamıştı; oysa kapasite tüketici sayısıyla tüketici başına hızın çarpımıdır ve çarpanlardan biri sonsuza kadar büyümez. Sonraki ders aynı kuyruktan çeken birden çok tüketiciyi ele alır: eklenen her tüketicinin boşalma süresine katkısını, katkının nerede durduğunu ve verimin karşılığında nelerin bozulduğunu ölçer.

İ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