Episode ini mengukur dan meningkatkan performa: fase-fase event loop dan penyebab blocking, profiling CPU dengan Node Inspector, load testing dengan autocannon, serta strategi deteksi kebocoran memori.

Aplikasi yang berfungsi belum tentu aplikasi yang cepat. Di bawah beban nyata, masalah performa muncul di tempat yang tak terduga: satu fungsi sinkron yang lambat, query yang tidak ber-index, atau kebocoran memori yang menumpuk diam-diam. Menemukan masalah itu butuh pengukuran, bukan tebakan.
Episode 19 membahas performa Node.js secara praktis: cara kerja event loop dan penyebab blocking, profiling dengan Node Inspector, load testing dengan autocannon, serta strategi memantau memori. Kalian akan belajar mengukur dulu, mengoptimalkan setelah tahu di mana hambatannya.
Event loop bekerja dalam beberapa fase yang berjalan berulang: timers, pending callbacks, poll, check, dan close callbacks. Operasi seperti setTimeout diproses di fase timers, sedangkan I/O jaringan diproses di fase poll. Memahami ini membantu kalian memprediksi urutan eksekusi — dan menemukan penyebab latensi.
Dua fase yang sering membingungkan adalah check dan process.nextTick. setImmediate dieksekusi di fase check, sedangkan process.nextTick dijalankan segera setelah operasi JavaScript saat ini selesai — bahkan sebelum fase event loop berikutnya dimulai. Karena sifatnya yang mendahului, memakai process.nextTick berlebihan bisa menunda pekerjaan lain; prefer setImmediate untuk pekerjaan yang bisa menunggu giliran event loop.
Karena hanya satu thread yang menjalankan JavaScript, operasi CPU-intensif yang berjalan di thread utama menghentikan seluruh server. Contoh umum: parsing JSON besar, hashing bcrypt dengan cost tinggi, kompresi data, dan loop panjang. bcrypt.hash dari episode 11 termasuk kasus ini — untuk memisahkan beban, jalankan di worker thread atau offload ke layanan khusus.
const data = [];
for (let i = 0; i < 10000000; i++) {
data.push(i * 2);
}
console.log(data.length);Loop di atas menghabiskan waktu lama di thread utama — selama itu, request lain menunggu. Di produksi, pecah pekerjaan besar atau pindahkan ke worker_threads. Detail strateginya akan kita bedah bersama profiling.
Profiling menjawab pertanyaan "di mana waktu terbuang?". Node.js menyediakan profiler CPU bawaan lewat flag --cpu-prof atau Node Inspector:
node --cpu-prof --cpu-prof-dir=./profil app.jsnode --cpu-prof --cpu-prof-dir=./profil app.js menulis file .cpuprofile selama aplikasi berjalan. Setelah aplikasi dihentikan, buka file itu di Chrome DevTools (tab Performance) atau di chrome://inspect untuk melihat flame chart — grafik fungsi mana yang paling banyak memakan waktu. Untuk aplikasi yang berjalan lama, --cpu-prof-interval bisa disetel agar sampling lebih halus.
Flame chart menampilkan tumpukan panggilan dari bawah ke atas; fungsi dengan batang terlebar adalah yang paling mahal. Pola yang sering muncul: fungsi yang dianggap sepele ternyata dipanggil jutaan kali, atau pemanggilan sinkron yang memblokir. Dari data inilah kalian memutuskan apa yang perlu dioptimasi — bukan dari perasaan. Untuk kebutuhan analisis yang lebih nyaman di terminal, alat seperti clinic.js mengemas profiling dan load test dalam satu panel ringkas.
Untuk pemantauan langsung saat aplikasi berjalan, jalankan node --inspect lalu buka chrome://inspect di browser. Profiler di DevTools merekam aktivitas selama beberapa detik dan menampilkan flame chart yang sama tanpa perlu me-restart aplikasi. Pendekatan ini berguna untuk mengamati kondisi yang sulit direproduksi, misalnya lonjakan beban yang hanya muncul saat banyak pengguna aktif.
Sebelum mengoptimalkan, kalian perlu angka. autocannon adalah tool load testing populer untuk Node.js:
npm install --global autocannon
autocannon -c 100 -d 10 http://localhost:3000/api/artikelautocannon -c 100 -d 10 http://localhost:3000/api/artikel membuka 100 koneksi bersamaan selama 10 detik. Outputnya menampilkan latensi rata-rata, percentil, dan jumlah request per detik — baseline yang bisa kalian bandingkan sebelum dan sesudah optimasi. Jalankan load test di environment terisolasi, bukan di mesin pengembangan yang sedang dipakai kerja, agar hasilnya tidak terkontaminasi aktivitas lain.
Perhatikan tiga angka utama: request rate (berapa request per detik), latency percentil (p95 dan p99 menunjukkan pengalaman terburuk), serta error rate (harus nol). Optimasi dikatakan berhasil jika angka ini membaik, bukan sekadar kode terlihat lebih pintar.
Kebocoran memori membuat heap tumbuh tanpa batas hingga proses crash. Mulai dengan memantau dasar:
setInterval(() => {
const memori = process.memoryUsage();
console.log({
heap: Math.round(memori.heapUsed / 1024 / 1024),
rss: Math.round(memori.rss / 1024 / 1024),
});
}, 10000).unref();process.memoryUsage() melaporkan heapUsed dan rss. setInterval(...).unref() memastikan timer tidak mencegah proses berhenti. Jika heapUsed terus naik dan tidak pernah kembali turun setelah GC, itu tanda kebocoran — referensi yang tidak sengaja disimpan, misalnya listener event yang tidak dihapus.
Penting untuk membedakan kebocoran dari perilaku normal: heap yang naik lalu turun mengikuti pola sawtooth (dipotong oleh garbage collection) adalah hal sehat. Masalah baru muncul ketika titik terendah antar GC ikut naik terus-menerus.
Penyebab kebocoran paling umum: menambah listener on tanpa pernah off, menyimpan data dalam variabel global, dan menyimpan objek besar dalam cache tanpa batas kedaluwarsa. Mulai dengan melepas listener yang tidak dipakai dan menetapkan TTL untuk cache. Pengukuran berkala memakai alat seperti node --heapsnapshot menghasilkan snapshot heap yang bisa dibandingkan antar waktu di DevTools — pertumbuhan objek serupa yang berulang adalah jejak khas kebocoran.
Tuning performa mengikuti siklus ketat: ukur baseline, temukan titik panas lewat profiling, optimalkan satu hal pada satu waktu, lalu ukur ulang untuk membuktikan perbaikan. Optimasi tanpa pengukuran hanyalah spekulasi — dan sering kali mengorbankan keterbacaan kode tanpa hasil nyata. Mulailah dari yang paling berdampak: perbaikan query dan cache (episode 16) sering memberi hasil terbesar sebelum kode aplikasi disentuh.
Inti yang harus dibawa pulang:
--cpu-prof menghasilkan flame chart lokasi waktu terbuang.process.memoryUsage memantau pertumbuhan heap yang mencurigakan.Di episode 20 selanjutnya kita akan membahas security hardening untuk aplikasi Node.js — security headers dengan helmet, validasi input dan pencegahan SQL injection, rate limiting, pengelolaan secrets, serta audit dependensi dengan npm. Kalian akan menutup celah yang paling sering dieksploitasi.