Belajar k6 - Skenario Kompleks dan Multi-endpoint Workloads
Series/Belajar k6/Episode 9
Episode 9 of 19

Belajar k6 - Skenario Kompleks dan Multi-endpoint Workloads

Menyusun campuran workload average, spike, dan soak dalam satu skrip dengan scenarios executor, memisahkan trafik per endpoint lewat group, dan mengukur hal spesifik dengan custom metrics Trend dan Rate dari k6/metrics.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Pendahuluan

Di episode 8 kalian menyalurkan data uji ke dalam skrip — beban yang dihasilkan sekarang realistis dari sisi input. Tapi perhatikan: semua contoh sejauh ini memakai satu model beban tunggal. vus: 10 selama satu menit menjawab pertanyaan "bagaimana server menangani beban stabil?" — namun tidak menjawab "bagaimana server menangani lonjakan lima kali lipat?" atau "apakah ada kebocoran memori setelah setengah jam?".

Episode ini menutup fondasi dengan menyatukan ketiganya. Kita akan menyusun skenario kompleks dan multi-endpoint workloads: memahami tiga model beban besar (average, spike, soak), menjalankan beberapa model dalam satu skrip lewat opsi scenarios, memisahkan trafik per endpoint dengan group(), dan mengukur hal yang spesifik bagi bisnis dengan custom metrics Trend dan Rate. Ini episode paling dekat dengan pekerjaan load tester profesional yang sesungguhnya.

Tiga Model Workload yang Wajib Dikuasai

Sebelum menulis konfigurasi, pahami mengapa tiga model ini ada. Masing-masing menjawab pertanyaan bisnis yang berbeda, dan mengabaikan salah satunya berarti membiarkan kelas kegagalan tertentu lolos:

ModelTujuanBentuk BebanPertanyaan yang Dijawab
Average / LoadKondisi normal puncakNaik bertahap, stabil, turunBagaimana performa saat trafik harian tersibuk?
SpikeLonjakan mendadakNaik drastis dalam hitungan detikApakah server selamat dari ledakan trafik?
SoakKetahanan jangka panjangKonstan, durasi panjang (jam)Adakah degradasi karena kebocoran memori atau koneksi?

Ketiganya tidak saling menggantikan — mereka melengkapi. Tim yang matang menjalankan average setiap rilis, spike sebelum event besar, dan soak berkala untuk memvalidasi stabilitas jangka panjang. Di episode 9 ini kalian akan menjalankan ketiganya dalam satu perintah.

Opsi scenarios: Banyak Executor dalam Satu Skrip

Kunci dari "satu skrip, banyak beban" adalah opsi scenarios. Ia memungkinkan kalian mendefinisikan beberapa skenario yang berjalan bersamaan, masing-masing dengan executor, beban, dan fungsi eksekusinya sendiri:

Tiga skenario: smoke, load, soak
export const options = {
  scenarios: {
    smoke: {
      executor: "shared-iterations",
      vus: 1,
      iterations: 10,
      exec: "checkoutFlow",
    },
    load: {
      executor: "ramping-vus",
      startTime: "30s",
      stages: [
        { duration: "1m", target: 80 },
        { duration: "3m", target: 80 },
        { duration: "1m", target: 0 },
      ],
      exec: "checkoutFlow",
    },
    soak: {
      executor: "constant-vus",
      startTime: "1m",
      vus: 40,
      duration: "15m",
      exec: "checkoutFlow",
    },
  },
};

Baca strukturnya: setiap skenario punya executor (mekanisme beban), exec (fungsi yang dijalankan), dan opsional startTime (kapan mulai relatif terhadap awal test). Mari petakan ke model workload:

  • shared-iterations — jumlah total iterasi dibagi merata ke VU. Cocok untuk smoke test: cukup satu VU dan beberapa iterasi untuk memvalidasi kebenaran fungsional sebelum beban besar datang.
  • ramping-vus — VU naik-turun mengikuti stages. Jantung average load dan spike (dengan stages yang menaikkan target sangat cepat).
  • constant-vus — jumlah VU tetap selama durasi tertentu. Bentuk alami soak test.

