Studi Kasus: Membongkar Kasus API Melambat Tanpa Deployment, Lonjakan Network I/O Database
Studi Kasus
Hari Jumat, pukul 17.00. Waktu yang seharusnya menjadi penutup minggu kerja.
Namun PagerDuty berbunyi.
Laporan awal menunjukkan kondisi yang membingungkan:
- Waktu respon API melonjak dari ±200 ms menjadi ±8 detik
- CPU normal di seluruh instance
- Memory normal
- Tidak ada deployment atau perubahan konfigurasi
- Query database tampak berjalan normal
- Masalah dimulai tepat 47 menit yang lalu
- Satu anomali mencolok: Network I/O pada primary database melonjak tajam di waktu yang sama
Ini bukan insiden biasa. Ini adalah kasus klasik yang menuntut pendekatan analitis, bukan asumsi.
Langkah Pertama: Membaca Sinyal, Jangan Menebak
Kesalahan paling umum saat on-call adalah langsung menebak penyebab berdasarkan pengalaman masa lalu.
Namun pada kasus ini, data berbicara cukup jelas.
Mari kita eliminasi kemungkinan satu per satu.
Apa yang Tidak Terjadi
Berdasarkan metrik:
- Bukan CPU-bound problem → CPU tetap rendah
- Bukan memory leak / GC pressure → memory stabil
- Bukan regresi deployment → tidak ada perubahan kode
- Bukan query lambat secara komputasi → eksekusi query tampak normal
Artinya, bottleneck bukan pada komputasi.
Jika bukan komputasi, maka yang tersisa adalah I/O.
Satu Sinyal yang Berteriak: Network I/O Database
Lonjakan tajam pada network I/O database adalah petunjuk paling penting.
Ini menandakan satu hal:
Database sedang mengirim data jauh lebih banyak dari biasanya.
Bukan membaca. Bukan menghitung. Tapi mengirim.
Diagram Alur Request–Database–Network
Untuk memahami masalah ini dengan cepat, berikut diagram teks yang menggambarkan alur request normal vs saat insiden terjadi.
Kondisi Normal
[ Client ]
|
v
[ API Service ]
|
| (Query kecil + LIMIT)
v
[ Database ] --(hasil data kecil)--> [ API Service ] --> Response cepat (~200 ms)
Pada kondisi normal:
- Database mengeksekusi query dengan cepat
- Data yang dikirim relatif kecil
- Network tidak menjadi bottleneck
Kondisi Saat Insiden
[ Client ]
|
v
[ API Service ]
|
| (Query tanpa LIMIT / dataset besar)
v
[ Database ] --(puluhan/ratusan MB data)---------------------->
^ |
|------ Network saturated / antre ---------|
[ API Service ] menunggu fetch data selesai
Response baru terkirim setelah 8 detik
Pada kondisi ini:
- Eksekusi query tetap cepat
- Namun transfer data memakan waktu lama
- API thread/goroutine idle sambil menunggu network
Diagram ini menjelaskan mengapa CPU rendah, tapi latency tinggi.
Hipotesis Utama: Query Menghasilkan Dataset Sangat Besar
Hipotesis paling masuk akal:
Ada query (atau sekumpulan query) yang tiba-tiba menghasilkan hasil data dalam jumlah sangat besar, sehingga waktu transfer jaringan mendominasi total response time.
Ini menjelaskan seluruh gejala:
- Query bisa selesai cepat (CPU rendah)
- Namun aplikasi menunggu lama saat
fetch rows - API terlihat lambat meskipun database tidak “sibuk”
Mengapa Masalah Ini Sulit Terdeteksi?
Karena banyak engineer hanya melihat:
- Query execution time
- Index usage
- Explain plan
Padahal:
Execution cepat tidak berarti response cepat jika ukuran data besar.
Contoh sederhana:
SELECT * FROM orders WHERE status = 'PAID';
Query ini bisa dieksekusi sangat cepat, tetapi jika mengembalikan ratusan ribu baris, waktu transfer bisa mencapai beberapa detik.
Petunjuk Waktu: Mengapa Tepat 47 Menit Lalu?
Masalah yang muncul tepat di menit tertentu hampir selalu disebabkan oleh sistem terjadwal, bukan manusia.
Kemungkinan yang perlu dicurigai:
- Cron job internal
- Scheduled report / export data
- Cache TTL yang habis serentak
- Background worker yang aktif berkala
Tidak ada deploy, tapi perilaku sistem berubah.
Pola Umum Penyebab di Lapangan
Dari pengalaman produksi, penyebab paling sering adalah:
Pagination atau LIMIT yang Hilang
Sebelumnya:
SELECT * FROM users ORDER BY created_at DESC LIMIT 50;
Tanpa disadari berubah menjadi:
SELECT * FROM users ORDER BY created_at DESC;
Dampaknya bukan CPU, tapi network saturation.
Cache Miss Storm
- Cache expired bersamaan
- Semua request menembus database
- Query yang sama mengirim data besar berulang kali
Network I/O database naik drastis, meski query cepat.
Scheduled Export atau Integrasi Eksternal
- BI tool
- Data sync partner
- Admin export CSV
Semua ini sering:
- Tidak tercatat sebagai deployment
- Mengakses database secara langsung
- Menarik data dalam jumlah besar
Langkah Investigasi Teknis yang Tepat
Cari Query dengan Jumlah Rows Terbesar
Fokus bukan pada durasi, tapi jumlah data.
Contoh pada PostgreSQL:
SELECT query, calls, rows
FROM pg_stat_statements
ORDER BY rows DESC
LIMIT 10;
Bandingkan dengan baseline historis.
Pecah Waktu di APM / Tracing
Perhatikan perbedaan:
- Query execution time
- Fetch / transfer time
Pola khas:
Execution: 40 ms
Fetch / Network: 7.8 s
Ini konfirmasi bahwa bottleneck ada di transfer data.
Audit Job Terjadwal
Cari proses yang:
- Aktif di menit yang sama
- Mengakses tabel besar
- Tidak melalui API utama
Mitigasi Cepat Saat Insiden
Jika insiden masih berlangsung:
- Terapkan hard limit pada hasil query
- Batasi ukuran response API
- Hentikan sementara job export
- Tambahkan rate limit pada endpoint sensitif
Tujuannya bukan sempurna, tapi menstabilkan sistem.
Pelajaran Arsitektural
Kasus ini mengajarkan hal penting:
- Monitoring tidak boleh hanya fokus ke CPU dan memory
- Ukuran data dan network I/O adalah first-class metric
- Query yang “cepat” tetap bisa melumpuhkan sistem
Kesimpulan
Ketika:
- API lambat
- CPU dan memory normal
- Tidak ada deployment
- Network I/O database melonjak
Maka pertanyaan utama bukan:
“Query apa yang lambat?”
Melainkan:
“Siapa yang menarik data terlalu banyak?”
Dan sering kali, jawabannya tersembunyi di balik query yang terlihat sangat biasa.
Kasus ini bukan tentang bug rumit — tapi tentang memahami bagaimana data bergerak di sistem terdistribusi.