Performance Engineering & Resource Management
Mengukur dan meningkatkan performa aplikasi: latency, throughput, profiling, load testing, dan alokasi resource.
Performa bukan sekadar “server cepat”. Ia adalah kemampuan memenuhi target latency dan throughput pada biaya yang wajar. Engineering performa dimulai dari pengukuran, bukan tebakan.
Metrik utama
| Metrik | Arti |
|---|---|
| Latency | Waktu satu request |
| Throughput | Request per detik |
| Error rate | Persentase request gagal |
| Saturation | Seberapa penuh resource |
| Apdex | Kepuasan pengguna berdasarkan latency |
Gunakan persentil, bukan rata-rata:
- p50 — median.
- p95 — 95% request lebih cepat dari ini.
- p99 — ekor latency; sering menunjukkan masalah.
Performance budget
Tentukan target sejak awal:
API p95 < 300 ms
Halaman utama p95 < 1.5 s
Error rate < 0.1%
CPU rata-rata < 70%Tanpa target, optimasi tidak punya arah.
Bottleneck umum
| Lapisan | Bottleneck |
|---|---|
| Client | Network lambat, aset besar |
| Proxy | Timeout, buffer, TLS |
| App | CPU, memory, event loop, query |
| Database | Query lambat, lock, koneksi habis |
| Cache | Miss rate tinggi, eviction |
| Storage | IOPS, throughput |
| Network | Bandwidth, latency, packet loss |
Node.js dan event loop
Node.js memakai event loop single-threaded. Operasi CPU-heavy memblokir semua request.
// Buruk: memblokir event loop
const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512');
// Lebih baik: async
const hash = await pbkdf2(password, salt, 100000, 64, 'sha512');Untuk beban CPU, gunakan worker thread, proses terpisah, atau PM2 cluster.
PM2 cluster
module.exports = {
apps: [{
name: 'app',
script: 'server.js',
instances: 'max',
exec_mode: 'cluster',
}],
};Cluster memanfaatkan semua core, tetapi setiap proses punya memory dan koneksi database sendiri.
Resource limit
Docker Compose:
services:
app:
deploy:
resources:
limits:
cpus: "1.5"
memory: 512M
reservations:
memory: 256MSystemd:
[Service]
MemoryMax=512M
CPUQuota=150%Limit mencegah satu service menghabiskan host, tetapi terlalu ketat memicu OOM/restart.
Load testing
k6
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
vus: 50,
duration: '2m',
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<500'],
},
};
export default function () {
const res = http.get('https://app.example.com/health');
check(res, { 'status 200': (r) => r.status === 200 });
sleep(1);
}k6 run load-test.jsab (ApacheBench)
ab -n 1000 -c 50 https://app.example.com/Jangan melakukan load test terhadap produksi tanpa izin dan tanpa rencana.
Profiling
node --prof server.js
node --prof-process isolate-*.log > profile.txtUntuk production, gunakan APM/OpenTelemetry untuk melihat span paling lambat.
Query database
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20;Perhatikan:
- Sequential scan pada tabel besar.
- Index yang tidak dipakai.
- N+1 query.
- Sorting dan join mahal.
Caching
Cache mengurangi beban database dan latency. Namun cache yang salah dapat menyajikan data basi.
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const data = await db.query(...);
await redis.set(key, JSON.stringify(data), 'EX', 60);
return data;Optimasi berurutan
- Ukur baseline.
- Temukan bottleneck terbesar.
- Perbaiki satu hal.
- Ukur ulang.
- Ulangi.
Optimasi prematur
Optimasi tanpa data sering menambah kompleksitas tanpa manfaat. Fokus pada bottleneck yang terukur dan berdampak pada pengguna.
Ringkasan
- Ukur latency, throughput, error rate, dan saturation.
- Gunakan p95/p99, bukan rata-rata.
- Identifikasi bottleneck di app, database, cache, atau jaringan.
- Load test dan profiling memandu optimasi.
- Cache dan clustering membantu, tetapi bukan solusi semua masalah.
Selanjutnya
Tambahkan cache produksi di Caching & Redis untuk Production.