Ada dua executor tambahan yang perlu kalian kenali: constant-arrival-rate dan ramping-arrival-rate — berbasis throughput (jumlah iterasi per detik) alih-alih jumlah VU. Kalian memakainya saat targetnya adalah request per second yang harus dipertahankan, bukan sekadar jumlah pengguna.

Satu aturan emas saat memakai scenarios: vus, duration, dan stages level atas tidak boleh digunakan — semuanya hidup di dalam masing-masing skenario. Jika tetap dipakai, k6 mengabaikannya atau menolak, dan hasilnya membingungkan.

Memisahkan Trafik dengan group()

Satu skrip multi-endpoint berarti hasil http_req_duration menggabungkan semua URL. Untuk melihat performa per endpoint, k6 sudah memberi tag url per request. Tapi untuk pemisahan yang lebih bermakna secara bisnis — "alur pembelian" vs "alur pencarian" — gunakan group():

Memisahkan trafik per alur bisnis
import http from "k6/http";
import { check, group } from "k6";
 
export function checkoutFlow() {
  group("browse", () => {
    const res = http.get("https://api.example.com/products");
    check(res, { "daftar produk 200": (r) => r.status === 200 });
  });
 
  group("beli", () => {
    const res = http.post("https://api.example.com/orders", "{}", {
      headers: { "Content-Type": "application/json" },
    });
    check(res, { "order dibuat": (r) => r.status === 201 });
  });
}

group() membungkus blok request dengan label yang muncul di summary — durasi total, request count, dan check masing-masing grup dilaporkan terpisah. Ini mengubah output dari "kumpulan URL" menjadi "cerita bisnis": berapa lama fase browse? berapa lama fase beli? Bagi tim non-teknis, perbedaan ini sangat berharga.

Custom Metrics: Trend dan Rate

Metrik bawaan k6 mengukur HTTP secara umum. Tapi bisnis kalian punya metrik sendiri — durasi pembayaran, rasio order gagal, jumlah transaksi per detik. k6/metrics menyediakan empat tipe untuk ini: Counter (jumlah total), Gauge (nilai terakhir), Trend (distribusi nilai, mirip http_req_duration), dan Rate (rasio nilai benar-salah). Dua yang paling sering dipakai: Trend dan Rate.

Trend dan Rate untuk metrik bisnis
import http from "k6/http";
import { check } from "k6";
import { Trend, Rate } from "k6/metrics";
 
const paymentLatency = new Trend("payment_duration", true);
const paymentErrors = new Rate("payment_errors");
 
export const options = {
  thresholds: {
    payment_duration: ["p(95)<700"],
    payment_errors: ["rate<0.01"],
  },
};
 
export default function () {
  const res = http.post("https://api.example.com/payments", "{}", {
    headers: { "Content-Type": "application/json" },
  });
 
  paymentLatency.add(res.timings.duration);
  paymentErrors.add(res.status !== 200);
  check(res, { "pembayaran diterima": (r) => r.status === 201 });
}

Bedah tiga baris terpentingnya:

  • new Trend("payment_duration", true) — argumen kedua true memberitahu k6 bahwa ini metrik waktu, sehingga dilaporkan dalam milidetik dengan format yang sama seperti http_req_duration. Nilai ditambah dengan .add(ms).
  • new Rate("payment_errors") — menerima boolean: add(res.status !== 200) menambah pembilang hanya saat kondisi true. Hasilnya adalah rasio error dalam desimal.
  • Threshold langsung bisa mengikat metrik ini — "p(95)<700" dan "rate<0.01" menegakkan standar pada hal yang spesifik bagi bisnis, bukan hanya HTTP mentah.

Perhatikan alasan di baliknya: http_req_duration mengukur semua request di skrip, campuran browse dan beli. payment_duration mengisolasi hanya fase pembayaran. Custom metrics adalah cara mengubah data mentah menjadi sinyal yang bisa ditindaklanjuti.

Skenario Kompleks yang Utuh

Rangkai semua lapisan — scenarios, group, dan custom metrics — menjadi satu skrip profesional:

Skenario lengkap: smoke, load, soak + metrik bisnis
import http from "k6/http";
import { check, group } from "k6";
import { Trend, Rate } from "k6/metrics";
 
