Ошибки и повторное выполнение

В процессе обработки заданий могут возникать исключительные ситуации. Это могут быть как внутренние ошибки — результат криво написанного кода, так и внешние, когда недоступны запрашиваетмые сервисы и внешние ресурсы. Во втором случае неплохо иметь возможность повторить попытку выполнить задание через некоторое время.

Для того чтобы это сделать существует несколько способов.

Опции повторного выполнения

Первый способ реализован глобальными настройками компонента:

'components' => [
    'queue' => [
        'class' => \zhuravljov\yii\queue\<driver>\Queue::class,
        'ttr' => 5 * 60, // Максимальное время выполнения задания 
        'attempts' => 3, // Максимальное кол-во попыток
    ],
],

Опция ttr устанавливает резервное время для выполнения заданий в очереди. Если в течении этого времени задание не выполнилось, оно вернется в очередь на повторную попытку. Опция attempts устанавливает максимальное кол-во попыток. Если попытки закончились, а задание не выполнено, оно будет удалено из очереди как выполненное.

Описанные опции действуют глобально на все задания в очереди, и чтобы для отдельных заданий это поведение переопределить существует второй способ.

Интерфейс RetryableJob

Индивидуальный контроль повторного выполнения реализован интерфейсом RetryableJob, код такого job-объекта может выглядеть так:

class SomeJob extends Object implements RetryableJob
{
    public function execute($queue)
    {
        //...
    }

    public function getTtr()
    {
        return 15 * 60;
    }

    public function canRetry($attempt, $error)
    {
        return ($attempt < 5) && ($error instanceof TemporaryException);
    }
}

Методы getTtr() и canRetry() имеют более высокий приоритет чем общие настройки очереди, и дают возможность реализовать индивидуальный алгоритм повторного выполнения задачи если предыдущая попытка завершилась неудачей.

Обработчики событий

Третий способ задать резервное время и необходимость повторного запуска невыполненной задачи предполагает использовать события Queue::EVENT_BEFORE_PUSH и Queue::EVENT_AFTER_ERROR.

Событие Queue::EVENT_BEFORE_PUSH можно использовать, чтобы задать резервное время:

Yii::$app->queue->on(Queue::EVENT_BEFORE_PUSH, function (PushEvent $event) {
    if ($event->job instanceof SomeJob) {
        $event->ttr = 300;
    }
});

А событие Queue::EVENT_AFTER_ERROR — чтобы определить задание на повторную попытку:

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);
    }
});

Обработчики событий выполняются после методов RetryableJob, и, следовательно, имеют наивысший приоритет.

Ограничения

Не все драйверы поддерживают повторное выполнение одинаково хорошо. Полнеценную поддержку обеспечивают драйвера: Beanstalk, DB, File и Redis. Синхронный драйвер, как отладочный, не будет повторять невыполненные задания. Gearman не поддерживает повторное выполнение вообще. А RabbitMQ имеет только свою базовую поддержку повторов, при которой номер попытки узнать не получится.