IlmHamroh
JavaScript Full-stack/31-qism. Prodakshn backend va AI integratsiya33/34-dars22 daqiqa
Mundarija (28)

Yuklama va stress testlari: autocannon, k6, p99, to'yinish nuqtasi va sig'imni rejalash

Qisqacha: Yuklama testi (load test) — ilovaga bir vaqtda ko'p so'rov yuborib, u qancha so'rovni qancha vaqtda bajarishini o'lchash. Ikki asosiy raqam: o'tkazuvchanlik (throughput, soniyasiga so'rovlar — req/s) va kechikish (latency) — o'rtacha emas, persentil: p50, p95, p99. Ulanishlarni oshirib borsangiz, bir nuqtada o'tkazuvchanlik o'smay qoladi, kechikish esa o'sishda davom etadi — bu to'yinish nuqtasi (saturation). Kanon o'lchovida vazifalar-api ro'yxat so'rovida ~2.5 ming req/s da to'yindi: 10 dan 200 ulanishgacha p99 10 ms dan 117 ms ga o'sdi, o'tkazuvchanlik deyarli o'zgarmadi. 31-qism qo'shgan qatlamlar (metrikalar, audit) har so'rovni 4–13 % sekinlashtirdi.

Bu darsda

  • Load, stress, spike va soak testlarini farqlaysiz.
  • Persentillarni hisoblaysiz va nega o'rtacha kechikish aldashini ko'rsatasiz.
  • Little qonuni bilan ulanishlar, o'tkazuvchanlik va kechikishni bog'laysiz.
  • Yuklama testini to'g'ri tashkil qilasiz: alohida jarayon, isitish, navbatma-navbat aylanalar, mediana.
  • Kanon o'lchovini o'qib, to'yinish nuqtasini topasiz va sig'imni rejalashtirasiz.

Oldin bilishingiz kerak: Backend unumdorligini oshirish, Event loop'ni bloklash, Rate limiting, Contract va API sxema testlari.

1. Nega bu kerak?

Juma kuni IlmHamroh Telegram kanalida "vazifalar ilovasi" haqida post chiqdi. Bir soatda 3 000 kishi ro'yxatdan o'tdi va ilovani ochdi. Server bardosh beradimi?

"Funksional" testlar bu savolga javob bermaydi. 340 ta test har so'rovni bir marta yuboradi va "to'g'ri javob keldimi" deb tekshiradi. Yuklama testi boshqa savol beradi: bir vaqtda 200 ta so'rov kelsa, javob qancha vaqtda keladi? Qaysi nuqtada server "bo'g'iladi"? Bo'g'ilganda nima bo'ladi — sekinlashadimi, xato beradimi, yiqiladimi?

O'xshatish: ko'prik. Muhandis uning chizmasini tekshiradi (funksional test), lekin ochilishdan oldin unga tosh ortilgan yuk mashinalarini qo'yadi va egilishni o'lchaydi (yuklama testi). Chizma to'g'ri bo'lishi ko'prik 40 tonnani ko'tarishini kafolatlamaydi.

Backend unumdorligini oshirish darsida yuklama vositasini tor joy qidirish uchun ishlatdik. Bu darsda yuklama testining o'zini — usuli, raqamlari va tuzoqlarini — chuqur o'rganamiz.

2. To'rt xil test

Test Savol Qanday
Load (yuklama) kutilgan yukda me'yordamizmi? kutilgan so'rovlar oqimi, 5–30 daqiqa
Stress qayerda sinadi va qanday sinadi? yukni sinishgacha oshirib borish
Spike (sakrash) birdan 10× yuk kelsa? bir lahzada katta yuk, keyin pasayish
Soak (uzoq muddatli) soatlab ishlasa nima bo'ladi? o'rtacha yuk, 4–24 soat

Har biri boshqa xatoni topadi. Stress testi to'yinish nuqtasini va sinish shaklini ko'rsatadi: chiroyli sekinlashish yoki 500 lar quyuni. Spike — avtomatik masshtablash va ulanish hovuzlarining tezligini. Soak esa vaqt o'tib chiqadigan xatolarni: xotira oqishi, to'lib boradigan kesh, ochiq qolgan ulanishlar, disk to'lishi. 31-qism kanonida soak testi yo'q — u soatlab mashinani band qiladi va bitta kompyuterda o'tkazilgan o'lchov ishonchsiz bo'lardi.

3. Raqamlar: o'tkazuvchanlik va persentillar

3.1 Nega o'rtacha aldaydi

Backend unumdorligini oshirish darsida p50 va p99 ni ko'rgansiz. Endi ularni o'zimiz hisoblaymiz. 100 ta so'rov: 97 tasi 10 ms da, 3 tasi 400 ms da bajarildi (masalan, sovuq kesh yoki axlat yig'uvchi pauzasi):

js
function persentil(saralangan, p) {
  const i = Math.ceil((p / 100) * saralangan.length) - 1;
  return saralangan[Math.max(0, i)];
}

