Job queue Laravel yang berhenti karena lock Redis tidak dilepas sering terlihat sebagai latensi tinggi, retry berulang tanpa penyelesaian, dan bergantung pada layanan eksternal yang sensitif terhadap delay. Langsung saja, jika queue Horizon Anda macet lebih lama dari biasanya dan job terus retry tanpa menyelesaikan, kemungkinan besar ada lock yang tidak pernah dilepas.
1. Latar Sistem dan Gejala Nyata
Tim backend kami menjalankan beberapa job queue Laravel yang mengirim notifikasi dan menulis hasil ke data warehouse. Semua queue menggunakan driver Redis dengan Horizon monitoring. Gejala yang muncul:
- Queue tertentu tiba-tiba memiliki latensi 3–5 kali lipat.
- Job yang sama retry tanpa pernah masuk status succeeded.
- Pemicu eksternal (API pihak ketiga) tetap responsif, tapi ada ketergantungan pada job yang gagal menulis hasil karena timeout lock.
Permasalahan tampak di Horizon dashboard sebagai status pending lama dengan worker keep-alive yang terus disetujui.
2. Metode Investigasi
2.1 Analisis log Horizon
Buka storage/logs/laravel.log atau log custom Horizon. Cari pola seperti Preparing lock for job dan Lock timeout. Contoh potongan log:
[2024-09-29 10:12:44] horizon.INFO: Attempting job: App\Jobs\SyncReport {"attempts":1}
[2024-09-29 10:12:44] horizon.INFO: Acquiring lock: sync-report-123
[2024-09-29 10:13:14] horizon.ERROR: Job timed out while waiting for lock release
Ini menunjukkan job memperoleh lock tapi tidak menyelesaikan pekerjaan dalam TTL standar.
2.2 Redis MONITOR dan pola locking
Gunakan Redis CLI untuk observasi operasi lock.
redis-cli MONITOR | grep -E "SET .*sync-report"
Perhatikan TTL yang diterapkan (misalnya menggunakan SET key value NX PX 60000). Jika TTL terlalu lama atau job crash sebelum DEL, kunci tetap tertinggal.
2.3 Multipel job storm
Dalam kasus kami, ada spike saat data besar diproses: ratusan job menciptakan lock serentak. Beberapa job menunggu lock yang tidak pernah dilepas karena job pertama mengalami deadlock internal atau timeout eksternal.
3. Root Cause
Setelah melihat log dan monitoring Redis, akar masalahnya adalah:
- TTL lock terlalu pendek (10 detik) terutama untuk job yang memanggil API eksteral atau proses I/O berat.
- Job tidak membersihkan lock ketika terjadi
TimeoutExceptiondari dependency eksternal, sehingga lock tetap tersimpan. - Spike job membuat banyak worker menunggu lock yang akhirnya membuat job retry terus-menerus.
Kesimpulan: konfigurasi lock dan handling error di job perlu diperbaiki agar lock tidak menjadi bottleneck.
4. Solusi Konkret
4.1 Sesuaikan Konfigurasi Lock Redis
Perpanjang TTL lock sesuai estimasi waktu eksekusi job. Jika job biasanya 45 detik, setel minimal 60 detik.
// config/horizon.php
'waits' => [
'redis:default' => 60,
],
// Di dalam job jika manual lock menggunakan Cache::lock
$lock = Cache::lock('sync-report-'. $id, 90);
if ($lock->get()) {
try {
// pekerjaan utama
} finally {
$lock->release();
}
}
Gunakan Cache::lock agar Laravel otomatis menyimpan referensi lock, memudahkan release.
4.2 Tambahkan Fallback Timeout dan Penanganan Eksepsi
Job harus menangkap eksepsi dependency eksternal lalu melepas lock sebelum queue kembali.
try {
$apiResponse = $this->externalService->fetch();
// proses data
} catch (RequestException $e) {
Log::error('Eksternal API timeout: ' . $e->getMessage());
throw $e; // agar job retry, tapi lock sudah dilepas di finally
} finally {
optional($lock)->release();
}
Gunakan finally untuk memastikan lock dilepas walau terjadi error.
4.3 Batasi Job Storm dengan Batching dan Queue Prioritas
Bagi job menjadi batch kecil dan gunakan queue berbeda: satu untuk job berat dengan concurrency rendah, satu lagi untuk job ringan. Ini mencegah semua worker menunggu lock bersamaan.
5. Checklist Verifikasi Pasca-Fix
- Apakah TTL lock di Horizon dan job manual disesuaikan dengan durasi eksekusi aktual?
- Apakah setiap job yang menggunakan lock memiliki
finallyuntuk release, termasuk saat retry? - Apakah log Horizon menunjukkan job menyelesaikan tanpa
Lock timeout? - Apakah Redis
MONITORmenunjukkanDELlock setelah job selesai? - Apakah spike job berhasil dibagi ke queue berbeda agar tidak saling menunggu lock?
Jalankan php artisan horizon:terminate dan php artisan horizon ulang setelah konfigurasi disesuaikan untuk memastikan worker menerima pembaruan.
Kesimpulan
Debugging job queue Laravel yang tergantung karena Redis lock menuntut pemahaman log Horizon, observasi Redis, serta handling job yang lengkap. Dengan menyesuaikan TTL lock, memastikan release lewat finally, serta memecah job spike, sistem kembali stabil. Gunakan checklist verifikasi agar perbaikan tidak hanya berjalan sesaat tetapi tahan terhadap beban produksi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!