Pagi itu saya baru bangun dan langsung ada pesan masuk dari rekan: “Bro, dashboard-nya error nih, gak bisa dibuka.”
Saya cek. Semua endpoint /api/* di kizuna.excellent.co.id membalas 502 Bad Gateway, bukan satu endpoint, bukan satu fitur, tapi semuanya. Backend mati total.
Yang bikin frustrasi, saya tidak melakukan deploy apapun malam sebelumnya. Tidak ada perubahan kode, tidak ada yang baru di-push. Server hanya tidur dan bangun dalam kondisi rusak.
Apa yang sebenarnya terjadi
Jawabannya ada di log systemd. Pukul 06:02 pagi, unattended-upgrades jalan otomatis seperti biasa. Ubuntu melakukan upgrade rutin pada mysql-server, mysql-client-8.0, dan libxml2. Proses normal, tidak ada yang aneh. Pukul 06:03, MySQL di-stop sementara untuk menyelesaikan proses upgrade, dan di sinilah masalahnya mulai.
Unit systemd kizuna-backend.service punya konfigurasi Requires=mysql.service. Di systemd, Requires artinya kalau dependency-nya berhenti, unit yang bergantung padanya juga ikut berhenti. Yang lebih berbahaya lagi, kalau MySQL nyala kembali setelah upgrade selesai, kizuna-backend tidak otomatis ikut nyala.
Jadi kronologinya persis seperti ini: pukul 06:02 MySQL upgrade dimulai, pukul 06:03 MySQL stop dan kizuna-backend ikut di-stop oleh systemd, pukul 06:04 MySQL selesai upgrade dan nyala kembali secara otomatis, lalu dari 06:04 sampai 08:59 kizuna-backend tetap mati sampai akhirnya di-start manual. Hampir 3 jam mati karena satu baris konfigurasi.
Kenapa Restart=always tidak menolong
Saya sudah pasang Restart=always di unit service itu, yang seharusnya membuatnya otomatis restart kalau mati. Ternyata tidak semudah itu. Restart=always hanya bereaksi pada crash atau exit yang tidak normal. Kalau systemd sendiri yang menghentikan service karena dependency-nya berhenti, itu dianggap sebagai “stop yang disengaja”, bukan crash, sehingga Restart=always tidak bereaksi sama sekali. Backend ke-drag mati bersama MySQL, lalu ditinggal begitu saja.
Tiga lapis perbaikan
Satu insiden ini menghasilkan tiga perubahan sekaligus, karena mencabut akar masalah saja tidak cukup. Selalu ada kemungkinan kondisi lain yang belum diprediksi.
Lapis pertama: ganti Requires jadi Wants. Wants berbeda dari Requires. Kalau MySQL berhenti saat upgrade, kizuna-backend tidak ikut di-stop. Backend mencoba reconnect ke MySQL sendiri sampai berhasil. Ini adalah akar masalah yang dicabut langsung.
# sebelum
Requires=mysql.service
# sesudah
Wants=mysql.service
After=mysql.service
Lapis kedua: StartLimitIntervalSec=0. Kalau karena satu dan lain hal backend sempat crash saat MySQL sebentar down, systemd defaultnya akan menyerah setelah 5 kali restart dalam 10 detik. Dengan StartLimitIntervalSec=0, ia retry tiap 5 detik tanpa menyerah.
StartLimitIntervalSec=0
RestartSec=5
Lapis ketiga: watchdog timer. Ini jaring pengaman terakhir. Satu timer yang berjalan tiap 2 menit dan menjalankan systemctl start kizuna-backend. Perintah start di systemd bersifat idempotent, jadi kalau backend sudah jalan tidak ada efeknya. Skenario terburuk, backend mati karena alasan apapun yang tidak diprediksi oleh lapis pertama dan kedua, watchdog akan menyalakannya kembali maksimal 2 menit kemudian.
[Timer]
OnBootSec=2min
OnUnitActiveSec=2min
[Install]
WantedBy=timers.target
Yang bisa kalian cek sekarang
Kalau kalian punya service production yang bergantung ke database dan pakai Requires, periksa ulang konfigurasi itu sekarang. Gunakan Wants kecuali kalian memang ingin service ikut mati ketika database-nya berhenti. Tambahkan juga StartLimitIntervalSec=0 supaya retry tidak punya batas, dan pertimbangkan watchdog timer sebagai jaring pengaman terakhir.
Kalau insiden serupa terjadi lagi, recovery manualnya cukup tiga perintah ini.
systemctl start kizuna-backend
systemctl status kizuna-backend --no-pager
curl -s -o /dev/null -w "%{http_code}n" http://127.0.0.1:3001/api/health
Output terakhir harus 200. Kalau sudah, dashboard kembali normal.
Kizuna sekarang sudah berjalan beberapa minggu tanpa insiden sejenis, bukan karena MySQL tidak pernah di-update lagi, tapi karena update MySQL memang bukan urusan backend service lagi.