const vaqtlar = [...Array(97).fill(10), 400, 400, 400];
const s = vaqtlar.toSorted((a, b) => a - b);
const ortacha = vaqtlar.reduce((x, y) => x + y, 0) / vaqtlar.length;

console.log("o'rtacha:", ortacha.toFixed(1), "ms");
for (const p of [50, 95, 97, 98, 99]) {
  console.log(`p${p}:`, persentil(s, p), "ms");
}

Konsolda:

text
o'rtacha: 21.7 ms
p50: 10 ms
p95: 10 ms
p97: 10 ms
p98: 400 ms
p99: 400 ms

O'rtacha — 21.7 ms. Bunday so'rov bitta ham yo'q: 97 tasi ikki baravar tezroq, 3 tasi 18 baravar sekinroq. O'rtacha ikkala guruhni ham yashiradi. p50 "odatdagi foydalanuvchi nimani ko'radi" deydi, p99 — "eng omadsiz yuzdan biri".

Nega p99 muhim? Bitta sahifa ochilishida brauzer API'ga 5–10 ta so'rov yuboradi. Har biri 1 % ehtimol bilan sekin bo'lsa, 10 ta so'rovli sahifaning sekin ochilish ehtimoli — taxminan 10 %. Persentilni hisoblashda bir nechta usul bor; bu yerdagisi — "eng yaqin daraja" (nearest rank). Vositalar o'zaro biroz farq qilishi mumkin.

3.2 Little qonuni

Uchta raqam bir-biriga bog'liq. Little qonuni: tizimdagi o'rtacha so'rovlar soni = o'tkazuvchanlik × o'rtacha javob vaqti, ya'ni L = λ × W.

autocannon kabi vositalarda ulanishlar soni qat'iy: har ulanish javob olgach, darhol keyingi so'rovni yuboradi. Demak tizimda doim L = ulanishlar soni so'rov bor. Bundan: W = ulanishlar / o'tkazuvchanlik. Kanon o'lchovidagi raqamlar bilan tekshiramiz:

js
// Kanon o'lchovi: GET /v1/vazifalar?limit=20, 31-qism kanoni
const olchov = [
  { ulanish: 10, rps: 2283, p50: 3 },
  { ulanish: 50, rps: 2613, p50: 17 },
  { ulanish: 200, rps: 2478, p50: 76 },
];

for (const { ulanish, rps, p50 } of olchov) {
  const w = (ulanish / rps) * 1000;
  console.log(`${ulanish} ulanish: Little W = ${w.toFixed(1)} ms,` +
    ` o'lchangan p50 = ${p50} ms`);
}

Konsolda:

text
10 ulanish: Little W = 4.4 ms, o'lchangan p50 = 3 ms
50 ulanish: Little W = 19.1 ms, o'lchangan p50 = 17 ms
200 ulanish: Little W = 80.7 ms, o'lchangan p50 = 76 ms

Formula o'rtacha vaqtni beradi, o'lchov esa medianani — o'rtacha medianadan biroz katta (sekin "dum" uni tortadi). Lekin tartib aniq mos. Asosiy xulosa: o'tkazuvchanlik to'yingandan keyin har qo'shimcha ulanish faqat kutish vaqtini oshiradi. 200 ulanishda server baribir ~2.5 ming req/s bajaradi — qolgan so'rovlar navbatda turadi.

Tekshirib ko'ring: Server to'yinish nuqtasida 2 500 req/s bajaradi. Unga 1 000 ulanish bilan yuk berilsa, o'rtacha javob vaqti taxminan qancha bo'ladi?

Javob

W = 1000 / 2500 = 0.4 s = 400 ms. Server tezlashmaydi — so'rovlar navbatda turadi. Haqiqatda undan ham yomon bo'lishi mumkin: navbat uzaygani sari xotira o'sadi, timeout'lar boshlanadi, mijozlar qayta urinadi — yuk o'z-o'zini kuchaytiradi (Resilience naqshlari darsidagi retry bo'roni).

4. O'zimiz o'lchab ko'ramiz

Nazariyani kichik tajribada tekshiramiz. Server har so'rovda 1 ms CPU ishini bajaradi (sikl bilan — Event loop'ni bloklash darsidagidek). Event loop bitta — demak soniyasiga ~1 000 so'rovdan ko'p bajara olmaydi. autocannon alohida jarayonda ishlaydi:

js
import { createServer } from "node:http";
import { execFile } from "node:child_process";

function ish(ms) {
  const t = performance.now();
  while (performance.now() - t < ms) { /* CPU band */ }
}

const server = createServer((req, res) => {
  ish(1);
  res.end("ok");
});
await new Promise((ok) => server.listen(5590, "127.0.0.1", ok));

const AC = "node_modules/autocannon/autocannon.js";
function yuk(ulanish, soniya) {
  const args = [AC, "-c", String(ulanish), "-d", String(soniya),
    "-j", "http://127.0.0.1:5590/"];
  return new Promise((ok, rad) => execFile(process.execPath, args,
    (x, out) => (x ? rad(x) : ok(JSON.parse(out)))));
}

