Jam sebelas malam, ponsel di meja bergetar tanpa henti. Bukan notifikasi biasa—channel Slack pribadi yang biasanya cuma berisi alert cron mingguan tiba-tiba beruntun tiga kali dalam sepuluh menit: p95 response time nendang jauh di atas ambang normal. Aku buka dashboard setengah panik, dan grafik trafik yang biasanya landai seperti garis lurus malam hari itu naik curam seperti tebing. Ternyata seseorang di grup Telegram developer membagikan tangkapan layar status page publik tracker langgananku—yang ceritanya sudah pernah kutulis di sini—sebagai contoh "SaaS kecil yang jujur soal insidennya", dan thread itu ikut dibagikan ulang sampai masuk ke linimasa yang jauh lebih ramai dari perkiraanku.
Bukan Kode Baru yang Menyelamatkan, Tapi Keputusan Lama
Firasat pertamaku waktu grafik itu naik adalah: ini bakal jadi malam panjang menambal kode di tengah panik. Tapi begitu aku cek log lebih dekat, yang terjadi justru sebaliknya. Endpoint publik—halaman landing, halaman pricing, form signup—memang kepayahan, respons melambat tapi tetap hidup. Yang benar-benar kritis, jalur webhook payment, sama sekali tidak ikut goyah. Alasannya bukan keberuntungan: dua bulan sebelumnya, sesudah insiden webhook nge-charge dobel yang pernah kutulis di sini, aku sudah memindahkan seluruh pemrosesan webhook dari jalur request-response langsung ke antrian—payload masuk, langsung diberi ack, lalu diproses worker terpisah dengan batas konkurensi tetap. Keputusan itu awalnya cuma untuk mencegah race condition pada retry, bukan untuk menghadapi lonjakan trafik sebesar ini. Tapi efek sampingnya persis yang kubutuhkan malam itu: antrian punya backpressure alami, jadi berapa pun banyaknya event yang masuk, worker tetap memprosesnya dengan kecepatan yang sama, tidak pernah membanjiri database sekaligus.
Yang Nyaris Jebol: Connection Pool ke Database
Bagian yang benar-benar nyaris jadi masalah bukan di jalur webhook, melainkan di endpoint publik yang menampilkan status page dan halaman pricing—keduanya query langsung ke Postgres tanpa cache, karena selama ini trafiknya kecil dan aku malas menambah lapisan cache untuk sesuatu "yang jarang diakses". Malam itu jumlah koneksi simultan meledak lewat batas max_connections default yang tak pernah kunaikkan sejak awal proyek, dan beberapa request mulai gagal dengan error "too many clients already". Untungnya aku sudah lama memasang PgBouncer di depan database untuk keperluan lain, jadi solusinya bukan migrasi darurat, cuma menaikkan pool size PgBouncer dari terminal sambil menonton grafik pelan-pelan turun kembali ke level wajar. Butuh sekitar dua puluh menit sejak alert pertama sampai semuanya stabil—cukup lama untuk bikin jantung berdebar, tapi jauh dari skenario terburuk yang kubayangkan waktu pertama kali lihat grafiknya naik.
Kenapa Ini Terasa Seperti Kemenangan, Bukan Insiden
Yang membuat malam itu berbeda dari insiden-insiden sebelumnya adalah rasanya: bukan panik mencari akar masalah dari nol, tapi menyaksikan keputusan arsitektur yang kuambil demi alasan lain sama sekali—mencegah race condition—ternyata juga jadi jaring pengaman untuk skenario yang bahkan tidak pernah kubayangkan waktu itu. Aku juga baru sadar berapa banyak pengunjung baru yang mendaftar trial malam itu justru karena halaman pricing tetap bisa diakses meski lambat, bukan mati total. Kalau webhook payment ikut ambruk bersamaan dengan lonjakan trafik publik, cerita malam itu pasti berakhir sangat berbeda—kehilangan kepercayaan tepat di momen paling banyak orang baru pertama kali melihat produknya.
Yang Kuubah Setelah Malam Itu
Besoknya aku menaikkan max_connections secara permanen dan menambahkan cache singkat lima menit untuk halaman pricing dan status page, supaya lonjakan trafik publik berikutnya tidak langsung memukul database. Aku juga mulai menerapkan pola antrian-plus-backpressure yang sama untuk endpoint publik yang berat, bukan cuma untuk webhook—kalau pola ini bisa menyelamatkan satu jalur tanpa direncanakan, kemungkinan besar dia juga berguna di jalur lain yang belum pernah diuji dengan trafik nyata. Yang paling kuingat justru pelajaran yang agak berlawanan dengan insting: keputusan teknis yang baik sering kali baru kelihatan nilainya jauh setelah alasan awal yang membuatnya sudah terlupakan.
Takeaway
Keputusan arsitektur yang dibuat demi satu masalah spesifik—dalam kasusku, mencegah race condition pada retry webhook—kadang justru jadi penyelamat untuk skenario yang sama sekali berbeda dan tidak pernah direncanakan. Kalau kamu mengelola proyek sampingan sendirian, investasi kecil semacam antrian dengan backpressure atau connection pooling di depan database bukan overengineering untuk trafik yang "mungkin tidak akan pernah datang"—itu asuransi murah yang baru terasa nilainya persis di malam kamu paling tidak siap menghadapinya.
---
Sumber gambar: Christopher Gower di Unsplash Sumber gambar: Ilya Pavlov di Unsplash Sumber gambar: imgix di Unsplash
The conversation 0
No comments yet. Start the conversation.