Pengguna biasanya tidak peduli bagaimana server bekerja. Mereka hanya ingin tombol yang ditekan segera memberi respons, halaman tidak menggantung, dan hasil akhirnya tetap muncul tanpa harus mengulang proses dari awal. Masalahnya, banyak aplikasi web masih memaksa satu request menangani semuanya sekaligus: menyimpan data, mengirim email, membuat PDF, memproses gambar, lalu mencatat laporan.
Di sinilah queue berguna. Queue adalah antrean pekerjaan yang bisa diproses oleh worker di luar alur request utama. Dengan pola ini, aplikasi cukup menerima permintaan pengguna, menyimpan pekerjaan yang perlu dilakukan, lalu mengembalikan respons lebih cepat. Pekerjaan berat diproses beberapa saat kemudian oleh proses khusus.
Masalah dengan pekerjaan yang terlalu banyak dalam satu request
Bayangkan pengguna mengunggah bukti pembayaran. Setelah file diterima, server mungkin perlu melakukan beberapa hal:
- menyimpan file ke storage;
- memeriksa ukuran dan format;
- mengubah ukuran gambar;
- mengirim notifikasi email;
- memperbarui status pesanan;
- mencatat aktivitas untuk kebutuhan audit.
Jika semua dilakukan sebelum server mengirim respons, pengguna harus menunggu seluruh rangkaian selesai. Ketika salah satu proses lambat, request ikut tertahan. Bahkan jika pengiriman email gagal, aplikasi bisa terlihat seolah-olah seluruh transaksi gagal, padahal data pesanan sebenarnya sudah tersimpan.
Masalah ini bukan hanya soal kecepatan. Request yang terlalu lama dapat menghabiskan koneksi, memicu timeout dari web server atau proxy, dan membuat lonjakan trafik terasa lebih berat daripada seharusnya.
Memahami queue dengan analogi sederhana
Queue dapat dibayangkan seperti nomor antrean di loket. Aplikasi web bertugas menerima permintaan dan memberikan nomor antrean. Worker bertugas memanggil nomor tersebut dan mengerjakan tugasnya satu per satu.
Dalam istilah teknis, sebuah job adalah unit pekerjaan yang ingin diproses. Contohnya adalah SendWelcomeEmail, GenerateMonthlyReport, atau ResizeUploadedImage. Job disimpan di queue, kemudian worker mengambilnya untuk dieksekusi.
Media penyimpanan queue bisa berupa tabel MySQL, Redis, atau layanan antrean khusus. Untuk aplikasi kecil, tabel database mungkin sudah cukup. Untuk volume pekerjaan yang lebih tinggi, Redis biasanya lebih sesuai karena dirancang untuk operasi cepat di memori.
Alur kerja yang umum
Pola dasarnya dapat diringkas menjadi empat langkah:
- Pengguna melakukan aksi, misalnya mengirim formulir pendaftaran.
- Aplikasi menyimpan data utama dan membuat job baru.
- Server segera mengirim respons bahwa permintaan telah diterima.
- Worker mengambil job dan memproses pekerjaan di latar belakang.
Contoh sederhananya, proses pendaftaran akun tidak perlu menunggu email selamat datang selesai dikirim. Aplikasi dapat menyimpan akun lebih dulu, menambahkan job pengiriman email, lalu menampilkan pesan bahwa akun berhasil dibuat.
user = createUser(formData)
queue.push({
type: "send_welcome_email",
userId: user.id
})
return response("Akun berhasil dibuat")Contoh tersebut hanya ilustrasi konsep. Dalam aplikasi nyata, job sebaiknya membawa data minimum yang diperlukan, seperti ID pengguna, bukan seluruh objek pengguna. Worker dapat mengambil data terbaru dari database ketika job mulai diproses.
Queue bukan berarti semua pekerjaan harus dibuat asinkron
Queue cocok untuk pekerjaan yang tidak perlu selesai sebelum pengguna melanjutkan. Pengiriman email, pembuatan thumbnail, ekspor data, sinkronisasi dengan layanan eksternal, dan pembuatan laporan adalah contoh yang umum.
Sebaliknya, jangan memindahkan semua hal ke queue. Validasi formulir, pemeriksaan hak akses, dan penyimpanan data inti biasanya harus selesai sebelum respons diberikan. Pengguna perlu mendapat kepastian apakah transaksi utama berhasil atau gagal.
Patokan sederhananya: jika pekerjaan tersebut menentukan boleh tidaknya transaksi utama dianggap berhasil, kerjakan dalam request. Jika pekerjaan hanya tindak lanjut, bisa dipertimbangkan untuk dimasukkan ke queue.
Bagian yang sering dilupakan: retry dan idempotensi
Worker dapat gagal. Koneksi ke layanan email mungkin terputus, API pihak ketiga bisa mengembalikan error, atau server tujuan sedang sibuk. Karena itu, sistem queue perlu mendukung retry, yaitu mencoba kembali job setelah kegagalan.
Namun retry bisa berbahaya jika job tidak dirancang dengan aman. Misalnya, job pengisian saldo gagal setelah berhasil mengirim permintaan ke bank, tetapi sebelum menerima respons. Ketika diulang, transaksi bisa terkirim dua kali.
Solusinya adalah membuat operasi bersifat idempotent. Artinya, menjalankan job yang sama beberapa kali tetap menghasilkan dampak akhir yang sama. Salah satu cara umum adalah menggunakan idempotency key atau ID transaksi unik yang diperiksa sebelum operasi dijalankan.
Untuk email, duplikasi mungkin hanya mengganggu. Untuk pembayaran, pengurangan stok, atau pembuatan pesanan, duplikasi dapat menjadi masalah serius.
Dead-letter queue dan pencatatan kegagalan
Tidak semua job akan berhasil setelah beberapa kali retry. Job yang terus gagal sebaiknya dipindahkan ke dead-letter queue, yaitu tempat khusus untuk pekerjaan yang memerlukan pemeriksaan manual atau penanganan terpisah.
Jangan hanya mencatat pesan “job gagal”. Simpan informasi yang membantu diagnosis, seperti jenis job, ID data terkait, jumlah percobaan, waktu kegagalan, dan pesan error. Hindari memasukkan password, token, atau data pribadi sensitif ke dalam log.
Dashboard sederhana untuk melihat job yang gagal sering kali lebih berguna daripada menunggu pengguna melaporkan bahwa email atau laporan mereka tidak pernah diterima.
Apa artinya bagi kita?
Queue bukan sekadar teknik untuk membuat aplikasi terlihat cepat. Queue membantu memisahkan dua jenis pekerjaan: respons langsung yang dibutuhkan pengguna dan pekerjaan lanjutan yang dapat diproses kemudian.
Pemisahan ini membuat arsitektur lebih mudah dikembangkan, tetapi juga menambah tanggung jawab. Tim perlu memantau worker, mengatur retry, mencegah duplikasi, dan memastikan data tetap konsisten. Sistem asinkron yang tidak dipantau hanya memindahkan masalah dari layar pengguna ke tempat yang lebih sulit terlihat.
Yang bisa dilakukan sekarang
- Catat endpoint yang paling sering lambat dan identifikasi pekerjaan non-esensial di dalamnya.
- Pilih satu pekerjaan sederhana, seperti pengiriman email, sebagai kandidat pertama untuk dipindahkan ke queue.
- Tambahkan status job: menunggu, diproses, berhasil, atau gagal.
- Uji skenario retry dan pastikan job tidak membuat data ganda.
- Buat log yang cukup untuk menemukan penyebab kegagalan tanpa membocorkan informasi sensitif.
- Pasang pemantauan untuk worker dan jumlah job yang menumpuk.
Mulailah dari satu alur kecil. Queue akan memberi manfaat ketika digunakan untuk masalah yang jelas, bukan ketika ditambahkan hanya karena terdengar lebih modern. Dengan batas yang tepat, aplikasi dapat merespons lebih cepat tanpa mengorbankan keandalan proses di belakang layar.
– Rio Yotto @rioyotto
