Belajar k6 - Analisis Performansi dan Optimasi Scripting
Series/Belajar k6/Episode 13
Episode 13 of 19

Belajar k6 - Analisis Performansi dan Optimasi Scripting

Memaknai hasil load test dengan benar: membaca metrik utama seperti jumlah VU, latensi, error, dan persentil p95 serta p99, lalu mengoptimalkan script k6 dengan http.batch, discardResponseBodies, dan expectedStatuses, serta menyusun threshold yang menargetkan SLA atau SLO.

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

Pendahuluan

Setelah episode 12, tes kalian aman, jujur, dan bisa dijalankan tanpa membuat tim infra marah. Tapi menjalankan tes hanyalah setengah cerita. Pertanyaan sesungguhnya muncul setelah angka muncul di layar: apa arti semua angka ini, dan apakah aplikasi kalian sehat?

Seorang dokter tidak mengobati pasien hanya dari satu angka tensi — ia melihat denyut, suhu, kadar oksigen, dan pergerakannya dalam waktu. Demikian pula load test: satu angka rata-rata bisa menipu. Episode 13 ini membahas dua sisi yang saling mendukung: bagaimana membaca hasil (metrik mana yang penting, dan mengapa p95 lebih jujur daripada rata-rata), dan bagaimana membuat script tidak bohong — karena mesin tes yang terlalu sibuk dengan JavaScript akan menghasilkan data yang justru mengukur k6, bukan aplikasi kalian.

Pembahasan Utama

Memahami Metrik Utama: Apa yang Sebenarnya Diukur

Ringkasan akhir k6 (end-of-test summary) berisi deretan metrik yang sering membuat pemula kebingungan. Berikut yang paling penting untuk dijadikan teman:

MetrikIsiYang harus kalian pikirkan
vusjumlah virtual user aktifapakah mendekati target yang direncanakan
http_req_durationdurasi total satu requestmetrik latensi utama, lihat avg, med, p(90), p(95)
http_reqsjumlah total request + rate per detikapakah throughput memenuhi target
http_req_failedproporsi request gagal (status 4xx/5xx, koneksi error)error rate yang disepakati dalam SLO
http_req_blockedwaktu menunggu slot untuk membuka koneksimembengkak jika koneksi dibuka ulang terus
http_req_connectingdurasi TCP + TLS handshakemengukur overhead jaringan, bukan aplikasi

Metrik http_req_duration adalah bintangnya, dan di dalamnya k6 menampilkan beberapa statistik sekaligus: avg, med, min, max, p(90), p(95). Contoh ringkasan:

Ringkasan akhir k6 — http_req_duration
http_req_duration..............: avg=148.2ms  min=42.1ms  med=120.5ms  max=1.42s  p(90)=310.7ms  p(95)=520.9ms
http_req_failed................: 0.25% (8 gagal dari 3131 request)
http_reqs......................: 3131    104.4/s
http_req_blocked...............: avg=3.1ms  min=0ms  med=0ms  max=120ms
http_req_connecting............: avg=8.4ms  min=0ms  med=0ms  max=98ms

Perhatikan betapa jujurnya baris pertama: rata-rata 148.2 ms, median 120.5 ms, tapi ada lonjakan hingga 1.42 s, dan 5 persen request memakan lebih dari 520.9 ms. Kalau kalian hanya melihat rata-rata, kalian menyimpulkan aplikasi sehat. Persentil membongkar kenyataan: ada sebagian pengguna yang mengalami pengalaman jauh lebih buruk.

Kenapa p95 dan p99 Lebih Jujur daripada Rata-rata

Ini pelajaran statistik yang paling penting dalam performance testing. Rata-rata bisa menyembunyikan ekor yang panjang: 99 request selesai dalam 100 ms dan satu request selesai dalam 10 detik menghasilkan rata-rata ~200 ms — terlihat baik, padahal ada pengguna yang menunggu 10 detik.

Persentil menceritakan kisah yang berbeda:

  • p(95) — 95 persen request selesai dalam nilai ini. 5 persen sisanya, entah seberapa lama, tidak ikut dalam angka ini.
  • p(99) — 99 persen request selesai dalam nilai ini. Ini yang mendekati pengalaman pengguna paling tidak beruntung.

Konsistensi persentil adalah indikator stabilitas yang jauh lebih kuat. Jika p(95) stabil sementara avg naik, mungkin ada beberapa outlier yang tidak mewakili mayoritas. Jika p(95) melonjak, mayoritas pengguna benar-benar terdampak. Inilah mengapa SLA yang baik berbunyi "95 persen request selesai di bawah 300 ms", bukan "rata-rata request di bawah 300 ms".

Optimasi Script: Buat Mesin Tes Tidak Bohong

k6 mengeksekusi JavaScript di atas Go runtime (Sobek). Setiap pekerjaan yang tidak perlu yang dilakukan script adalah biaya yang tercampur ke metrik — dan jika mesin tes kehabisan sumber daya, tes mengukur mesin tes, bukan aplikasi. Empat teknik paling berdampak:

1. Kurangi overhead JavaScript. Pindahkan komputasi yang bisa dihitung sekali ke setup() atau scope modul. Parsing JSON berulang-ulang untuk data yang sama, atau membangun objek header baru di setiap iterasi, adalah sampah yang memperlambat VU. Hitung sekali, simpan, pakai ulang.

2. Pakai http.batch() untuk request paralel. Jika sebuah halaman memuat beberapa endpoint yang independen, jangan panggil satu per satu secara berurutan — itu memperpanjang durasi iterasi secara artifisial. http.batch() mengirimnya paralel dalam satu iterasi:

