Dari crash di 100 user ke 5000+ peserta tanpa downtime
Meningkatkan performa server dan aplikasi ujian online OlimpiadeKu yang tumbang setiap kali ujian dimulai serentak.
TL;DR
Server dan aplikasi ujian online OlimpiadeKu sering crash ketika ujian dimulai serentak, bahkan hanya dengan 100+ user. Saya memperbaiki akar masalahnya di sisi aplikasi dan server, sehingga spesifikasi server database bisa diturunkan dari 32 vCPU/256 GB menjadi 16 vCPU/48 GB (biaya hemat sekitar 40–50%). Kini platform melayani 5000+ peserta per event tanpa downtime.
Konteks & Masalah
OlimpiadeKu adalah platform ujian online skala nasional untuk pelajar di Indonesia, terdiri dari web (NextJS), mobile (React Native), dan backend API (Laravel, MySQL, NGINX). Aplikasi ini sudah berjalan di production dengan tim dan lead yang ada. Saya ditugaskan sebagai Lead Developer khusus untuk menyelesaikan masalah performa, dengan wewenang mengambil keputusan teknis dan mengonfigurasi langsung di sisi server maupun aplikasi.
Ujian dimulai serentak setiap hari Minggu jam 8 pagi. Di detik yang sama, peserta login, memulai ujian, lalu setiap klik jawaban langsung disimpan ke database. Pola traffic seperti ini membuat sistem tumbang:
- Server database kewalahan. Dengan 100+ user saja, CPU MySQL langsung mencapai 100%.
- PHP-FPM crash. Worker habis karena menunggu respons database, lalu NGINX mengembalikan error 502/504. PHP-FPM harus di-restart manual agar aplikasi bisa jalan lagi, tetapi masalah yang sama terus muncul.
- Upgrade server tidak menyelesaikan masalah. Sebelumnya masalah ini ditangani sebagai kekurangan resource, sehingga server terus di-upgrade hingga database mencapai 32 vCPU/256 GB RAM. Namun sistem tetap crash, karena akar masalahnya ada di aplikasi:
- Banyak query N+1, terutama saat mengambil soal beserta opsi jawabannya.
- Tabel transaksi yang paling sering diakses (seperti penyimpanan jawaban) belum memiliki index.
- Setiap jawaban peserta langsung ditulis ke database, sehingga jumlah request jauh lebih besar dari jumlah user.
Dampaknya ke bisnis cukup serius: ujian harus diundur, klien kecewa, dan peserta mengajukan komplain.
Pendekatan
- Mengukur sebelum mengubah. Saya mengaktifkan MySQL slow query log untuk menemukan query yang paling berat, dan memasang Prometheus + Grafana untuk memonitor resource server app dan database secara real time saat ujian berlangsung.
- Menghentikan pendekatan scale up. Berdasarkan data tersebut, saya memutuskan untuk berhenti menambah spesifikasi server dan fokus memperbaiki akar masalah di aplikasi lebih dulu.
- Optimasi database. Bersama tim dev, kami memperbaiki query N+1 dan menambahkan index pada tabel transaksi yang paling sering diakses, sehingga query yang sebelumnya membebani CPU MySQL menjadi jauh lebih ringan.
- Caching dengan Redis. Data yang sering dibaca, seperti users dan sessions, dipindahkan ke Redis agar tidak perlu query ke MySQL di setiap request.
- Mengubah alur ujian menjadi client-first (web dan mobile).
- Soal di-download ke local storage peserta sebelum ujian dimulai, sehingga soal tampil lebih cepat dan tidak ada lonjakan request soal ke server di jam 8.
- Jawaban disimpan dulu di sisi client, lalu dikirim ke server secara berkala dan saat submit. Write ke database per klik jawaban hilang sepenuhnya.
- Tuning server sesuai kapasitas. Konfigurasi PHP-FPM (jumlah worker dihitung dari RAM yang tersedia) dan MySQL (buffer pool, max connections) disesuaikan dengan resource server, sehingga PHP-FPM tidak lagi crash dan tidak perlu di-restart manual.
- Validasi langsung di ujian live. Perubahan diuji pada ujian berikutnya dan berhasil, server tetap stabil saat peak. Setelah itu saya terus mengoptimasi secara bertahap di sisi server maupun aplikasi, dipandu data dari Grafana.
- Menurunkan spesifikasi server. Setelah beban database terbukti jauh di bawah kapasitas, saya menggunakan data monitoring sebagai dasar untuk menurunkan server database ke 16 vCPU/48 GB RAM.
Hasil
| Metrik | Sebelum | Sesudah |
|---|---|---|
| Spesifikasi server database | 32 vCPU / 256 GB RAM | 16 vCPU / 48 GB RAM (CPU −50%, RAM −81%) |
| Biaya server database | Baseline | Hemat ~40–50% per bulan |
| Kapasitas | Crash di 100+ user | 5000+ peserta per event (~1.000–1.500 concurrent per sesi) |
| Stabilitas | PHP-FPM crash, harus restart manual, ujian diundur | Nol downtime |
| Komplain terkait server/aplikasi | Klien dan peserta komplain | Nol komplain |