Laravel Queues Failure Handling Retries Backoff Timeout Failed Jobs Idempotency
Definisi
Queue di Laravel memisahkan pekerjaan berat (email, resize gambar, integrasi pihak ketiga) dari request utama. Job dikirim ke antrian, lalu dijalankan oleh worker di background. Failure handling mencakup retry, backoff, timeout, job gagal, dan idempotensi.
Kapan dipakai
Pakai queue kalau operasi lambat atau bisa gagal sementara (network flaky, API rate limit). Jangan pakai queue untuk hal yang harus langsung direspons ke user dalam request yang sama.
Cara kerja
Dispatch dan retry
class SendWelcomeEmail implements ShouldQueue
{
use Queueable, InteractsWithQueue, SerializesModels;
public $tries = 3;
public $backoff = [5, 15, 60]; // detik antar retry
public function handle(): void
{
Mail::to($this->user->email)->send(new Welcome());
}
public function failed(Throwable $e): void
{
logger()->error('email gagal', ['id' => $this->user->id]);
}
}$tries membatasi percobaan, $backoff memberi jeda makin lama tiap gagal (exponential-ish).
Timeout
Worker bisa dihentikan kalau job lewat batas waktu:
// config/queue.php
'worker_timeout' => 60,Atau per-job lewat timeout property (membutuhkan pcntl).
Failed jobs table
php artisan queue:failed-table
php artisan migrate
php artisan queue:retry allJob gagal disimpan di tabel failed_jobs dan bisa di-retry tanpa kehilangan payload.
Idempotensi
Worker bisa menjalankan job lebih dari sekali (retry, atau worker mati tengah jalan). Job harus aman dijalankan berulang:
public function handle(): void
{
if (Cache::has("sent:{$this->user->id}")) {
return; // sudah diproses, hindari duplikat
}
Mail::to($this->user->email)->send(new Welcome());
Cache::put("sent:{$this->user->id}", true, now()->addHour());
}Struktur alur
Catatan implementasi
- Jalankan worker dengan
php artisan queue:work, bukanqueue:listendi production (listen restart tiap job, lebih lambat). - Set
retry_afterdi config queue lebih besar dari waktu job tertinggi, supaya job tidak dianggap gagal padahal masih jalan. - Untuk job kritis, simpan status di DB supaya progress bisa dilacak dan tidak dobel proses.
Kesalahan umum
- Job tidak idempoten: retry mengirim email dua kali karena tidak ada guard.
retry_afterterlalu kecil: worker membunuh job yang sebenarnya masih jalan, lalu retry dobel.- Lupa
failed()handler: error tertelan, tidak ada log atau alert. - Menyimpan model utuh di property job: model bisa berubah; lebih aman simpan ID lalu query ulang di
handle().