Menguji performa API endpoints secara spesifik — response time per endpoint, payload validation, dan error handling — serta mengidentifikasi database bottleneck: query latency, N+1 queries, connection pooling, dan indexing.

Setelah di episode 3-6 kita memahami berbagai genre testing (load, stress, soak, spike) dan tools (k6, JMeter, Locust), kini saatnya memperdalam testing ke area spesifik: API endpoints dan database performance. Di dunia modern, sebagian besar aplikasi berinteraksi lewat API, dan di balik setiap API ada database yang menyimpan dan mengambil data. Keduanya adalah titik bottleneck paling umum.
Episode ini membawa kalian dari menguji satu endpoint hingga memahami bagaimana database mempengaruhi performa API — dengan pendekatan terstruktur yang bisa langsung kalian praktikkan.
Load testing API dimulai dari satu endpoint. Pastikan kalian menguji bukan hanya HTTP status, tetapi juga response body dan response time per endpoint — karena setiap endpoint punya karakteristik berbeda.
import http from 'k6/http';
import { check } from 'k6';
export const options = {
vus: 50,
duration: '2m',
};
export default function () {
// GET products - read-heavy, harus cepat
const products = http.get('https://api.example.com/products');
check(products, {
'products status 200': (r) => r.status === 200,
'products has items': (r) => r.json().length > 0,
'products < 200ms': (r) => r.timings.duration < 200,
});
// POST order - write-heavy, mungkin lebih lambat
const order = http.post('https://api.example.com/orders', JSON.stringify({
productId: 1,
quantity: 2,
}), { headers: { 'Content-Type': 'application/json' } });
check(order, {
'order status 201': (r) => r.status === 201,
'order < 500ms': (r) => r.timings.duration < 500,
});
}Setiap endpoint punya karakteristik berbeda. Produk list mungkin harus di bawah 200ms, tetapi checkout mungkin boleh sampai 500ms. Buat thresholds per endpoint:
export const options = {
thresholds: {
'http_req_duration{endpoint:products}': ['p(95)<200'],
'http_req_duration{endpoint:orders}': ['p(95)<500'],
'http_req_duration{endpoint:search}': ['p(95)<300'],
},
};API performance bukan hanya tentang latency — tetapi juga tentang ukuran payload. Response yang terlalu besar memakan bandwidth dan memperlambat client-side rendering. Ukur ukuran response:
export default function () {
const res = http.get('https://api.example.com/products');
check(res, {
'response size < 50KB': (r) => r.body.length < 50000,
'status is 200': (r) => r.status === 200,
});
// Ukuran response mempengaruhi bandwidth dan client rendering
}Database bottleneck paling sering muncul dalam tiga bentuk: query latency tinggi, N+1 queries, dan connection pool exhaustion. Performance test yang baik harus mengidentifikasi ketiganya.
Query latency adalah waktu yang dibutuhkan database untuk mengeksekusi satu query. Untuk mengukurnya, kita perlu menghubungkan response time API dengan query time database — biasanya lewat database profiling atau application performance monitoring (APM).
# PostgreSQL: enable slow query logging
ALTER SYSTEM SET log_min_duration_statement = 200; # Log query > 200ms
SELECT pg_reload_conf();
# Lihat slow queries
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;N+1 queries adalah anti-pattern di mana aplikasi menjalankan 1 query untuk mendapatkan N items, lalu 1 query lagi untuk setiap item — menghasilkan N+1 queries total. Di bawah load, ini menjadi bottleneck masif.
-- Query pertama: ambil 100 products
SELECT * FROM products LIMIT 100;
-- N queries tambahan: ambil category untuk setiap product
SELECT * FROM categories WHERE id = 1;
SELECT * FROM categories WHERE id = 2;
-- ... 98 queries lagiCara mendeteksi: hitung jumlah query per request di APM. Jika 1 request ke /products menghasilkan 101 queries → N+1 problem.
Connection pool adalah pool koneksi database yang dibagikan antar threads/requests. Jika pool terlalu kecil, request harus menunggu koneksi tersedia — menambah latency. Jika pool terlalu besar, database kehabisan connections.
// k6 test yang mengungkap connection pool issues
export const options = {
vus: 200, // 200 VUs = 200 concurrent connections
duration: '2m',
};
export default function () {
// Jika connection pool di aplikasi hanya 50,
// 200 VUs akan menyebabkan waiting dan timeout
const res = http.get('https://api.example.com/heavy-query');
check(res, {
'status 200': (r) => r.status === 200,
'no timeout': (r) => r.timings.duration < 5000,
});
}Index yang tidak ada atau tidak tepat adalah penyebab utama query lambat. Saat load test, query yang tadinya cepat di satu user bisa menjadi lambat di banyak concurrent users karena lock contention pada index.
-- Cek index usage di PostgreSQL
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC
LIMIT 10;
-- Index yang tidak pernah dipakai = overhead write tanpa manfaat readPerformance test yang powerful menggabungkan API-level testing dengan database-level monitoring. Saat menjalankan k6 test, pastikan kalian melihat:
Jika API latency naik dari 100ms ke 500ms tetapi database query time tetap 10ms → bottleneck di aplikasi (CPU, serialization, atau external call), bukan di database. Jika database query time naik dari 10ms ke 200ms → bottleneck di database.
// Ramp-up agresif untuk menguji connection pool
export const options = {
stages: [
{ duration: '1m', target: 50 }, // Normal
{ duration: '1m', target: 100 }, // Above pool size
{ duration: '2m', target: 100 }, // Steady-state above pool
{ duration: '1m', target: 200 }, // Far above pool
{ duration: '2m', target: 200 }, // Steady-state far above
{ duration: '1m', target: 0 },
],
};Jika pool size = 50, maka di 100 VUs kalian akan melihat latency naik — ini menunjukkan request mulai menunggu koneksi. Di 200 VUs, error rate bisa melonjak jika timeout terlalu pendek.
Di episode 7 ini kalian telah memahami API & database performance testing:
Di episode 8 selanjutnya, kita akan beralih ke sisi lain performa: Frontend & Web Performance — Core Web Vitals, Lighthouse, dan bagaimana menguji performa dari sisi client. Sampai jumpa di episode 8!