Uji beban ke angka trafikmu
Uji beban yang memukul satu URL berulang kali mengukur kecepatan cache, bukan kapasitas. Ini cara membuat ujinya berarti.
Intisari
- Uji ke origin langsung untuk mengukur kapasitas; lewat Cloudflare untuk mengukur pengalaman pembaca.
- Variasikan URL. Satu URL berulang menghangatkan setiap cache dan memberi angka palsu.
- Model trafik portal berita: banyak pembaca anonim, sedikit member, sesekali artikel viral.
- Uji juga jalur yang tidak di-cache: server island, login, webhook.
- Cari titik patah, bukan cuma "lulus" โ kamu perlu tahu di mana batasnya.
Empat skenario yang berbeda
| Skenario | Mengukur | Target ke |
|---|---|---|
| Kapasitas origin | Berapa yang bisa dilayani Astro sendiri | Origin langsung |
| Pengalaman pembaca | Latensi yang dirasakan | Lewat Cloudflare |
| Cache dingin | Apa yang terjadi setelah purge everything | Lewat Cloudflare, setelah purge |
| Artikel viral | Satu URL, trafik ekstrem | Lewat Cloudflare |
Skenario campuran yang realistis
// uji/beban.js
import http from "k6/http";
import { check, group } from "k6";
import { Rate, Trend } from "k6/metrics";
const gagalIsland = new Rate("gagal_island");
const waktuIsland = new Trend("waktu_island");
const ORIGIN = __ENV.TARGET;
const TOKEN = __ENV.ORIGIN_TOKEN;
const H = { "X-Origin-Token": TOKEN };
export const options = {
scenarios: {
// 85% pembaca anonim membuka artikel โ bentuk trafik sesungguhnya
pembaca_anonim: {
executor: "ramping-arrival-rate",
startRate: 10, timeUnit: "1s",
preAllocatedVUs: 100, maxVUs: 500,
stages: [
{ duration: "2m", target: 20 },
{ duration: "5m", target: 60 }, // puncak yang diperkirakan
{ duration: "3m", target: 120 }, // 2ร puncak
{ duration: "2m", target: 0 },
],
exec: "bacaArtikel",
},
// 10% membuka beranda
beranda: {
executor: "constant-arrival-rate",
rate: 8, timeUnit: "1s",
duration: "12m", preAllocatedVUs: 30, maxVUs: 100,
exec: "bukaBeranda",
},
// 5% member dengan sesi โ memicu server island
member: {
executor: "constant-arrival-rate",
rate: 4, timeUnit: "1s",
duration: "12m", preAllocatedVUs: 20, maxVUs: 60,
exec: "memberBaca",
},
},
thresholds: {
"http_req_duration{skenario:pembaca_anonim}": ["p(95)<300"],
"http_req_failed": ["rate<0.001"],
"waktu_island": ["p(95)<100"],
"gagal_island": ["rate<0.001"],
},
};
// Variasikan URL: pembaca sungguhan tidak membuka artikel yang sama
function slugAcak() {
const n = Math.floor(Math.random() * 2000);
return `artikel-uji-${n}`;
}
export function bacaArtikel() {
const res = http.get(`${ORIGIN}/berita/${slugAcak()}`, { headers: H });
check(res, { "artikel 200": (r) => r.status === 200 });
}
export function bukaBeranda() {
const res = http.get(`${ORIGIN}/`, { headers: H });
check(res, { "beranda 200": (r) => r.status === 200 });
}
export function memberBaca() {
group("member", () => {
const halaman = http.get(`${ORIGIN}/berita/${slugAcak()}`, {
headers: { ...H, Cookie: `__Host-sesi=${__ENV.SESI_UJI}` },
});
check(halaman, { "halaman 200": (r) => r.status === 200 });
// Server island: inilah yang benar-benar memukul origin per pembaca
const island = http.get(`${ORIGIN}/_server-islands/KonteksPembaca`, {
headers: { ...H, Cookie: `__Host-sesi=${__ENV.SESI_UJI}` },
});
waktuIsland.add(island.timings.duration);
gagalIsland.add(island.status !== 200);
});
}
ramping-arrival-rate, bukan ramping-vus. Bedanya penting: eksekutor
berbasis VU akan melambat saat sistemmu melambat โ sepuluh VU yang menunggu respons lambat
menghasilkan lebih sedikit request per detik, sehingga uji beban justru meringankan sistem yang sedang
kepayahan. Eksekutor berbasis laju kedatangan mempertahankan tekanan apa pun yang terjadi, yang jauh
lebih menyerupai pembaca sungguhan.
Menyiapkan datanya
-- Uji beban butuh artikel yang benar-benar ada dan bervariasi ukurannya
INSERT INTO artikel (judul, slug, isi, ringkasan, status, terbit_pada, penulis_id, premium)
SELECT
CONCAT('Artikel uji ', n),
CONCAT('artikel-uji-', n),
REPEAT('<p>Paragraf isi artikel untuk uji beban.</p>', 20 + (n % 60)),
CONCAT('Ringkasan artikel uji ', n),
'terbit', NOW() - INTERVAL (n % 365) DAY, 1, n % 5 = 0
FROM angka WHERE n < 2000;
Variasikan panjang artikel. Kalau semua artikel ujimu sama panjang, kamu tidak akan pernah menemukan bahwa artikel 40.000 kata membuat render p99-mu meledak. Portal berita punya distribusi panjang yang lebar โ dari berita singkat sampai laporan mendalam.
Menjalankan
# 1. Kapasitas origin โ ini yang menentukan jumlah task
TARGET=https://astro-origin.portal.contoh.id \
ORIGIN_TOKEN=$TOKEN SESI_UJI=$SESI \
k6 run uji/beban.js
# 2. Pengalaman pembaca lewat Cloudflare
TARGET=https://portal.contoh.id k6 run uji/beban.js
# 3. Cache dingin โ jalankan SETELAH purge, di lingkungan staging
# Jangan pernah melakukan ini di produksi saat jam sibuk.
Membaca hasilnya
http_req_duration..............: p(50)=31ms p(95)=118ms p(99)=284ms
http_req_failed................: 0.00%
waktu_island...................: p(50)=9ms p(95)=41ms
iterations.....................: 43821 60.8/s
โ artikel 200
โ halaman 200
| Gejala | Kemungkinan penyebab |
|---|---|
| p95 naik tajam pada laju tertentu | Kapasitas terlampaui โ catat angkanya, itu titik patahmu |
| Error mulai muncul, CPU rendah | Pool koneksi database โ kembali ke Fase 2 |
| p99 โซ p95 | Beberapa halaman jauh lebih berat; cari mana |
| Latensi naik perlahan sepanjang uji | Kebocoran memori atau koneksi |
| Task di-restart di tengah uji | OOM โ periksa max-old-space-size |
| Island jauh lebih lambat dari halaman | Kueri sesi tanpa index, atau membaca dari writer |
Cari titik patah
// Naikkan terus sampai rusak. Ini yang paling berguna dari semua uji.
export const options = {
scenarios: {
patah: {
executor: "ramping-arrival-rate",
startRate: 20, timeUnit: "1s",
preAllocatedVUs: 200, maxVUs: 2000,
stages: [
{ duration: "2m", target: 50 },
{ duration: "2m", target: 100 },
{ duration: "2m", target: 200 },
{ duration: "2m", target: 400 },
{ duration: "2m", target: 800 },
],
exec: "bacaArtikel",
},
},
};
"Lulus uji beban" tidak memberitahumu apa pun tentang seberapa jauh kamu dari batas. Yang ingin kamu ketahui: pada laju berapa p95 mulai memburuk, dan apa yang patah lebih dulu โ CPU, memori, pool koneksi, atau database. Angka itu yang menentukan seberapa besar lonjakan yang bisa kamu hadapi tanpa autoscaling sempat bereaksi.
Pantau sisi sistem selama uji
| Metrik | Yang dicari |
|---|---|
| CPU task ECS | Harus naik seiring beban; kalau tidak, ada bottleneck lain |
| Memori task | Harus stabil; naik terus = kebocoran |
DatabaseConnections | Jangan mendekati batas |
| CPU RDS | Kalau ini yang jenuh, menambah task tidak menolong |
| Jumlah task | Apakah autoscaling bereaksi seperti yang diharapkan? |
| Log kueri lambat | Kueri mana yang muncul di bawah beban |
Kapan menjalankannya
| Kapan | Skenario |
|---|---|
| Sebelum cutover | Semuanya, termasuk titik patah |
| Setelah perubahan arsitektur besar | Kapasitas origin |
| Sebelum peristiwa yang diperkirakan ramai | Kapasitas + cache dingin |
| Per kuartal | Kapasitas, untuk mendeteksi regresi perlahan |
Latihan: siapkan 2.000 artikel uji dengan panjang bervariasi, lalu jalankan skenario campuran ke origin langsung. Catat p95 pada puncak yang kamu perkirakan. Lalu jalankan uji titik patah dan catat dua angka: laju di mana p95 melewati 300 ms, dan apa yang jenuh lebih dulu. Bandingkan hasilnya dengan hitungan kapasitas yang kamu buat di Fase 10.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.