await yuk(5, 1); // isitish
for (const c of [1, 10, 50]) {
  const r = await yuk(c, 2);
  console.log(`${String(c).padStart(2)} ulanish: ` +
    `${Math.round(r.requests.average)} req/s, ` +
    `p50 ${r.latency.p50} ms, p99 ${r.latency.p99} ms`);
}
server.close();

Skript soddalashtirib ko'rsatilgan: AC — autocannon o'rnatilgan papkadagi fayl yo'li, bizning nusxamizda jadvalli chiqish uchun qo'shimcha tarmoq ham bor edi. Biz ishga tushirdik (autocannon 8.0.0, Node 24.21.0, bitta mashina, har bosqich 2 s; chiqishdan xatolar soni ustuni olib tashlangan — hammasi 0):

text
 1 ulanish: 640 req/s, p50 1 ms, p99 2 ms
10 ulanish: 770 req/s, p50 12 ms, p99 18 ms
50 ulanish: 648 req/s, p50 67 ms, p99 189 ms

Bitta ulanishda server bo'sh turgan paytlari bor: javob mijozga borib, keyingi so'rov kelguncha. 10 ulanishda u to'liq band — o'tkazuvchanlik eng yuqori. 50 ulanishda o'tkazuvchanlik oshmadi (hatto pasaydi — ko'p ulanishni boshqarish ham CPU oladi), p50 esa 67 ms ga chiqdi. Little: 50 / 648 ≈ 77 ms. O'lchov 2 soniyalik, bitta yugurish — raqamlar har safar biroz boshqacha bo'ladi, shakli esa yo'q.

autocannon jadvalli chiqishi ham shuni ko'rsatadi (10 ulanish, 2 s):

text
Running 2s test @ http://127.0.0.1:5590/
10 connections


┌─────────┬──────┬───────┬───────┬───────┬──────────┬─────────┬───────┐
│ Stat    │ 2.5% │ 50%   │ 97.5% │ 99%   │ Avg      │ Stdev   │ Max   │
├─────────┼──────┼───────┼───────┼───────┼──────────┼─────────┼───────┤
│ Latency │ 6 ms │ 12 ms │ 18 ms │ 20 ms │ 12.14 ms │ 2.59 ms │ 24 ms │
└─────────┴──────┴───────┴───────┴───────┴──────────┴─────────┴───────┘
┌───────────┬───────┬───────┬───────┬─────────┬─────────┬───────┬───────┐
│ Stat      │ 1%    │ 2.5%  │ 50%   │ 97.5%   │ Avg     │ Stdev │ Min   │
├───────────┼───────┼───────┼───────┼─────────┼─────────┼───────┼───────┤
│ Req/Sec   │ 782   │ 782   │ 782   │ 792     │ 787     │ 5     │ 782   │
├───────────┼───────┼───────┼───────┼─────────┼─────────┼───────┼───────┤
│ Bytes/Sec │ 97 kB │ 97 kB │ 97 kB │ 98.2 kB │ 97.6 kB │ 608 B │ 97 kB │
└───────────┴───────┴───────┴───────┴─────────┴─────────┴───────┴───────┘

Req/Bytes counts sampled once per second.
# of samples: 2

2k requests in 2.01s, 195 kB read

Yuqori jadval — kechikish persentillari, pastki — soniyalik o'tkazuvchanlik. # of samples: 2 ga e'tibor bering: o'tkazuvchanlik har soniyada bir marta o'lchanadi, 2 soniyali test atigi 2 ta nuqta beradi. Ishonchli raqam uchun uzoqroq test kerak.

5. Kanon o'lchovi: 31-qism har so'rovga qancha qo'shdi

5.1 Usul

31-qismda vazifalar-api ga ko'p narsa qo'shildi: Prometheus metrikalari (har so'rovda vaqt o'lchanadi), global audit interceptor, feature flag'lar. Ular har so'rovga qancha narx qo'shdi? Kanon 30-qism oxiridagi va 31-qism oxiridagi ilovani bir vaqtda, bir xil sozlama bilan ishga tushirdi (xotira ombori, auth'siz, cheklovsiz) va solishtirdi.

Usul 30-qismdagi yordamchidan olingan va uning har qadami bitta xatoning oldini oladi:

Qadam Nega
autocannon alohida jarayonda server bilan bitta event loop'da yuk beruvchi o'zi "tor joy" bo'lib qoladi
1 s isitish birinchi so'rovlar JIT kompilyatsiyasi va keshlarni to'ldiradi
5 aylana × 3 s, variantlar navbatma-navbat mashina yuki o'zgarsa, ikkala variantga teng tushadi
mediana va oraliq bitta omadli yoki omadsiz aylana natijani buzmasin

5.2 Natija

