Laravel Queue Failed Jobs: Cara Retry dan Menangani Job yang Gagal

Laravel Queue memungkinkan aplikasi menjalankan pekerjaan di background tanpa membuat pengguna harus menunggu proses selesai. Contohnya adalah mengirim email, membuat laporan, memproses gambar, mengirim notifikasi, atau melakukan sinkronisasi data dengan API eksternal.

Namun, tidak semua Job selalu berhasil.

Sebuah Job dapat gagal karena API sedang down, koneksi database bermasalah, Redis tidak dapat diakses, data yang dibutuhkan tidak ditemukan, terjadi exception di dalam kode, atau waktu eksekusi Job melebihi batas yang ditentukan.

Laravel menyediakan fitur Failed Jobs untuk menyimpan informasi tentang Job yang gagal sehingga developer dapat mengetahui penyebabnya dan menjalankan Job tersebut kembali.

Pada artikel ini kita akan membahas:

  • Apa itu Laravel Failed Jobs
  • Mengapa Job bisa masuk ke failed_jobs
  • Cara melihat Failed Jobs
  • Cara membaca error Job
  • Cara retry satu Job
  • Cara retry semua Job
  • Cara menghapus Failed Job
  • Mengatur jumlah percobaan dengan tries
  • Mengatur jeda retry dengan backoff
  • Menggunakan exponential backoff
  • Menggunakan retryUntil
  • Menangani kegagalan dengan method failed()
  • Perbedaan retry Job dan menjalankan Job baru
  • Cara menangani Failed Jobs di production
  • Troubleshooting Job yang terus gagal setelah di-retry

Apa Itu Laravel Failed Jobs?

Failed Job adalah Job yang sudah dicoba diproses oleh Laravel tetapi akhirnya dianggap gagal setelah mencapai batas percobaan atau mengalami kondisi yang menyebabkan Job gagal.

Laravel dapat menyimpan informasi Failed Job ke dalam penyimpanan failed-job yang dikonfigurasi. Pada konfigurasi database, informasi tersebut disimpan di tabel:

failed_jobs

Tabel ini membantu developer melakukan troubleshooting tanpa harus kehilangan informasi mengenai Job yang gagal.

Laravel menyediakan command Artisan untuk melihat Failed Jobs:

php artisan queue:failed

Command tersebut menampilkan informasi seperti ID Job, connection, queue, dan waktu ketika Job gagal.


Mengapa Laravel Queue Job Bisa Gagal?

Sebelum melakukan retry, hal terpenting adalah memahami mengapa Job gagal.

Beberapa penyebab yang umum antara lain:

1. Exception di dalam Job

Contohnya:

public function handle(): void
{
    $user = User::findOrFail($this->userId);

    // ...
}

Jika user tidak ditemukan, Laravel dapat melempar exception dan Job gagal.

2. API eksternal tidak tersedia

Misalnya Job melakukan request ke:

https://api.example.com

Jika API sedang down atau timeout, Job dapat gagal.

3. Database bermasalah

Contohnya:

SQLSTATE[HY000]
Connection refused
Too many connections

4. Redis bermasalah

Jika Redis digunakan sebagai queue backend, koneksi Redis yang gagal dapat menyebabkan masalah pada proses Queue.

5. Timeout

Job yang membutuhkan waktu terlalu lama dapat melewati batas timeout worker.

6. Jumlah percobaan sudah habis

Sebuah Job dapat dicoba beberapa kali. Jika seluruh percobaan gagal, Laravel akan menganggap Job tersebut gagal.

Laravel memungkinkan jumlah percobaan ditentukan melalui --tries pada worker atau konfigurasi pada Job itu sendiri.


Cara Melihat Laravel Failed Jobs

Untuk melihat daftar Failed Jobs, jalankan:

php artisan queue:failed

Contoh output dapat terlihat seperti:

+--------------------------------------+------------+---------+---------------------+
| ID                                   | Connection | Queue   | Failed At           |
+--------------------------------------+------------+---------+---------------------+
| 8d8c...                              | redis      | default | 2026-08-16 10:20:12 |
| 92ab...                              | redis      | emails  | 2026-08-16 10:21:45 |
+--------------------------------------+------------+---------+---------------------+

ID tersebut penting karena dapat digunakan untuk melakukan retry terhadap Job tertentu.

