The execution of a job can fail. This can be due to internal errors which result from poorly written code which should be fixed first. But they can also fail due to external problems like a service or a resource being unavailable. This can lead to Exceptions or timeouts.
In the latter cases, it's good to be able to retry a job after some time. There are several ways to do this.
The first method is to use component options:
'components' => [
'queue' => [
'class' => \yii\queue\<driver>\Queue::class,
'ttr' => 5 * 60, // Max time for job execution
'attempts' => 3, // Max number of attempts
],
],
The ttr
option sets the time to reserve for job execution. If the execution of a job takes longer than
this time, execution will be stopped and it will be returned to the queue for later retry. The same happens
if the job throws an Exception. The attempts
option sets the max. number of attempts. If this number is
reached, and the job still isn't done, it will be removed from the queue as completed.
These options apply to all jobs in the queue. If you need to change this behavior for specific jobs, see the following method.
To have more control over the retry logic a job can implement the RetryableJobInterface
.
For example:
class SomeJob extends BaseObject implements RetryableJobInterface
{
public function execute($queue)
{
//...
}
public function getTtr()
{
return 15 * 60;
}
public function canRetry($attempt, $error)
{
return ($attempt < 5) && ($error instanceof TemporaryException);
}
}
The getTtr()
and canRetry()
methods have a higher priority than the component options mentioned above.
The third method to control TTR and number of retries for failed jobs involves the
Queue::EVENT_BEFORE_PUSH
and Queue::EVENT_AFTER_ERROR
events.
The TTR can be set with the Queue::EVENT_BEFORE_PUSH
event:
Yii::$app->queue->on(Queue::EVENT_BEFORE_PUSH, function (PushEvent $event) {
if ($event->job instanceof SomeJob) {
$event->ttr = 300;
}
});
And the Queue::EVENT_AFTER_ERROR
event can be used to decide whether to try another attempt:
Yii::$app->queue->on(Queue::EVENT_AFTER_ERROR, function (ErrorEvent $event) {
if ($event->job instanceof SomeJob) {
$event->retry = ($event->attempt < 5) && ($event->error instanceof TemporaryException);
}
});
Event handlers are executed after RetryableJobInterface
methods, and therefore have the highest
priority.
Full support for retryable jobs is implemented in the Beanstalk, DB, File, AMQP Interop and Redis drivers. The Sync driver will not retry failed jobs. The Gearman driver doesn't support retryable jobs. RabbitMQ has only its basic retryable support, where the number of attempts can not be set.
AWS SQS uses Dead Letter Queue for handling messages that were failed to process. All unprocessed messages after a maximum number of attempts are moved to that queue. You should set an address of a Dead Letter Queue and a maximum number of attempts in the AWS Console while creating a queue.