Har so'rov uchun ikki qator bor: yuqorisi — 30-qism oxiri, pastkisi — 31-qism oxiri. Medianani va qavs ichidagi oraliqni birga o'qing. Kanon chiqishi (2026-11-03, Node 24.21.0, bitta mashina — o'yin va boshqa jarayonlar bilan band):

text
GET /v1/vazifalar?limit=20
  30-qism oxiri: mediana 2552 req/s (oraliq 2066–2860), p50 3 ms, p99 10 ms, xato 0
  31-qism oxiri: mediana 2224 req/s (oraliq 1657–2394), p50 3 ms, p99 12 ms, xato 0
GET /v1/vazifalar/1
  30-qism oxiri: mediana 3280 req/s (oraliq 3142–3394), p50 2 ms, p99 8 ms, xato 0
  31-qism oxiri: mediana 3045 req/s (oraliq 2998–3348), p50 2 ms, p99 9 ms, xato 0
GET /v1/teglar
  30-qism oxiri: mediana 3844 req/s (oraliq 3496–4193), p50 2 ms, p99 7 ms, xato 0
  31-qism oxiri: mediana 3653 req/s (oraliq 3376–3822), p50 2 ms, p99 8 ms, xato 0
PATCH /v1/vazifalar/1 {"bajarildi":true}
  30-qism oxiri: mediana 2570 req/s (oraliq 2235–2662), p50 3 ms, p99 12 ms, xato 0
  31-qism oxiri: mediana 2460 req/s (oraliq 2180–2542), p50 3 ms, p99 12 ms, xato 0

"30-qism oxiri" — 30-qism yakunidagi loyiha, "31-qism oxiri" — shu darslardagi yakuniy loyiha (yorliqlar qisqartirilgan). Grafikda — 31-qism o'tkazuvchanligi 30-qismnikiga nisbatan:

31-qism o'tkazuvchanligi (30-qism = 100 %)
  • GET ro'yxat87 %
  • GET bitta93 %
  • GET teglar95 %
  • PATCH96 %

Manba: IlmHamroh kanon o'lchovi, 2026-11-03: autocannon, 5 aylana × 3 s mediana; 30 va 31 kanonlari bir vaqtda, xotira ombori; bitta mashina, band

5.3 Natijani halol o'qish

Farq bor, lekin kichik. Ro'yxat so'rovida −13 %, qolganlarida −4…−7 %. p99 deyarli o'zgarmadi (10 → 12 ms).

Oraliqlar ustma-ust tushadi. Ro'yxatda 30-qism 2 066–2 860, 31-qism 1 657–2 394. 31-qismning eng yaxshi aylanasi 30-qismning ko'p aylanasidan tezroq. Shuning uchun "−13 %" ni aniq raqam emas, yo'nalish sifatida o'qing: 31-qism biroz sekinroq. Mashina o'lchov paytida band edi — mutlaq req/s ni boshqa kompyuterga ko'chirib bo'lmaydi.

Sabab — taxmin. Kanon har so'rovdagi uchta yangi ishni ko'rsatadi: metrikalar middleware'i (vaqt o'lchash va finish hodisasi), global AuditInterceptor (har so'rovda belgini o'qiydi, yozuv faqat belgilangan amallarda), Problem sarlavhalari. Ularning har biri alohida o'lchanmagan. Aniq javob kerak bo'lsa — profil (CPU profiling va flamegraph) yoki bittalab o'chirib o'lchash.

Narx arzimi? 4–13 % evaziga loyiha har so'rovning metrikasini, admin amallarining o'zgarmas jurnalini oldi. Ro'yxat so'rovi baribir 3 ms (p50) da javob beradi — foydalanuvchi farqni sezmaydi. Bu — muhandislik qarori, o'lchov esa uni ongli qiladi.

6. To'yinish nuqtasi

6.1 Kanon chiqishi

Kanon 31-qism ilovasiga ro'yxat so'rovida ulanishlarni oshirib bordi (har bosqich 5 s). Qatordan qatorga req/s qanchalik o'zgarganini va p99 qanchalik o'zgarganini alohida kuzating:

text
saturatsiya (31, GET /v1/vazifalar?limit=20, 5 s):
   10 ulanish: 2283 req/s, p50 3 ms, p99 10 ms, xato 0
   50 ulanish: 2613 req/s, p50 17 ms, p99 40 ms, xato 0
  200 ulanish: 2478 req/s, p50 76 ms, p99 117 ms, xato 0

O'tkazuvchanlik (bitta yugurish, mutlaq qiymat):

Ulanishlar va o'tkazuvchanlik (31-qism, GET ro'yxat)
o'tkazuvchanlik, req/s
2 6132 28310200ulanishlar, tareq/s: 10 ta → 2 283 req/sreq/s: 50 ta → 2 613 req/sreq/s: 200 ta → 2 478 req/s
Ulanishlar va o'tkazuvchanlik (31-qism, GET ro'yxat)
ulanishlarreq/s
102 283
502 613
2002 478