Laravel mendokumentasikan bahwa queue:failed menampilkan ID yang kemudian dapat digunakan sebagai argument untuk queue:retry.


Cara Mengetahui Penyebab Failed Job

Melihat daftar Failed Jobs saja belum cukup.

Anda perlu mengetahui exception atau error yang menyebabkan Job gagal.

Salah satu tempat pertama yang perlu diperiksa adalah log Laravel:

tail -f storage/logs/laravel.log

Kemudian perhatikan error yang muncul ketika Job dijalankan.

Contohnya:

SQLSTATE[HY000] [2002] Connection refused

atau:

RedisException: Connection refused

atau:

Allowed memory size exhausted

atau:

Maximum execution time exceeded

Jangan langsung melakukan retry berkali-kali sebelum memahami error tersebut.

Jika penyebabnya belum diperbaiki, retry hanya akan membuat Job gagal lagi.


Cara Retry Failed Job di Laravel

Setelah penyebab error diperbaiki, Anda dapat menjalankan kembali Failed Job.

Misalnya hasil:

php artisan queue:failed

menampilkan ID:

8d8c1234-xxxx-xxxx-xxxx-123456789abc

Jalankan:

php artisan queue:retry 8d8c1234-xxxx-xxxx-xxxx-123456789abc

Laravel akan memasukkan kembali Job tersebut ke queue untuk diproses oleh worker.

Setelah itu pastikan worker sedang berjalan:

php artisan queue:work

Jika menggunakan Redis:

php artisan queue:work redis

Retry Beberapa Failed Job Sekaligus

Anda juga dapat memberikan beberapa ID sekaligus.

Contoh:

php artisan queue:retry ID_1 ID_2 ID_3

Misalnya:

php artisan queue:retry \
8d8c1234-xxxx-xxxx-xxxx-123456789abc \
92ab1234-xxxx-xxxx-xxxx-987654321abc

Cara ini berguna ketika Anda sudah memperbaiki satu masalah yang menyebabkan beberapa Job gagal.


Cara Retry Semua Failed Jobs

Jika Anda yakin seluruh Failed Jobs aman untuk dijalankan kembali, gunakan:

php artisan queue:retry all

Laravel menyediakan all untuk me-retry seluruh Failed Jobs yang tersimpan.

Namun, jangan sembarangan menjalankan command ini di production.

Bayangkan ada 10.000 Job yang gagal karena API eksternal mengalami gangguan.

Jika API tersebut belum pulih kemudian Anda menjalankan:

php artisan queue:retry all

semua Job dapat kembali diproses dan gagal lagi.

Bahkan jika API sudah pulih, retry massal dapat menyebabkan lonjakan request.

Karena itu, lebih baik:

  1. identifikasi penyebab,
  2. perbaiki masalah,
  3. pastikan service tujuan sudah normal,
  4. retry Job yang relevan,
  5. monitor hasilnya.

Retry Failed Jobs pada Queue Tertentu

Jika aplikasi memiliki beberapa queue, misalnya:

high
default
emails
reports

Anda dapat melakukan retry berdasarkan queue tertentu.

Contoh:

php artisan queue:retry --queue=emails

Ini berguna ketika Anda hanya ingin mengembalikan Failed Jobs yang berasal dari queue tertentu. Laravel menyediakan opsi --queue pada queue:retry.


Menghapus Failed Job

Tidak semua Failed Job harus di-retry.

Misalnya Job sudah tidak relevan karena:

  • data sudah dihapus,
  • user sudah membatalkan transaksi,
  • proses sudah dilakukan secara manual,
  • atau Job tersebut berasal dari testing.

Untuk menghapus satu Failed Job:

php artisan queue:forget JOB_ID

Contoh:

php artisan queue:forget 8d8c1234-xxxx-xxxx-xxxx-123456789abc

Laravel juga menyediakan:

php artisan queue:flush

untuk menghapus seluruh Failed Jobs.

Hati-hati dengan queue:flush

Command:

php artisan queue:flush

menghapus seluruh record Failed Jobs.

Jangan menjalankannya hanya karena tabel failed_jobs terlihat banyak.

Record tersebut bisa sangat berguna untuk debugging dan audit masalah Queue.

Laravel juga menyediakan opsi --hours untuk membatasi record yang dihapus berdasarkan usia kegagalannya.

Contoh:

php artisan queue:flush --hours=48

