Ders 19 / 30
Yatay Ölçekleme
Ölçüt tabanlı otomatik ölçeklemenin bir denetleyici olarak yazılması ve değişken yük altında koşturulması: ölçüt gecikmesi arttıkça salınımın, aşırı kopya-turun ve yetersiz kapasite turunun nasıl değiştiği, bekleme penceresi ile adım sınırının salınımı ne kadar düşürdüğü ve tepkiyi kaç tur yavaşlattığı.
İçindekiler
Önceki iki ders kümenin sabit kapasitesini paylaştırdı: kim nereye girer, sıkışınca kim çıkar. Üçüncü seçenek hiç sorulmadı — kopya sayısını değiştirmek. Bölgesel ölçüm ağının (kurgu) okuma toplayıcısı gün içinde dalgalanan bir yük görür; kopya sayısı sabit kaldığı sürece bu dalganın bedeli ya boş kapasite ya da kuyruktur.
Bu karar bir denetleyiciye devredilebilir: bir ölçüt izlenir, hedefle karşılaştırılır, kopya sayısı ayarlanır. Kaç kopya gerektiğinin aritmetiği burada tekrarlanmaz — kopya başına doyum verimi ve doluluk tavanı kapasite planlamasından gelen girdilerdir. Bu dersin ölçtüğü şey kararın kendisi: denetleyici, ölçütü gecikmeli gördüğünde ne yapıyor.
Denetleyicinin kuralı tek satırdır: istenen kopya, güncel kopya sayısının ölçülen dolulukla çarpılıp hedef dolulukla bölünmesidir. Bu satırdaki tuzak, güncel kopya sayısıyla eski bir ölçütün çarpılmasıdır. Ölçülen şey bir modeldir; gerçek küme koşturulmaz.
Denetleyici ve Ölçüt Gecikmesi
Yük serisi 120 turluk bir dizidir: 40 turluk bir dalga, 60. turda ani bir artış ve kendi üretecimizden gelen yüzde 8 bandında gürültü. Her tur için gereken kopya sayısı ayrıca hesaplanır ve denetleyicinin ürettiği kopya sayısı ona göre puanlanır.
// zamanlama/olcekleme.mjs — olcut tabanli yatay olcekleme denetleyicisi. MODELDIR, gercek // kume kosturulmaz. Kopya basina doyum verimi ve doluluk tavani M19'un girdisidir. export const DOYUM = 1176.47; // kopya basina istek/s (M19, hesap; burada girdi) export const HEDEF = 0.70; // hedef doluluk (M19'un kapasite payi; burada girdi) export const TUR = 120; // ZO25: bir tur bir olcum araligi export const MAKS = 12; // ZO27: kopya ust siniri const TOHUM = 20260805; // ZO26: yuk serisi kurgudur — 40 turluk dalga, 60. turda ani artis, ±%8 gurultu. export function seriUret() { let s = TOHUM % 2147483647; const r = () => (s = (s * 48271) % 2147483647) / 2147483647; return Array.from({ length: TUR }, (_, t) => { const dalga = 1200 + 900 * Math.sin((2 * Math.PI * t) / 40); const adim = t >= 60 && t < 90 ? 1800 : 0; return Math.max(200, Math.round((dalga + adim) * (0.92 + 0.16 * r()))); }); } export const SERI = seriUret(); export const gereken = (yuk) => Math.max(1, Math.ceil(yuk / (DOYUM * HEDEF))); // Denetleyici: gecikmeli olcutu GUNCEL kopya sayisiyla carpar. Gecikme buyudukce asiri duzeltir. export function kostur({ gecikme = 0, pencere = 1, adim = 0, seri = SERI } = {}) { let kopya = 2, sonYon = 0, dusuk = 0; const doluluklar = []; let salinim = 0, asiri = 0, yetersiz = 0, toplam = 0, adimYetersiz = 0; let acKapa = 0, enYuksek = 0, tepki = -1; for (let t = 0; t < seri.length; t++) { doluluklar.push(seri[t] / (kopya * DOYUM)); const olcut = doluluklar[Math.max(0, t - gecikme)]; let istenen = Math.min(MAKS, Math.max(1, Math.ceil((kopya * olcut) / HEDEF))); if (adim > 0) istenen = Math.min(kopya + adim, Math.max(kopya - adim, istenen)); if (istenen < kopya) { dusuk++; if (dusuk < pencere) istenen = kopya; } else dusuk = 0; const yon = Math.sign(istenen - kopya); if (yon !== 0) { acKapa++; if (sonYon !== 0 && yon !== sonYon) salinim++; sonYon = yon; } kopya = istenen; const g = gereken(seri[t]); toplam += kopya; asiri += Math.max(0, kopya - g); if (kopya < g) { yetersiz++; if (t >= 60 && t < 90) adimYetersiz++; } if (t >= 60 && tepki < 0 && kopya >= g) tepki = t - 60; enYuksek = Math.max(enYuksek, kopya); } return { salinim, acKapa, asiri, yetersiz, toplam, adimYetersiz, enYuksek, tepki }; } if (process.argv[1].endsWith("olcekleme.mjs")) { const g = SERI.map(gereken); console.log(`model: ${TUR} tur, yuk ${Math.min(...SERI)}–${Math.max(...SERI)} istek/s ` + `(tohum ${TOHUM}); kopya basina ${DOYUM} istek/s, hedef doluluk %${HEDEF * 100}`); console.log(`gereken kopya ${Math.min(...g)}–${Math.max(...g)}, ideal toplam ` + `${g.reduce((a, b) => a + b, 0)} kopya-tur\n`); const s = (x, n = 17) => String(x).padStart(n); console.log("olcut gecikmesi taraniyor (bekleme penceresi ve adim siniri yok):"); console.log(`${"gecikme".padEnd(10)}${s("degisiklik")}${s("salinim")}${s("asiri kopya-tur")}` + `${s("yetersiz tur")}${s("toplam kopya-tur")}${s("en yuksek kopya", 16)}`); for (const gecikme of [0, 1, 2, 3, 4, 5, 6]) { const o = kostur({ gecikme }); console.log(`${String(gecikme).padEnd(10)}${s(o.acKapa)}${s(o.salinim)}${s(o.asiri)}` + `${s(o.yetersiz)}${s(o.toplam)}${s(o.enYuksek)}`); } }
model: 120 tur, yuk 278–3915 istek/s (tohum 20260805); kopya basina 1176.47 istek/s, hedef doluluk %70 gereken kopya 1–5, ideal toplam 301 kopya-tur olcut gecikmesi taraniyor (bekleme penceresi ve adim siniri yok): gecikme degisiklik salinim asiri kopya-tur yetersiz tur toplam kopya-tur en yuksek kopya 0 14 8 0 0 301 5 1 63 30 52 29 319 8 2 67 23 186 40 422 12 3 59 17 225 40 457 12 4 56 15 261 44 489 12 5 53 13 286 42 515 12 6 43 11 275 41 510 12
Gecikme Salınımı Büyütüyor, Sonra Gizliyor
Gecikmesiz satır bir alt sınır veriyor: 14 değişiklik, sıfır aşırı kopya-tur, sıfır yetersiz tur ve tam 301 kopya-tur, yani idealin kendisi. Ölçüt anında geldiğinde devredilen karar kusursuz çalışıyor. Sekiz salınım da yükün kendi dalgasından gelir, denetleyicinin hatasından değil.
Tek turluk gecikme bu tabloyu bozuyor. Değişiklik sayısı 14’ten 63’e, salınım 8’den 30’a çıkıyor; 29 tur yetersiz kapasiteyle geçiyor ve en yüksek kopya sayısı beşten sekize tırmanıyor. Nedeni denetleyicinin kuralındadır: kopya sayısı bir tur önce artırılmıştır, ölçüt hâlâ artıştan önceki doluluğu göstermektedir ve denetleyici aynı yükü ikinci kez düzeltir. Sistem kendi son kararının ölçüte yansıyıp yansımadığını bilmiyor ve bilmediği tek kalem bu.
Gecikme büyüdükçe okunması gereken sütun değişiyor. Salınım 30’dan 11’e iniyor ve bu bir iyileşme değildir. Aşırı kopya-tur 52’den 275’e çıkıyor, toplam kaynak 319’dan 510 kopya-tura tırmanıyor ve en yüksek kopya sayısı üst sınıra yapışıyor. Denetleyici artık salınmıyor, çünkü tavana çıkıp orada kalıyor: yetersiz tur sayısı 41’de duruyor, yani hem kaynak idealin 1,7 katı hem de turların üçte biri yetersiz. Salınımın azalması tek başına bir başarı göstergesi değildir; yanında aşırı kopya-tur ve yetersiz tur okunmadıkça yanlış yorumlanır.
Gecikmenin kendisi bir ayar değil, bir toplamdır. Ölçüt önce kapsülde üretilir, bir aralık boyunca biriktirilir, toplayıcıya aktarılır, denetleyicinin okuduğu ortalamaya girer ve ancak ondan sonra karara dönüşür. Bu adımların her biri bir tur ekler. Tablodaki gecikme sütunu bu yüzden bir seçim değil, izleme düzeninin sonucudur: toplama aralığını kısaltmak gecikmeyi düşürür ve karşılığında ölçüt sayısını, saklama yükünü ve gürültüyü artırır. Devredilen kararın maliyeti burada iki ayrı bütçeye bölünür — biri kopya-tur, öteki ölçüt.
İki Söndürücü: Bekleme Penceresi ve Adım Sınırı
Salınıma karşı iki ayar vardır. Bekleme penceresi küçültme kararını geciktirir: istenen kopya sayısı arka arkaya belirli tur boyunca düşük gelmedikçe küçültme uygulanmaz. Adım sınırı bir turdaki değişimin büyüklüğünü kısar. İkisi de aynı gecikmede, üç tur ölçüt gecikmesiyle ölçülür ve tepki ayrıca gürültüsüz bir basamak testiyle sayılır.
// zamanlama/denge.mjs — bekleme penceresi ve adim siniri: salinimin dususu ile tepkinin // yavaslamasi birlikte sayilir. Denetleyici ve yuk serisi onceki dosyadan gelir. import { kostur } from "./olcekleme.mjs"; const GECIKME = 3; // ZO28: olcut uc tur gecikmeli geliyor const s = (x, n = 17) => String(x).padStart(n); const satir = (ad, o) => console.log(`${ad.padEnd(22)}${s(o.acKapa)}${s(o.salinim)}${s(o.asiri)}` + `${s(o.yetersiz)}${s(o.adimYetersiz)}${s(o.toplam)}`); console.log(`olcut gecikmesi ${GECIKME} turda sabit; sonuclar 120 turluk kosumdan:`); console.log(`${"ayar".padEnd(22)}${s("degisiklik")}${s("salinim")}${s("asiri kopya-tur")}` + `${s("yetersiz tur")}${s("adimda yetersiz")}${s("toplam kopya-tur")}`); satir("ayarsiz", kostur({ gecikme: GECIKME })); // ZO29: bekleme penceresi — kucultme karari ancak w turdur ust uste dusuk gelirse uygulanir. for (const pencere of [2, 3, 5, 8]) satir(`bekleme penceresi ${pencere}`, kostur({ gecikme: GECIKME, pencere })); // ZO30: adim siniri — bir turda kopya sayisi en fazla a kadar degisir. for (const adim of [1, 2]) satir(`adim siniri ${adim}`, kostur({ gecikme: GECIKME, adim })); satir("pencere 3 + adim 1", kostur({ gecikme: GECIKME, pencere: 3, adim: 1 })); satir("pencere 5 + adim 1", kostur({ gecikme: GECIKME, pencere: 5, adim: 1 })); // Gurultusuz basamak testi: 60 tur 800 istek/s, sonra 60 tur 3200 istek/s (gereken 1 -> 4). const BASAMAK = Array.from({ length: 120 }, (_, t) => (t < 60 ? 800 : 3200)); console.log("\nbasamak testi — yuk 60. turda 800'den 3200 istek/s'ye ciktiginda tepki turu:"); console.log(`${"ayar".padEnd(22)}${s("gecikme 0")}${s("gecikme 3")}${s("gecikme 6")}`); for (const [ad, ek] of [["ayarsiz", {}], ["adim siniri 1", { adim: 1 }], ["adim siniri 2", { adim: 2 }], ["pencere 3 + adim 1", { pencere: 3, adim: 1 }]]) console.log(`${ad.padEnd(22)}` + [0, 3, 6] .map((g) => s(kostur({ ...ek, gecikme: g, seri: BASAMAK }).tepki)).join("")); console.log("\nayni iki ayar gecikme yokken (gecikme 0):"); satir("ayarsiz", kostur({ gecikme: 0 })); satir("pencere 3 + adim 1", kostur({ gecikme: 0, pencere: 3, adim: 1 })); const a = kostur({ gecikme: GECIKME }), b = kostur({ gecikme: GECIKME, pencere: 3, adim: 1 }); console.log(`\nsalinim ${a.salinim} -> ${b.salinim} (%${(100 * (1 - b.salinim / a.salinim)).toFixed(0)} dusus), ` + `adim penceresinde yetersiz tur ${a.adimYetersiz} -> ${b.adimYetersiz}`); console.log(`kaynak ${a.toplam} -> ${b.toplam} kopya-tur (ideal 301); ` + `denetleyicinin okudugu olcut sayisi 120, her biri ${GECIKME} tur gecikmeli`);
olcut gecikmesi 3 turda sabit; sonuclar 120 turluk kosumdan: ayar degisiklik salinim asiri kopya-tur yetersiz tur adimda yetersiz toplam kopya-tur ayarsiz 59 17 225 40 15 457 bekleme penceresi 2 46 17 257 37 12 493 bekleme penceresi 3 45 15 315 33 13 552 bekleme penceresi 5 40 13 343 31 9 596 bekleme penceresi 8 31 11 371 23 7 632 adim siniri 1 71 15 98 35 14 347 adim siniri 2 71 15 173 35 15 410 pencere 3 + adim 1 59 13 122 32 11 385 pencere 5 + adim 1 53 11 152 27 10 417 basamak testi — yuk 60. turda 800'den 3200 istek/s'ye ciktiginda tepki turu: ayar gecikme 0 gecikme 3 gecikme 6 ayarsiz 0 3 6 adim siniri 1 2 5 8 adim siniri 2 1 4 7 pencere 3 + adim 1 2 5 8 ayni iki ayar gecikme yokken (gecikme 0): ayarsiz 14 8 0 0 0 301 pencere 3 + adim 1 14 6 17 0 0 318 salinim 17 -> 13 (%24 dusus), adim penceresinde yetersiz tur 15 -> 11 kaynak 457 -> 385 kopya-tur (ideal 301); denetleyicinin okudugu olcut sayisi 120, her biri 3 tur gecikmeli
Salınımın Düşüşü ve Tepkinin Yavaşlaması
İki ayar aynı sorunu iki farklı yerden tutuyor. Bekleme penceresi salınımı 17’den 11’e indiriyor ve bunun bedelini kaynakta ödüyor: toplam kopya-tur 457’den 632’ye çıkıyor, aşırı kopya-tur 225’ten 371’e. Küçültmeyi geciktirmek kapasiteyi yukarıda tuttuğu için yetersiz tur sayısı da 40’tan 23’e iniyor. Bu ayar salınımı azaltmaz, bir yöne çevirir.
Adım sınırı tersini yapıyor. Bir turda tek kopya değişimine izin verildiğinde aşırı kopya-tur 225’ten 98’e, toplam kaynak 457’den 347 kopya-tura düşüyor; salınım da 15’e iniyor. Değişiklik sayısı ise 59’dan 71’e çıkıyor, çünkü aynı mesafe daha çok adımda alınıyor. Adım sınırı aşırı düzeltmenin büyüklüğünü keser ve bu yüzden gecikmenin doğurduğu sorunu doğrudan hedefler.
Bedel basamak testinde görünüyor. Yük 800’den 3.200 istek/s’ye çıktığında ayarsız denetleyici gecikme kadar tur sonra yetişiyor: gecikme 3 ise 3 tur, gecikme 6 ise 6 tur. Adım sınırı 1 eklendiğinde aynı sayılar 5 ve 8 oluyor — tepki iki tur yavaşlıyor, çünkü bir kopyadan dört kopyaya çıkmak üç tur sürüyor. Adım sınırı 2 ile fark bir tura iniyor. Salınımın düşüşü ile tepkinin yavaşlaması aynı ayarın iki yüzüdür ve ikisi ayrı ayrı sayılmadıkça ayar bir tercih değil, bir tahmin olur.
İki ayarın birlikte kullanıldığı satır ikisinin toplamını veriyor: salınım 17’den 13’e iniyor, kaynak 385 kopya-turda kalıyor ve adım penceresindeki yetersiz tur sayısı 15’ten 11’e düşüyor. Bu satır ayarsız satırdan her sütunda daha iyidir, ama ideal 301 kopya-turun hâlâ yüzde 28 üstündedir. Gecikme durdukça bu fark kapanmaz.
Son tablo ayarların ne olmadığını söylüyor. Gecikme sıfırken bekleme penceresi ve adım sınırı hiçbir şeyi düzeltmiyor: salınım 8’den 6’ya iniyor, buna karşılık 17 kopya-tur fazladan harcanıyor ve ideal 301 yerine 318 kopya-tur ödeniyor. Bu iki ayar bir ölçüm sorununun tamiridir, bir yük sorununun değil. Ölçütü tazelemek mümkünse ayarlara gerek kalmaz.
Ölçütün seçimi de aynı hesabın parçasıdır. Bu modelde izlenen şey doluluktur ve doluluk türetilmiş bir sayıdır: kullanımın kopya sayısına ve kopya başına doyum verimine bölünmesiyle çıkar. Türetilmiş her sayı, girdilerinin en eskisi kadar eskidir. Doğrudan sayılabilen bir ölçüt — bekleyen istek sayısı gibi — daha az adımda toplanır ve gecikme sütununu sola kaydırır; karşılığında hedef değerin ne olacağı artık kapasite planlamasından okunamaz.
Kararın dayandığı bilgi 120 ölçüttür ve her biri üç tur eskidir; yanına dört sabit eklenir — kopya başına doyum verimi, hedef doluluk, alt ve üst kopya sınırı. Bu kalemlerin tazeliği doğrudan kaynağa çevrilir: aynı yük, aynı denetleyici ve aynı hedefle, yalnız ölçüt gecikmesi yüzünden 301 kopya-tur yerine 510 kopya-tur harcanabiliyor.
Özet
- Ölçüt anında geldiğinde devredilen karar kusursuz: 301 kopya-turluk ideal kaynakla birebir aynı, sıfır aşırı ve sıfır yetersiz tur.
- Tek turluk gecikme salınımı 8’den 30’a çıkarıyor ve 29 turu yetersiz kapasiteyle geçiriyor; denetleyici kendi son kararının ölçüte yansıyıp yansımadığını bilmediği için aynı yükü iki kez düzeltiyor.
- Gecikme büyüdükçe salınım 11’e iniyor, ama bu bir iyileşme değil: denetleyici üst sınıra yapışıyor, kaynak 510 kopya-tura (idealin 1,7 katı) çıkıyor ve turların üçte biri yine yetersiz kalıyor.
- Bekleme penceresi salınımı 17’den 11’e indirirken kaynağı 457’den 632 kopya-tura çıkarıyor; küçültmeyi geciktirdiği için yetersiz turu 40’tan 23’e düşürüyor.
- Adım sınırı aşırı düzeltmeyi kestiği için hem salınımı hem kaynağı düşürüyor (457’den 347 kopya-tura), buna karşılık basamak testinde tepki 3 turdan 5 tura çıkıyor ve değişiklik sayısı 59’dan 71’e yükseliyor.
- Gecikme sıfırken aynı iki ayar 17 kopya-tur fazladan harcayıp iki salınım kazandırıyor; söndürücü ayarlar bir ölçüm sorununun tamiridir, yük sorununun değil.
Sonraki Adım
Bu dersin denetleyicisi tek bir kolu çeviriyor: kopya sayısı. İki kol daha var ve ikisi de aynı yükü başka bir yerden karşılıyor. Kopyanın kendisi büyütülebilir — kapsülün isteği gözlenen kullanıma göre yeniden boyutlandırılabilir, ama bunun bir bedeli vardır ve bedel yeniden başlatmayla ödenir. Kümenin kendisi büyütülebilir — düğüm eklenebilir, ama yeni düğüm anında hazır olmaz. Sonraki ders bu iki kolu ölçer ve üçünün birbirini bozduğu durumu gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.