Manba: IlmHamroh kanon o'lchovi, 2026-11-03: autocannon, har nuqta 5 s, bitta yugurish; xotira ombori; bitta mashina, band

10 dan 50 ga — o'tkazuvchanlik +14 %, p99 4 baravar. 50 dan 200 ga — o'tkazuvchanlik o'smadi, p99 yana 3 baravar. To'yinish nuqtasi — taxminan 2.5 ming req/s, 10–50 ulanish oralig'ida. Undan keyin server ko'proq ish qilmaydi, faqat ko'proq kutdiradi.

Bu grafik ko'pincha "tirsak" (knee) shaklida chiziladi: avval chiziq tik ko'tariladi, keyin gorizontal yotadi. Kechikish grafigi esa teskari: avval tekis, tirsakdan keyin keskin yuqoriga.

6.2 Haqiqiy tizimdagi to'yinish: 429

Yuqoridagi o'lchov — xotira omborida, auth'siz. Haqiqiy rejimda (Postgres, sessiya bilan) kanon Backend unumdorligini oshirish darsida boshqa narsani ko'rgan edi. O'sha qatorni eslang (yorliqlar qisqartirilgan):

text
2026-11-03T10:54:07+05:00 | asos | SAQLASH=postgres LOG_LEVEL=info  | 838 req/s, p50 22 ms, p99 38 ms, kodlar 429×12569, xatolar 0

Barcha javob — 429. Yuklama testi bitta foydalanuvchi (Aziz) sessiyasi bilan yuborildi, Rate limiting esa bitta foydalanuvchiga daqiqasiga 600 so'rov beradi. Test API'ni emas, cheklagichni o'lchadi. xatolar 0 ham aldaydi: autocannon tarmoq xatolarini sanaydi, 429 esa to'g'ri HTTP javob.

Saboq: ssenariy haqiqatga o'xshashi kerak. Haqiqiy yukda minglab foydalanuvchi bor, har birining o'z sessiyasi. Yuklama testida ham: ko'p test foydalanuvchisi (oldindan yaratilgan, sessiyalari bilan) yoki test muhitida cheklov chegarasini oshirish. Javob kodlarini doim tekshiring — "tez" javob ko'pincha "tez rad".

Tekshirib ko'ring: Yuklama testida 5 000 req/s chiqdi, p99 — 2 ms. Hamkasbingiz "server juda tez!" deydi. Nimani birinchi tekshirasiz?

Javob

Javob kodlarini. 429 (cheklov), 401 (sessiya tugagan), 404 (noto'g'ri URL) yoki 304 (kesh) — hammasi tez va "xatosiz". Keyin — javob tanasi haqiqiy ishni ko'rsatadimi (bo'sh ro'yxat ham tez). Kanon aynan shu tuzoqqa tushgan: hamma javob 429 edi.

7. Ssenariy va vositalar

7.1 Ochiq va yopiq model

autocannon — yopiq model (closed model): N ulanish, har biri javob olgach keyingi so'rovni yuboradi. Server sekinlashsa, yuk ham avtomatik kamayadi. Haqiqiy foydalanuvchilar esa serverni kutib o'tirmaydi — ular o'z vaqtida keladi: ochiq model (open model), "soniyasiga 500 ta yangi so'rov" deb beriladi.

Farq muhim. Yopiq modelda server bir zum qotib qolsa, yuk beruvchi ham to'xtaydi va o'sha paytdagi kechikishlar o'lchovga tushmaydi — muvofiqlashgan e'tiborsizlik (coordinated omission). Natijada p99 haqiqatdagidan chiroyliroq chiqadi. Ochiq model buni ko'rsatadi: kutilmagan so'rovlar navbati o'sadi va kechikishga yoziladi.

7.2 k6

k6 — Grafana'ning yuklama vositasi: ssenariy JavaScript'da yoziladi, dvigatel esa Go'da (Node emas — import lar k6 ning o'z modullari). Ochiq modelni ham qo'llaydi. Quyidagi ssenariy rasmiy hujjat bo'yicha yozilgan, kanonda sinalmagan (2026-11):

js
import http from "k6/http";
import { check } from "k6";

export const options = {
  scenarios: {
    royxat: {
      executor: "constant-arrival-rate", // ochiq model
      rate: 500, timeUnit: "1s",         // soniyasiga 500 so'rov
      duration: "2m",
      preAllocatedVUs: 50, maxVUs: 200,
    },
  },
  thresholds: {
    http_req_duration: ["p(99)<100"],    // p99 < 100 ms
    http_req_failed: ["rate<0.01"],      // xato < 1 %
  },
};

export default function () {
  const r = http.get("http://127.0.0.1:3000/v1/vazifalar?limit=20", {
    headers: { Cookie: `__Host-sessiya=${__ENV.SESSIYA}` },
  });
  check(r, { "200": (x) => x.status === 200 });
}