Mengatur Jumlah Retry dengan --tries

Secara default, jumlah percobaan Job dapat dikontrol dari worker.

Contoh:

php artisan queue:work redis --tries=3

Artinya worker mengizinkan Job dicoba sampai batas yang ditentukan tersebut sebelum dianggap gagal.

Laravel juga memungkinkan jumlah percobaan ditentukan langsung pada Job. Jika Job memiliki konfigurasi attempts sendiri, konfigurasi tersebut dapat mengambil precedence dibanding nilai --tries dari worker.


Mengatur tries pada Job

Contoh Job:

<?php

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class SendInvoiceEmail implements ShouldQueue
{
    use Queueable;

    public $tries = 3;

    public function handle(): void
    {
        // Proses pengiriman email
    }
}

Dengan konfigurasi tersebut, Job mempunyai batas percobaan sendiri.

Pendekatan ini berguna ketika setiap jenis Job membutuhkan strategi retry yang berbeda.

Misalnya:

SendEmailJob       → 3 attempts
SyncPaymentJob     → 5 attempts
GenerateReportJob  → 2 attempts

Dengan begitu Anda tidak perlu memberikan konfigurasi retry yang sama kepada semua Job.


Menggunakan retryUntil

Selain jumlah percobaan, Laravel memungkinkan Anda menentukan batas waktu sampai kapan Job boleh dicoba kembali.

Contoh:

use DateTime;

public function retryUntil(): DateTime
{
    return now()->plus(minutes: 30);
}

Artinya Job dapat terus dicoba dalam jangka waktu tersebut, sesuai mekanisme retry yang digunakan.

Pendekatan ini berguna ketika Anda lebih peduli pada batas waktu daripada jumlah percobaan.

Misalnya Anda memiliki Job untuk melakukan sinkronisasi pembayaran.

Daripada mengatakan:

coba maksimal 5 kali

Anda dapat menggunakan pendekatan:

coba selama maksimal 30 menit

Laravel memberikan precedence kepada retryUntil() jika retryUntil dan tries sama-sama didefinisikan.


Mengatur Jeda Retry dengan backoff

Tidak semua error sebaiknya langsung dicoba kembali.

Misalnya API eksternal sedang mengalami gangguan.

Jika Job langsung retry tanpa jeda:

Request gagal
↓
Retry
↓
Gagal
↓
Retry
↓
Gagal

aplikasi justru dapat memberikan beban tambahan pada service yang sedang bermasalah.

Laravel menyediakan backoff untuk menentukan waktu tunggu sebelum retry.

Contoh pada worker:

php artisan queue:work redis --tries=3 --backoff=5

Artinya worker menunggu sebelum mencoba kembali Job yang mengalami exception. Laravel mendukung pengaturan backoff pada worker maupun Job.


Mengatur backoff pada Job

Anda juga dapat menentukan backoff langsung pada Job.

Contoh:

public $backoff = 5;

Full example:

<?php

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class SendInvoiceEmail implements ShouldQueue
{
    use Queueable;

    public $tries = 3;

    public $backoff = 5;

    public function handle(): void
    {
        // Kirim email
    }
}

Sekarang Job memiliki:

Maximum attempts: 3
Retry delay:      5 seconds

Exponential Backoff pada Laravel Queue

Untuk API eksternal atau service yang kemungkinan sedang overload, exponential backoff sering lebih masuk akal daripada retry dengan interval tetap.

Contohnya:

public function backoff(): array
{
    return [1, 5, 10];
}

Dengan konfigurasi tersebut, retry dapat menggunakan pola:

Attempt 1
   ↓
1 detik
   ↓
Attempt 2
   ↓
5 detik
   ↓
Attempt 3
   ↓
10 detik

Laravel mendukung array pada method backoff() untuk menentukan beberapa nilai delay retry. Jika jumlah percobaan lebih banyak daripada jumlah nilai dalam array, nilai terakhir digunakan untuk percobaan berikutnya.

Contoh:

public function backoff(): array
{
    return [1, 5, 10];
}

Ini lebih baik daripada retry tanpa jeda untuk banyak jenis kegagalan sementara.


Contoh Job dengan Retry yang Lebih Lengkap

Berikut contoh sederhana Job yang mempunyai batas retry, backoff, dan handler ketika seluruh percobaan gagal:

<?php

namespace App\Jobs;