const paymentLatency = new Trend("payment_duration", true);
const paymentErrors = new Rate("payment_errors");
 
export const options = {
  scenarios: {
    smoke: {
      executor: "shared-iterations",
      vus: 1,
      iterations: 5,
      exec: "checkoutFlow",
    },
    load: {
      executor: "ramping-vus",
      startTime: "30s",
      stages: [
        { duration: "1m", target: 80 },
        { duration: "3m", target: 80 },
        { duration: "1m", target: 0 },
      ],
      exec: "checkoutFlow",
    },
    soak: {
      executor: "constant-vus",
      startTime: "1m",
      vus: 40,
      duration: "15m",
      exec: "checkoutFlow",
    },
  },
  thresholds: {
    http_req_duration: ["p(95)<500"],
    payment_duration: ["p(95)<700"],
    payment_errors: ["rate<0.01"],
  },
};
 
export function checkoutFlow() {
  group("browse", () => {
    check(http.get("https://api.example.com/products"), {
      "daftar produk 200": (r) => r.status === 200,
    });
  });
 
  group("beli", () => {
    const pay = http.post("https://api.example.com/payments", "{}", {
      headers: { "Content-Type": "application/json" },
    });
    paymentLatency.add(pay.timings.duration);
    paymentErrors.add(pay.status !== 201);
    check(pay, { "pembayaran diterima": (r) => r.status === 201 });
  });
}

Jalankan dengan k6 run script.js dan perhatikan yang terjadi: smoke berjalan lebih dulu memvalidasi alur, lalu load merangkak naik menekan server, dan soak memegang beban stabil selama 15 menit — ketiganya menembak fungsi checkoutFlow yang sama. Satu skrip, tiga pertanyaan bisnis, tiga jawaban terpisah.

Ketiga skenario berbagi fungsi yang sama, jadi pastikan mereka tidak saling mengganggu state — pola per-VU dari episode 5 (__VU) menjamin setiap VU punya sesi sendiri. Jika satu skenario butuh alur berbeda, tinggal definisikan fungsi exec kedua.

Kesalahan Umum

  1. Menaruh vus atau duration di samping scenarios. Konfigurasi level atas dan scenarios tidak boleh bercampur — beban harus hidup di dalam skenario.
  2. Mengukur satu metrik untuk semua endpoint. http_req_duration mencampur semua URL. Pisahkan dengan group() dan custom metrics.
  3. Memakai beban konstan untuk semua pertanyaan. Spike dan soak butuh bentuk beban yang berbeda; satu model tidak menjawab semua.
  4. Lupa startTime untuk skenario paralel. Tanpa jeda, semua skenario menyerbu sejak detik pertama dan hasilnya tak terbaca.
  5. Menambahkan metrik tanpa threshold. Metrik yang tidak diikat threshold hanyalah angka yang tidak menegakkan standar.

Penutup

Di episode 9 ini kalian telah merakit fondasi lengkap load testing profesional:

  • Tiga model workload: average, spike, dan soak — masing-masing menjawab pertanyaan bisnis yang berbeda.
  • Opsi scenarios: banyak executor dalam satu skrip, dengan shared-iterations untuk smoke, ramping-vus untuk load, dan constant-vus untuk soak.
  • group(): memisahkan trafik per alur bisnis agar hasil terbaca sebagai cerita, bukan daftar URL.
  • Custom metrics: Trend dan Rate dari k6/metrics untuk mengukur dan mengikat standar pada hal yang spesifik.

Selamat — kalian kini menulis load test yang tidak hanya mengirim request, tetapi bercerita tentang sistem. Namun ada satu lapisan yang sejauh ini kita perlakukan sebagai kotak hitam: jaringan itu sendiri. Di episode 10 kita membuka lapisan itu — Jaringan, TLS, dan Protocol Support: mengendalikan verifikasi TLS, perilaku koneksi, redirect, dan user agent, serta menguji WebSocket. Karena di bawah beban tinggi, yang pertama menyerah sering kali bukan kode aplikasi kalian, melainkan lapisan jaringannya. Sampai jumpa di episode 10!