batch.js — kirim beberapa request sekaligus
import http from "k6/http";
import { check } from "k6";
 
const responses = http.batch([
    ["GET", "https://api.example.com/v1/user"],
    ["GET", "https://api.example.com/v1/orders"],
    ["GET", "https://api.example.com/v1/notifications"],
]);
 
for (const [nama, res] of [
    ["user", responses[0]],
    ["orders", responses[1]],
    ["notifications", responses[2]],
]) {
    check(res, {
        [`${nama} merespons 200`]: (r) => r.status === 200,
    });
}

http.batch() sekaligus mengurangi overhead: request dikirim bersamaan dan hasilnya dikembalikan sebagai array. Default batch memungkinkan 20 koneksi paralel per panggilan, cukup untuk hampir semua kasus.

3. Aktifkan discardResponseBodies. Ini optimasi dengan dampak paling besar dan usaha paling kecil. Jika kalian tidak perlu membaca body response, k6 masih menyimpannya ke memori — dan mendorong GC bekerja lebih keras. Matikan dengan satu opsi:

options — buang body yang tidak dipakai
export const options = {
    vus: 50,
    duration: "5m",
    discardResponseBodies: true,
};

Untuk request yang memang butuh body-nya, set responseType: "text" per-request. Hasilnya: memori tes turun drastis, GC jarang dipicu, dan metrik latensi tidak terkontaminasi oleh kerja tambahan mesin tes.

4. Gunakan http.setResponseCallback. Secara default k6 menganggap status 2xx dan 3xx sebagai sukses. Jika aplikasi kalian punya kode status sukses khusus (misal 202 Accepted untuk async job), set callback di level opsi:

options — definisikan status yang dianggap sukses
import http from "k6/http";
 
export const options = {
    vus: 50,
    duration: "5m",
    setResponseCallback: http.expectedStatuses(200, 201, 202),
};

Dengan ini http_req_failed hanya menghitung status di luar daftar — metrik error menjadi akurat, dan threshold yang kalian susun berdiri di atas fondasi yang benar.

Menyusun Threshold: Target SLA dan SLO

Angka yang bagus tidak ada artinya tanpa batas yang disepakati. Threshold adalah garis batas yang membuat k6 gagal secara eksplisit ketika aplikasi melanggar target — ini yang mengubah load test dari "melihat-lihat angka" menjadi "pemeriksaan penerimaan". Susun threshold berdasarkan SLO kalian:

options — threshold berbasis SLO
export const options = {
    vus: 100,
    duration: "10m",
    thresholds: {
        http_req_duration: ["p(95)<300", "p(99)<800"],
        http_req_failed: ["rate<0.01"],
        http_reqs: ["rate>50"],
    },
};
  • "p(95)<300" — 95 persen request selesai di bawah 300 ms: SLO latensi utama.
  • "p(99)<800" — 99 persen di bawah 800 ms: pengaman untuk pengguna paling tidak beruntung.
  • "rate<0.01" — error rate di bawah 1 persen.
  • "rate>50" — throughput minimum 50 request per detik.

Jika threshold dilanggar, k6 keluar dengan exit code non-nol — dan CI/CD bisa langsung menolak rilis. Untuk beban ekstra, tambahkan { abortOnFail: true, delayAbortEval: "30s" } agar tes berhenti lebih awal ketika threshold sudah pasti gagal, tanpa menunggu durasi penuh.

Kesalahan Umum dalam Membaca Hasil

  1. Hanya melihat avg — sembunyikan ekor panjang. Lihat p(95) dan p(99).
  2. Mengabaikan http_req_failed — error 1 persen dari 100 ribu request adalah 1.000 pengguna yang gagal.
  3. Membandingkan angka lintas lingkungan berbeda — staging dengan spek lebih kecil tidak bisa dibandingkan dengan produksi.
  4. Script yang boros — parsing berulang dan body yang disimpan membuat tes mengukur mesin sendiri. Optimalkan sebelum menyimpulkan apa pun.

Penutup

Pada episode 13 ini kalian telah mampu membaca hasil tes seperti seorang profesional: memahami metrik utama (vus, http_req_duration, http_reqs, http_req_failed, http_req_blocked, http_req_connecting), melihat kenapa persentil p95 dan p99 lebih jujur daripada rata-rata, mengoptimalkan script dengan pengurangan overhead JavaScript, http.batch(), discardResponseBodies, dan http.setResponseCallback(http.expectedStatuses(...)), serta menyusun threshold yang menargetkan SLA dan SLO — lengkap dengan abortOnFail untuk penghentian dini.

Inti yang harus kalian bawa:

  • Baca p(95) dan p(99), jangan hanya avg.
  • Optimasi script adalah bagian dari metodologi, bukan kemewahan.
  • discardResponseBodies: true untuk tes membaca-bukan-mengolah body.
  • Threshold adalah terjemahan SLO menjadi keputusan pass/fail yang otomatis.

Ringkasan terminal sudah cukup untuk satu tes. Tapi bagaimana kalau kalian menjalankan sepuluh tes per hari, ingin melihat tren mingguan, dan berbagi dashboard dengan tim? Itu butuh observability yang sesungguhnya. Di episode 14 kita akan membahas Observability, Tracing & Monitoring — mengintegrasikan k6 dengan Grafana, InfluxDB, dan Prometheus, memakai output eksternal, serta custom metrics dan tagging untuk analisis yang lebih dalam. Sampai jumpa di episode 14!

Belajar k6 - Analisis Performansi dan Optimasi Scripting | Belajar k6