use Throwable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class SyncOrderToApi implements ShouldQueue
{
    use Queueable;

    public $tries = 3;

    public function backoff(): array
    {
        return [5, 15, 30];
    }

    public function handle(): void
    {
        // Kirim data order ke API eksternal
    }

    public function failed(?Throwable $exception): void
    {
        // Jalankan ketika Job akhirnya gagal
    }
}

Strateginya menjadi:

Attempt 1
   ↓ gagal
tunggu 5 detik
   ↓
Attempt 2
   ↓ gagal
tunggu 15 detik
   ↓
Attempt 3
   ↓ gagal
failed()

Menangani Job yang Benar-Benar Gagal dengan failed()

Laravel menyediakan method failed() pada Job untuk menangani kondisi ketika Job akhirnya gagal.

Contoh:

public function failed(?Throwable $exception): void
{
    Log::error('Sync order gagal', [
        'order_id' => $this->orderId,
        'error' => $exception?->getMessage(),
    ]);
}

Method ini dapat digunakan untuk:

  • mencatat error,
  • mengirim notifikasi,
  • mengubah status database,
  • memberi tahu administrator,
  • atau melakukan cleanup tertentu.

Contoh yang lebih realistis:

public function failed(?Throwable $exception): void
{
    Order::whereKey($this->orderId)->update([
        'sync_status' => 'failed',
    ]);

    Log::error('Order gagal disinkronisasi', [
        'order_id' => $this->orderId,
        'error' => $exception?->getMessage(),
    ]);
}

Dengan begitu aplikasi tidak hanya mengetahui bahwa Queue gagal, tetapi juga dapat memperbarui status bisnis yang berkaitan dengan Job.

Laravel mencatat bahwa instance Job baru dibuat sebelum method failed() dipanggil. Karena itu, perubahan property yang hanya terjadi selama handle() tidak boleh diasumsikan masih tersedia di failed(). Exception terakhir dapat diterima melalui parameter $exception.


Jangan Retry Semua Jenis Error

Ini bagian yang sering terlewat ketika membangun sistem Queue.

Tidak semua error cocok untuk retry.

Misalnya API sedang timeout:

Connection timeout

Retry mungkin masuk akal.

Tetapi jika errornya:

Invalid API key

retry berkali-kali tidak akan memperbaiki masalah.

Begitu juga:

User tidak ditemukan

atau:

Data sudah dihapus

Retry kemungkinan hanya akan menghasilkan error yang sama.

Secara sederhana, bedakan error menjadi:

Jenis errorRetry?
API timeoutYa
Redis sementara tidak tersediaYa
Database connection timeoutYa
HTTP 429 Too Many RequestsYa, dengan backoff
HTTP 500 dari API eksternalBiasanya ya
API key salahTidak sebelum konfigurasi diperbaiki
Data tidak ditemukanBiasanya tidak
Validation error permanenTidak
Bug pada kodeTidak sebelum bug diperbaiki

Strategi retry sebaiknya mengikuti karakter error, bukan sekadar menetapkan tries=10.


Retry vs Release: Apa Bedanya?

Laravel juga memungkinkan Job dilepas kembali ke queue menggunakan:

$this->release();

Anda juga dapat menentukan delay:

$this->release(10);

Artinya Job dikembalikan ke queue dan baru tersedia untuk diproses kembali setelah waktu yang ditentukan. Laravel mendukung delay menggunakan integer detik atau date instance.

Contoh:

public function handle(): void
{
    if ($this->serviceIsTemporarilyUnavailable()) {
        $this->release(30);

        return;
    }

    // Lanjutkan proses
}

Pendekatan ini berguna ketika Anda mengetahui bahwa kondisi tersebut bersifat sementara dan Job sebaiknya dicoba lagi nanti.


Retry Failed Job Tidak Sama dengan Membuat Job Baru

Ini penting untuk dipahami.

Ketika Anda menjalankan:

php artisan queue:retry JOB_ID

Anda sedang mengembalikan Failed Job ke queue untuk dicoba kembali.

Sedangkan:

dispatch(new SyncOrderToApi($orderId));

membuat dan memasukkan Job baru ke queue.

Dalam aplikasi yang menangani pembayaran, order, atau transaksi, perbedaan ini sangat penting.

Jika Anda tidak hati-hati, Anda dapat memproses operasi yang sama lebih dari satu kali.