autocannon'da uch narsa yo'q. Birinchisi — ochiq model (constant-arrival-rate). Ikkinchisi — chegaralar (thresholds — p99 100 ms dan oshsa, k6 nol bo'lmagan kod bilan chiqadi, ya'ni CI'da test "yiqiladi"). Uchinchisi — tekshiruv (check — status kodini tekshiradi; 429 tuzog'iga qarshi). Ko'p foydalanuvchi uchun sessiyalar ro'yxatini fayldan o'qish mumkin.

Artillery — YAML'da ssenariy, Node'da ishlaydi; foydalanuvchi "sayohati" (kirish → ro'yxat → qo'shish) uchun qulay. U ham sinalmagan.

Vosita Ssenariy Model Kanonda
autocannon CLI bayroqlari yoki Node API yopiq bor (25-qismdan)
k6 JavaScript (Go dvigateli) ochiq va yopiq sinalmagan
Artillery YAML + JS ochiq sinalmagan

7.3 Prodakshnga o'xshash muhit

Yuklama testi faqat muhit o'xshash bo'lsa ma'noli. Kanon o'lchovi bunga javob bermaydi — u ataylab soddalashtirilgan (xotira ombori, bitta kompyuter). Haqiqiy reja uchun:

Jihat Nima bo'lishi kerak
Yuk beruvchi alohida mashinada (server CPU'sini yemasin)
Ma'lumot hajmi prodakshndagidek: 10 qatorli va 10 million qatorli jadval — boshqa so'rov rejalari (26-qismdagi EXPLAIN)
Sozlama o'sha NODE_ENV, log darajasi, ulanish hovuzi o'lchami
Foydalanuvchilar ko'p sessiya; cheklov — prodakshndagidek yoki ataylab oshirilgan
Tashqi xizmatlar AI, email, to'lov — soxta (simulyator); haqiqiysiga yuklama yubormang

Oxirgi qator jiddiy. Yuklama testi AI provayderiga 10 000 so'rov yuborsa — bu hisob-faktura. Kanon testlari haqiqiy Gemini'ga bormaydi: soxta provayder ishlaydi.

8. Sig'imni rejalash

Yuklama testining amaliy maqsadi — "nechta server kerak?" degan savolga javob. Oddiy hisob:

js
function serverlar({ choqqi, toyinish, ulush = 0.7 }) {
  // to'yinishning 70 % igacha yuklaymiz — 30 % bo'sh joy
  return Math.ceil(choqqi / (toyinish * ulush));
}

const toyinish = 2500; // kanon: GET ro'yxat, bitta jarayon
for (const choqqi of [300, 2000, 6000]) {
  console.log(`cho'qqi ${choqqi} req/s →`,
    serverlar({ choqqi, toyinish }), "ta jarayon");
}

Konsolda:

text
cho'qqi 300 req/s → 1 ta jarayon
cho'qqi 2000 req/s → 2 ta jarayon
cho'qqi 6000 req/s → 4 ta jarayon

Nega 70 %? To'yinish nuqtasi yaqinida kechikish keskin o'sadi (Little), va bitta jarayon yiqilsa, qolganlari uning yukini olishi kerak. "Cho'qqi" esa o'rtacha emas: 3 000 kishi bir soatda — o'rtacha 1 kishi/s, lekin post chiqqan dastlabki 5 daqiqada ularning yarmi keladi.

Bu hisob soddalashtirilgan: u faqat bitta so'rov turini va bitta Node jarayonini oladi. Haqiqatda tor joy ko'pincha Node emas — ma'lumotlar bazasi ulanishlari yoki tashqi xizmat. Ko'p yadroli serverda bir nechta jarayon (cluster) va ularni ko'tarish — 33-qism mavzusi.

9. Ko'p uchraydigan xatolar

Xato Oqibati To'g'risi
O'rtacha kechikishga qarash sekin "dum" ko'rinmaydi p50, p95, p99
Yuk beruvchi server bilan bitta jarayonda yuk beruvchi o'zi tor joy alohida jarayon yoki mashina
Bitta foydalanuvchi sessiyasi bilan test cheklagich o'lchanadi (429) ko'p sessiya; javob kodlarini tekshirish
Isitishsiz bitta qisqa yugurish JIT va kesh natijani buzadi isitish, bir necha aylana, mediana
Noutbukdagi raqamni prodakshnga ko'chirish sig'im noto'g'ri baholanadi nisbat va shakl; prodakshnga o'xshash muhit
Haqiqiy tashqi xizmatga yuklama pul, bloklanish soxta provayder
Faqat yopiq model coordinated omission — p99 chiroyli chiqadi ochiq model (k6 arrival-rate)

10. Mashqlar

Mashqlar kurs/mashqlar/31/33-yuklama/ da. 2- va 3-mashq yechimlari Node 24.21.0 da tekshirilgan (biz ishga tushirdik).

1-mashq (oson): bo'shliqlarni to'ldiring

  • Soatlab o'rtacha yuk bilan xotira oqishini qidiruvchi test — test.
  • Little qonuni: L = λ ×
  • Kanon o'lchovida 31-qism ro'yxat so'rovi o'tkazuvchanligi 30-qismdan taxminan % past chiqdi.
  • autocannon ishlatadigan model — model.
  • Bitta foydalanuvchi bilan yuklama testida kanon hamma javobda kodini oldi.
Yechim
  • soak — uzoq muddatli test; 31-qism kanonida yo'q.
  • W — o'rtacha javob vaqti; L — tizimdagi so'rovlar, λ — o'tkazuvchanlik.
  • 13 — 2 552 dan 2 224 req/s ga (oraliqlar ustma-ust).
  • yopiq — N ulanish, javobdan keyin keyingi so'rov.
  • 429 — cheklov: bitta foydalanuvchiga daqiqasiga 600 so'rov.

2-mashq (o'rta): autocannon natijasidan hisobot

autocannon -j (JSON) chiqishidan qisqa hisobot yasaydigan hisobot(r) funksiyasini yozing: o'tkazuvchanlik (butun songa yaxlitlangan), p50 va p99, muvaffaqiyatli javoblar ulushi (non2xx ham xato). Agar 2xx bo'lmagan javoblar 1 % dan ko'p bo'lsa, oxiriga "OGOH: 2xx bo'lmagan javoblar" qo'shing. Namuna ma'lumot autocannon chiqishi tuzilmasida berilgan. Ishora: toFixed, shablon satr.

Yechim
js
function hisobot(r) {
  const jami = r.requests.total;
  const yomon = r.errors + r.non2xx;
  const ulush = ((jami - yomon) / jami) * 100;
  let s = `${Math.round(r.requests.average)} req/s, ` +
    `p50 ${r.latency.p50} ms, p99 ${r.latency.p99} ms, ` +
    `muvaffaqiyat ${ulush.toFixed(1)} %`;
  if (r.non2xx / jami > 0.01) s += " | OGOH: 2xx bo'lmagan javoblar";
  return s;
}

const tez = {
  requests: { average: 838.4, total: 25152 },
  latency: { p50: 22, p99: 38 },
  errors: 0, non2xx: 25152,
};
const sog = {
  requests: { average: 2283.2, total: 11416 },
  latency: { p50: 3, p99: 10 },
  errors: 0, non2xx: 0,
};
console.log(hisobot(sog));
console.log(hisobot(tez));

Konsolda:

text
2283 req/s, p50 3 ms, p99 10 ms, muvaffaqiyat 100.0 %
838 req/s, p50 22 ms, p99 38 ms, muvaffaqiyat 0.0 % | OGOH: 2xx bo'lmagan javoblar

Ikkinchi namuna kanonning 429 holatiga o'xshatib yasalgan: hamma javob 2xx emas. errors: 0 bo'lsa ham, muvaffaqiyat 0 % — hisobot buni birinchi qatorda ko'rsatadi. Namunadagi total raqamlari o'ylab topilgan (tuzilma uchun); req/s, p50 va p99 — kanon o'lchovidan.

3-mashq (qiyin): to'yinish nuqtasini topish

Ulanishlar oshirilgan o'lchovlar ro'yxati berilgan. toyinish(olchov) funksiyasini yozing: keyingi bosqichda o'tkazuvchanlik 10 % dan kam o'sgan, p99 esa 50 % dan ko'p o'sgan birinchi nuqtani qaytarsin (bu — to'yinish boshlangan joy). Bunday nuqta bo'lmasa — null. Kanon ma'lumoti va o'zingizning kichik tajribangiz natijasida sinang. Ishora: for sikl va qo'shni elementlar (massivning HOF metodlari ham mumkin).

Yechim
js
function toyinish(olchov) {
  for (let i = 0; i + 1 < olchov.length; i++) {
    const [a, b] = [olchov[i], olchov[i + 1]];
    const rpsOsish = b.rps / a.rps - 1;
    const p99Osish = b.p99 / a.p99 - 1;
    if (rpsOsish < 0.1 && p99Osish > 0.5) {
      return { ulanish: a.ulanish, rps: a.rps, p99: a.p99 };
    }
  }
  return null;
}

const kanon = [
  { ulanish: 10, rps: 2283, p99: 10 },
  { ulanish: 50, rps: 2613, p99: 40 },
  { ulanish: 200, rps: 2478, p99: 117 },
];
const tajriba = [
  { ulanish: 1, rps: 640, p99: 2 },
  { ulanish: 10, rps: 770, p99: 18 },
  { ulanish: 50, rps: 648, p99: 189 },
];
console.log("kanon:", toyinish(kanon));
console.log("1 ms CPU server:", toyinish(tajriba));
console.log("to'yinmagan:", toyinish(kanon.slice(0, 2)));

Konsolda:

text
kanon: { ulanish: 50, rps: 2613, p99: 40 }
1 ms CPU server: { ulanish: 10, rps: 770, p99: 18 }
to'yinmagan: null

Kanonda 10 → 50 o'tishida o'tkazuvchanlik hali 14 % o'sdi — bu to'yinish emas. 50 → 200 da o'smadi, p99 esa uch baravar — nuqta 50 ulanishda. "To'yinmagan" qatorda 10 → 50 o'tishi 10 % dan ko'p o'sdi, demak oxirgi nuqtagacha to'yinish ko'rinmadi — ulanishlarni yana oshirish kerak. Chegaralar (10 % va 50 %) — kelishuv, qonun emas.

11. Loyiha qadami: kanon o'lchovi

Bu darsda vazifalar-api kodi o'zgarmaydi — yuklama testi kanonda alohida tajriba sifatida qilindi va loyihaga fayl qo'shmadi. Natijalar yuqorida: 31-qism qatlamlari narxi va to'yinish nuqtasi.

O'zingiz takrorlay olasiz (Docker'siz, xotira omborida). Avval loyihani yig'ing va ishga tushiring:

bash
npm run build
SAQLASH=xotira EGASI_ID=1 LOG_LEVEL=warn \
  CORS_ORIGINS=http://localhost:5173 PORT=3000 node dist/main.js

Bu — kanon o'lchovidagi sozlama. SAQLASH=xotira va EGASI_ID=1 — 30-qismdagi auth'siz xotira rejimi: cheklov ham, baza ham yo'q, faqat ilova kodi o'lchanadi. LOG_LEVEL=warn — har so'rov logga yozilmasin. Boshqa terminalda bir nechta vazifa qo'shing va yuk bering:

bash
for i in 1 2 3 4 5; do
  curl -s -X POST -H "Content-Type: application/json" \
    -d "{\"matn\":\"Yuklama vazifasi $i\"}" \
    http://127.0.0.1:3000/v1/vazifalar > /dev/null
done
URL="http://127.0.0.1:3000/v1/vazifalar?limit=20"
npx autocannon -c 10 -d 5 "$URL"
npx autocannon -c 50 -d 5 "$URL"

Natijangiz kanondan boshqacha bo'ladi — kompyuteringiz boshqa. Mutlaq req/s ni emas, shaklni solishtiring: 10 dan 50 ulanishga o'tishda o'tkazuvchanlik qancha o'sdi, p99 necha baravar. Agar -c 200 ham sinasangiz — to'yinish nuqtangizni topasiz. Javob kodlariga qarang: 2xx dan boshqa javoblar bo'lsa, autocannon ularning sonini jadval ostida alohida aytadi — «Haqiqiy tizimdagi to'yinish: 429» bo'limidagi tuzoqni eslang.

Hali commit qilmang — loyihaning yakuniy bosqichi keyingi darsda tugaydi: test arxitekturasi, CI fayli va commit.

12. Real ishda

  • Qayerda uchraydi: katta aksiya, reklama, imtihon kuni ro'yxatdan o'tish oldidan — "Qora juma" ga tayyorgarlik har elektron do'konda yuklama testidan boshlanadi. Infratuzilmani o'zgartirgandan keyin ham (yangi server turi, yangi Postgres versiyasi).
  • Vositalar: k6 (Grafana k6 Cloud), Artillery, autocannon, Gatling, JMeter, Locust; natijalar — Prometheus va Grafana'da (Observability). SLO: "p99 < 300 ms, 99.9 % so'rovlar" — yuklama testining chegaralari shundan olinadi.
  • Intervyu savollari: "p99 nima va nega o'rtacha emas?", "Throughput va latency qanday bog'langan?", "Load, stress va soak testining farqi?", "Yuklama testi natijasiga qanday ishonasiz?", "Coordinated omission nima?"

Xulosa

  • Yuklama testi bir vaqtdagi yukda o'tkazuvchanlik va kechikishni o'lchaydi; load, stress, spike va soak — turli savollarga javob.
  • O'rtacha kechikish aldaydi — p50, p95, p99 ga qarang; Little qonuni: W = ulanishlar / o'tkazuvchanlik.
  • To'yinishdan keyin o'tkazuvchanlik o'smaydi, faqat kutish o'sadi: kanonda ~2.5 ming req/s, p99 10 → 117 ms.
  • 31-qism qatlamlari har so'rovni 4–13 % sekinlashtirdi — bitta band mashinada, oraliqlar ustma-ust: yo'nalish, aniq raqam emas.
  • Ssenariy haqiqatga o'xshasin: ko'p sessiya, javob kodlari tekshiriladi — aks holda cheklagich o'lchanadi (429).
  • Sig'im: cho'qqi yuk / (to'yinish × 0.7) — keyin prodakshnga o'xshash muhitda tekshiriladi.

Keyingi dars: Test arxitekturasi va CI — 340 ta testni tez va barqaror qilish: parallellik, beqaror testlar, lokal check, CI fayli va 31-qismning yakuniy commit'i.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Yuklama va stress testlari: autocannon, k6, p99, to'yinish nuqtasi va sig'imni rejalash — IlmHamroh