Pengantar: Kenapa Deadlock Redis pada Job Queue Terjadi?

Ketika job queue backend berhenti memproses tugas, tim operasional sering melihat anomali berupa antrean panjang dan worker yang tetap idle padahal cukup kapasitas. Inilah problem utama: deadlock Redis antara worker yang menunggu lock dan job queue yang terus memicu retry, sehingga seluruh sistem stagnan. Artikel ini menjelaskan bagaimana mengidentifikasi gejala awal dari monitoring/log, langkah observasi untuk menemukan deadlock Redis, analisis penyebab utama, dan perbaikan praktis agar kasus serupa dapat ditangani sendiri.

Observasi dari Monitoring dan Log

Gejala awal berawal dari metrik latency job queue dan penumpukan job yang gagal. Monitoring seperti Prometheus/Grafana biasanya menunjukkan:

  • Worker idle, but job queue depth naik tajam.
  • Retry rate meningkat secara eksponensial setelah job pertama gagal.
  • Redis command latency spike, khususnya BLPOP atau BRPOP untuk antrean.

Log worker memperlihatkan pesan error tipe timeout atau exception pada update status job, tanpa ada log keberhasilan berikutnya. Untuk memahami apa yang pasti terjadi pada Redis, gunakan redis-cli monitor untuk melihat operasi real-time.

redis-cli monitor

Perhatikan apakah ada pola:

  • Worker menulis lock dengan SET key value NX PX timeout.
  • Job queue melakukan BRPOP namun tidak selesai, menunggu lock yang tidak dilepas.
  • Terdapat retry job dengan perintah RPUSH sebelum lock selesai.

Alternatif lain adalah redis-cli CLIENT LIST diikuti INFO clients untuk melihat apakah ada koneksi worker yang menunggu dan apakah ada script lama (Lua) yang menggantung.

Analisis Deadlock antara Worker dan Redis Job Queue

Deadlock muncul bila worker memperoleh lock di Redis untuk eksekusi job, tapi gagal melepaskannya karena proses crash atau blocking saat menulis hasil. Konfigurasi retry otomatis kemudian memasukkan job kembali ke queue, sementara worker lain tidak bisa mengambil job karena key lock tadi masih aktif. Gejala umum:

  • Lock TTL terlalu panjang sehingga tidak otomatis hilang saat worker hang.
  • Worker menulis status dengan operasi blocking, seperti menunggu downstream HTTP, tanpa timeout.
  • Retry queue tidak memeriksa apakah job sebelumnya sudah selesai, sehingga job yang sama dijalankan berkali-kali.

Root cause case ini: worker menulis lock Redis dengan TTL default tinggi untuk menjamin eksklusif, tetapi tidak memiliki circuit breaker saat dependent service lambat. Setelah timeout downstream terpenuhi, worker mencoba menyimpan hasil ke Redis lagi, namun perintah SET untuk melepaskan lock gagal karena lock sebelumnya belum terupdate, sehingga job stuck.

Langkah Perbaikan Praktis

1. Konfigurasi Timeout

Setiap bagian yang mengakses Redis harus memiliki timeout. Misalnya, gunakan client library dengan timeout read/write explicit dan timeouts untuk HTTP dependency agar worker tidak tergantung selamanya. Dalam Redis lock pattern, TTL lock harus lebih pendek dari waktu maksimum yang dibutuhkan job, lalu diperbarui (renew) secara periodik hanya jika job masih berjalan.

2. Circuit Breaker dan Retry Idempotent

Implementasikan circuit breaker di service dependency. Apabila downstream tidak responsif, worker harus membatalkan job, mencatat error, dan beralih ke retry yang tertunda. Pastikan job idempotent, misalnya dengan menyimpan job_id unik di Redis sehingga re-try tidak menyebabkan state tidak konsisten.

3. Observasi Lock dan Queue

Gunakan perintah berikut untuk memeriksa lock yang tersisa dan job queue depth:

redis-cli GET job:lock:worker123

Jika lock masih ada padahal worker sudah restart, hapus manual lock tersebut atau gunakan TTL pendek. Untuk antrean:

redis-cli LLEN job:queue

kemudian LRANGE job:queue 0 20 untuk melihat job mana yang stuck.

4. Penyesuaian Job Worker

Tambahkan log yang merekam aktivitas lock (mendapatkan, memperbarui, melepaskan) termasuk waktu. Gunakan alarm jika lock tidak dilepas dalam periode tertentu.

Kesimpulan

Deadlock Redis di job queue sering muncul akibat kombinasi timeout lock panjang, retry agresif, dan worker yang tidak tahan terhadap dependensi lambat. Diagnosis dimulai dari monitoring metrik queue, log worker, lalu verifikasi operasi Redis dengan perintah monitor atau CLIENT LIST. Perbaikan praktis meliputi konfigurasi timeout untuk lock dan dependency, penerapan circuit breaker dengan retry terkontrol, serta memastikan job idempotent. Dengan pendekatan ini, tim backend bisa menerapkan diagnosis serupa sebelum sistem kehilangan throughput secara signifikan.