Waspadai Duplicate Processing

Bayangkan Job berikut:

public function handle(): void
{
    $this->chargePayment();

    throw new Exception('Something went wrong');
}

Bagaimana jika:

chargePayment()

sebenarnya berhasil, tetapi exception terjadi setelah pembayaran sukses?

Laravel menganggap Job gagal.

Kemudian Anda menjalankan:

php artisan queue:retry JOB_ID

Job diproses kembali.

Akibatnya:

Pembayaran pertama → sukses
Retry → pembayaran kedua → sukses

Anda berpotensi melakukan transaksi dua kali.

Karena itu, Job yang melakukan operasi penting harus dirancang agar idempotent atau aman ketika dijalankan lebih dari sekali.

Contohnya gunakan:

  • unique transaction ID,
  • idempotency key,
  • pengecekan status sebelum melakukan operasi,
  • database constraint,
  • atau mekanisme deduplikasi.

Ini sangat penting untuk Job yang berkaitan dengan:

  • pembayaran,
  • invoice,
  • pengiriman,
  • stok,
  • webhook,
  • dan integrasi API eksternal.

Failed Jobs dengan Redis

Jika Anda menggunakan Redis sebagai Queue connection, proses Queue biasanya berjalan seperti:

Laravel
   ↓
Redis
   ↓
Queue Worker
   ↓
Job

Jika Job gagal berkali-kali sampai melewati batas percobaan, informasi Failed Job akan ditangani oleh mekanisme failed-job yang dikonfigurasi Laravel.

Contoh worker:

php artisan queue:work redis --tries=3

Kemudian periksa:

php artisan queue:failed

Untuk sistem Laravel + Redis + Supervisor, Anda juga perlu memastikan worker tetap berjalan setelah Job gagal.

Jika menggunakan Horizon, mekanisme monitoring dan pengelolaan queue berbeda karena Horizon memang dirancang untuk Redis queues. Laravel mendokumentasikan bahwa Horizon mengelola Redis queues.


Failed Jobs dengan Laravel Horizon

Jika aplikasi menggunakan Laravel Horizon, Anda dapat memonitor Queue melalui dashboard Horizon.

Horizon sangat berguna untuk melihat kondisi Queue secara lebih visual, termasuk Job yang mengalami masalah.

Namun, command yang digunakan untuk menghapus Failed Job perlu diperhatikan.

Untuk aplikasi yang menggunakan Horizon, Laravel merekomendasikan menggunakan:

php artisan horizon:forget JOB_ID

daripada:

php artisan queue:forget JOB_ID

ketika menghapus Failed Job yang dikelola oleh Horizon.

Ini menjadi salah satu alasan mengapa artikel tentang Failed Jobs sebaiknya dibaca bersama artikel tentang Laravel Horizon.


Apa yang Harus Dilakukan Setelah Job Gagal?

Gunakan alur berikut:

Job gagal
   ↓
Periksa queue:failed
   ↓
Cari error
   ↓
Periksa laravel.log
   ↓
Tentukan apakah error sementara/permanen
   ↓
Perbaiki penyebab
   ↓
Retry Job
   ↓
Monitor hasil

Jangan menggunakan pola:

Job gagal
   ↓
queue:retry all
   ↓
Job gagal lagi
   ↓
queue:retry all

Jika masalah utama belum diperbaiki, retry hanya mengulang masalah.


Contoh Troubleshooting Failed Job

Misalnya Anda memiliki Job:

class SendInvoiceEmail implements ShouldQueue
{
    public $tries = 3;

    public function handle(): void
    {
        Mail::to($this->email)->send(
            new InvoiceMail($this->invoice)
        );
    }
}

Kemudian:

php artisan queue:failed

menampilkan:

Connection: redis
Queue: emails

Anda periksa log:

tail -f storage/logs/laravel.log

ternyata ditemukan:

Connection could not be established with SMTP server

Jangan langsung retry.

Periksa terlebih dahulu:

SMTP_HOST
SMTP_PORT
MAIL_USERNAME
MAIL_PASSWORD

Setelah konfigurasi SMTP diperbaiki, baru jalankan:

php artisan queue:retry JOB_ID

Kemudian monitor:

php artisan queue:failed

Jika Job sudah berhasil, record tersebut tidak lagi berada dalam daftar Failed Jobs.


