Laravel's queue system provides robust mechanisms to handle job retries and failures, which ensure that background jobs can be reliably processed even when unexpected errors occur.
Failed Job Storage Configuration
Laravel uses a dedicated database table named `failed_jobs` to store information about jobs that do not succeed. This table records details such as the connection, queue name, payload (job data), the exception that caused the failure, and the time when the failure occurred. If this table does not exist by default, Laravel provides Artisan commands to create it using migrations. This persistent storage facilitates debugging and allows for manual or automatic reprocessing of failed jobs.Handling Job Failures
Within a queued job class in Laravel, developers can define a `failed` method. This method is automatically called when the job fails permanentlyâafter all retry attempts have been exhausted. The method receives the exception that caused the failure, allowing the application to log the failure, send notifications (such as emails), or update other system states in response.Configuring Job Retries
Laravel supports retrying jobs several times before marking them as failed. This is controlled using the `tries` property on the job class, which defines the maximum number of attempts. For instance, setting `public $tries = 3;` will allow the job to be tried up to three times before being considered failed.In addition, Laravel supports specifying backoff or delay times between retries using the `backoff` property, which can be a single number or an array of intervals. This means after a failure, the job can be delayed before being retried, which is useful for handling temporary issues like network errors or slow services. The `queue:work` Artisan command also accepts parameters like `--tries` and `--backoff` to control retry limits and delays at runtime.
Job Timeouts and Retry After
Each job can be assigned a `timeout` value indicating the maximum number of seconds it can run. If the job exceeds that time, the queue worker process is killed by the operating system. However, Laravel does not immediately know the job was killed due to timeout. To handle this safely, Laravel has a `retry_after` parameter (configured per queue connection), which instructs Laravel to retry a job if it hasn't finished after this many seconds. It ensures jobs are retried in case of timeouts but requires `retry_after` to be greater than the longest `timeout` value to avoid prematurely retrying still-running jobs.Releasing Jobs Back into the Queue
Sometimes a job should be retried immediately or after a certain delay based on application logic. Laravel provides a `release` method that re-queues the job, optionally with a delay. This is useful to handle temporary job failures without counting as a final failure. Released jobs are put back into the queue to be attempted again either immediately or after a delay. This mechanism relies on the features of the underlying queue driver (Redis, database, etc.) to handle timed delays.Failed Job Monitoring and Management Commands
Developers can monitor failed jobs using the `php artisan queue:failed` command, which lists all the jobs that have failed, showing their ID, connection, queue, job class, and failure timestamp.Jobs that have failed can be retried manually using the `php artisan queue:retry {id}` command, which will re-queue the failed job for processing again. There is also an option to retry all failed jobs at once by running `php artisan queue:retry all`. To clean up old failed job records, `php artisan queue:flush` deletes all failed jobs, and `php artisan queue:prune-failed` can delete those older than a configurable number of hours.
Automatic Failure Callbacks
Laravel also allows registering a global failure callback using the `Queue::failing()` method. This callback is invoked exactly once for every failed job. It can be used to perform centralized failure handling such as logging or external notifications in addition to individual job failure methods.Common Configuration and Usage Patterns
- In the job class, typical properties are defined as follows:- `public $tries` to specify max retry attempts.
- `public $backoff` to specify delay(s) between retry attempts.
- `public $timeout` to set maximum job run time.
- The queue connection configuration (`config/queue.php`) includes a `retry_after` setting, critical for retrying timed-out jobs.
- The queue worker is started using `php artisan queue:work` with optional flags for `--tries`, `--backoff`, and queue names.
- It's important that `retry_after` in the queue config is greater than any `timeout` set on jobs to avoid overlapping retries while the job is still running.
How Retries and Failures Work in Practice
When a job fails (usually due to an uncaught exception), Laravel checks whether the job has remaining `tries`. If so, Laravel waits for the configured backoff duration, then retries the job. If the job reaches its maximum tries without success, Laravel stores it in the `failed_jobs` table and calls the `failed` method on the job class if implemented.If a job exceeds its timeout, the worker process running it is killed by the OS. Laravel relies on the `retry_after` setting to eventually retry the job after assuming it failed. The job will then be re-attempted up to the limit set by `tries`. After that, it will be marked as failed.
Laravel workers will not automatically retry failed jobs stored in `failed_jobs` unless manually retried or configured with external tools like Horizon to handle that.
Jobs can also be explicitly released back into the queue using the `release()` method on the job instance, allowing for manual retries with optional delays without counting against maximum tries immediately.
Summary of Key Points
- Laravel stores failed jobs in a database table for later inspection and manual retries.- Jobs can be retried automatically a configurable number of times via the `tries` property.
- Delays between retries can be controlled with the `backoff` property.
- A `failed` method on the job class runs after all retries have been exhausted.
- Job timeouts cause the worker to kill the job process; `retry_after` ensures these jobs are retried.
- Jobs can be released back into the queue manually with delays.
- Artisan commands aid monitoring, retrying, and cleaning failed jobs.
- Global failure callbacks provide centralized failure handling beyond individual jobs.
- Proper configuration of `retry_after` and timeouts is critical to preventing infinite retries or premature retries.