Bagaimana Jika Job Terus Gagal Setelah Retry?

Jika Job tetap gagal setelah retry, jangan terus menjalankan:

php artisan queue:retry all

Cari pola errornya.

Jika error selalu sama

Contoh:

Class "App\Services\PaymentService" not found

Kemungkinan ada masalah pada kode atau deployment.

Jika error hanya kadang terjadi

Contoh:

Connection timed out

Kemungkinan masalahnya bersifat sementara.

Gunakan retry dengan backoff.

Jika error terjadi setelah waktu tertentu

Misalnya:

TimeoutExceededException

Periksa:

--timeout
retry_after

Laravel menjelaskan bahwa --timeout dan retry_after bekerja bersama. Nilai --timeout sebaiknya beberapa detik lebih pendek daripada retry_after agar worker dihentikan sebelum Job menjadi tersedia kembali untuk diproses. Jika timeout worker lebih panjang daripada retry_after, Job dapat berpotensi diproses dua kali.


Jangan Membuat tries Terlalu Besar

Contoh konfigurasi:

php artisan queue:work redis --tries=20

Tidak selalu berarti sistem lebih reliable.

Jika Job gagal karena bug:

Attempt 1 → gagal
Attempt 2 → gagal
Attempt 3 → gagal
...
Attempt 20 → gagal

Anda hanya membuang resource server.

Untuk error permanen, lebih baik:

Job gagal
↓
Catat error
↓
Perbaiki masalah
↓
Retry

Daripada:

Job gagal
↓
Retry 20x

Strategi Retry yang Lebih Aman

Untuk Job yang berinteraksi dengan API eksternal, Anda dapat menggunakan kombinasi:

public $tries = 5;

public function backoff(): array
{
    return [5, 15, 30, 60];
}

Strateginya:

Attempt 1
   ↓ gagal
5 detik
   ↓
Attempt 2
   ↓ gagal
15 detik
   ↓
Attempt 3
   ↓ gagal
30 detik
   ↓
Attempt 4
   ↓ gagal
60 detik
   ↓
Attempt 5

Setelah semua percobaan gagal, Job masuk ke kondisi Failed Job.

Laravel mendukung pola backoff berbentuk array seperti ini untuk memberikan jeda berbeda pada setiap percobaan.


Monitoring Failed Jobs di Production

Untuk production, Failed Jobs sebaiknya tidak hanya menjadi tabel yang diperiksa ketika ada masalah.

Anda sebaiknya mempunyai mekanisme monitoring.

Minimal:

Queue Worker
    ↓
Failed Jobs
    ↓
Laravel Log
    ↓
Monitoring / Alert

Jika aplikasi menggunakan Horizon, manfaatkan dashboard untuk melihat aktivitas Queue.

Jika menggunakan Supervisor, pastikan worker berstatus:

sudo supervisorctl status

Dan untuk Laravel Queue:

php artisan queue:failed

Anda juga dapat menggabungkan monitoring tersebut dengan sistem logging atau notification yang digunakan aplikasi.


Bersihkan Failed Jobs Secara Berkala

Tabel failed_jobs dapat terus bertambah jika tidak pernah dibersihkan.

Untuk menghapus Failed Jobs lama, Laravel menyediakan:

php artisan queue:flush --hours=48

Perintah tersebut dapat digunakan untuk menghapus record Failed Jobs yang sudah berada di luar rentang waktu yang ditentukan.

Namun, sebelum menghapusnya, pertimbangkan apakah record tersebut masih diperlukan untuk:

  • debugging,
  • audit,
  • investigasi incident,
  • atau kebutuhan operasional.

Jangan menganggap semakin sedikit Failed Jobs berarti aplikasi semakin sehat.

Jumlah Failed Jobs yang tiba-tiba meningkat justru bisa menjadi sinyal adanya masalah.


Checklist Laravel Failed Jobs

Sebelum melakukan retry, periksa:

[ ] Sudah menjalankan php artisan queue:failed
[ ] Sudah mengetahui Job yang gagal
[ ] Sudah memeriksa error/log
[ ] Penyebab error sudah diperbaiki
[ ] API/database/Redis yang bermasalah sudah normal
[ ] Job aman untuk dijalankan ulang
[ ] Tidak berpotensi melakukan transaksi dua kali
[ ] Worker masih berjalan
[ ] Connection Queue sudah benar
[ ] Queue yang digunakan sudah benar

Setelah itu:

php artisan queue:retry JOB_ID

Kemudian monitor hasilnya.


Perintah Laravel Failed Jobs yang Perlu Diingat

Berikut command yang paling sering digunakan:

Melihat Failed Jobs

php artisan queue:failed

Retry satu Job

php artisan queue:retry JOB_ID

Retry beberapa Job

php artisan queue:retry ID_1 ID_2 ID_3

Retry semua Job

php artisan queue:retry all

Retry berdasarkan queue

php artisan queue:retry --queue=emails

Menghapus satu Failed Job

php artisan queue:forget JOB_ID

Menghapus semua Failed Jobs

php artisan queue:flush

Menghapus Failed Jobs yang lebih lama dari periode tertentu

php artisan queue:flush --hours=48

Untuk Horizon, gunakan:

php artisan horizon:forget JOB_ID

untuk menghapus Failed Job yang dikelola Horizon.


Kesimpulan

Laravel Failed Jobs bukan berarti sistem Queue Anda rusak.

Failed Job justru merupakan mekanisme penting untuk mengetahui bahwa sebuah Job sudah gagal diproses dan membutuhkan perhatian.

Alur yang paling aman adalah:

Job gagal
     ↓
queue:failed
     ↓
Cari penyebab
     ↓
Perbaiki error
     ↓
Pastikan Job aman diulang
     ↓
queue:retry JOB_ID
     ↓
Monitor

Untuk Job yang berkomunikasi dengan service eksternal, gunakan strategi retry yang sesuai. Anda dapat mengatur jumlah percobaan dengan tries, memberikan jeda menggunakan backoff, atau menggunakan retryUntil ketika batas waktu lebih relevan daripada jumlah percobaan. Laravel juga menyediakan method failed() untuk menjalankan penanganan khusus ketika Job benar-benar gagal.

Yang paling penting, jangan menganggap retry sebagai solusi untuk semua error.

Jika penyebabnya adalah bug permanen, API key salah, data tidak tersedia, atau masalah logika aplikasi, retry berkali-kali hanya akan mengulang kegagalan.

Dan untuk Job yang menangani pembayaran, stok, invoice, atau transaksi penting, pastikan Job dirancang agar aman ketika diproses lebih dari sekali.


FAQ

Apa itu Failed Jobs di Laravel?

Failed Jobs adalah Job yang tidak berhasil diproses setelah melewati batas percobaan atau mengalami kondisi kegagalan. Laravel menyediakan mekanisme untuk menyimpan dan melihat Job tersebut sehingga dapat dianalisis dan di-retry.

Bagaimana cara melihat Failed Jobs Laravel?

Gunakan:

php artisan queue:failed

Command ini menampilkan ID, connection, queue, dan informasi waktu kegagalan Job.

Bagaimana cara retry Failed Job Laravel?

Gunakan:

php artisan queue:retry JOB_ID

Ganti JOB_ID dengan ID Failed Job yang diperoleh dari php artisan queue:failed.

Bagaimana cara retry semua Failed Jobs?

Gunakan:

php artisan queue:retry all

Namun, pastikan penyebab kegagalan sudah diperbaiki sebelum melakukan retry massal.

Bagaimana cara mengatur jumlah retry Laravel Queue?

Anda dapat menggunakan:

php artisan queue:work redis --tries=3

atau menentukan tries langsung pada Job:

public $tries = 3;

Laravel juga menyediakan retryUntil() sebagai alternatif berbasis waktu.

Apa fungsi backoff pada Laravel Queue?

backoff menentukan berapa lama Laravel menunggu sebelum mencoba kembali Job yang mengalami exception.

Contohnya:

public $backoff = 10;

atau:

public function backoff(): array
{
    return [5, 15, 30];
}

Apakah Failed Job harus selalu di-retry?

Tidak.

Retry hanya dilakukan jika penyebab kegagalan sudah diperbaiki dan Job aman untuk dijalankan kembali.

Kenapa Job berhasil tetapi kemudian masuk Failed Jobs?

Salah satu kemungkinan adalah Job sebenarnya sudah melakukan efek samping, tetapi kemudian mengalami exception sebelum proses dianggap selesai. Karena itu, Job yang melakukan transaksi penting sebaiknya dirancang agar idempotent dan aman ketika diproses lebih dari sekali.