Давай по новой
Когда что-то не срабатывает с первого раза, что мы делаем? Правильно, пробуем снова. Вдруг на этот раз получится? Этот же подход хорошо работает и в разработке.
Мы можем повторять неудачные операции, чтобы восстановиться после временных ошибок или сбоев в сети. Laravel предоставляет “из коробки” набор отличных механизмов “повтора” для обработки временных сбоев!
Перечисленные методы ни как не помогут со сбоями вызванной неправильной бизнес-логикой, ошибками в коде, или неверными данными (
.env
). Независимо от того, сколько раз мы их повторяем, эти сбои не будут автоматически исправлены, пока мы не исправим код или конфигурацию.
Теперь давайте узнаем о механизмах “Повтора” в Laravel:
Транзакции базы данных
При выполнении транзакции базы данных может возникнуть взаимоблокировка. Это может произойти, когда транзакция не может завершиться из-за конфликта с другой параллельной транзакцией, пытающейся записать те же данные.
В методе DB::transaction
можно просто передать второй параметр, чтобы указать, сколько раз он будет повторять транзакцию, если произойдет взаимоблокировка.
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
}, 5);
Подробнее можно посмотреть в документации:
HTTP Вызов
Мы все знаем, что сторонние API или сервисы не всегда надежны! По этому должны построить наше приложение так, чтобы мы могли элегантно обрабатывать сбои этих сервисов.
Если мы используем HTTP Client
, предоставленный Laravel, мы можем использовать метод retry
, чтобы автоматически повторить вызов API, если он не был успешным.
$response = Http::retry(3, 100)->post(/* ... */);
Мы также можем передать второй параметр методу, чтобы указать, сколько времени Laravel должен подождать перед повторной попыткой.
Хорошей идеей является ожидание некоторого времени перед повторной попыткой. Потому что, если сервис недоступен, мгновенная повторная попытка также может завершиться неудачей.
Подробнее можно посмотреть в документации:
Очереди
При выполнении задания в очереди оно может не выполниться из-за какой-то ошибки. Если сбой вызван временными ошибками, хорошей идеей будет попытаться выполнить задание снова.
При запуске queue:work
мы можем передать аргумент для указания максимального количества попыток выполнения задания, если оно не удалось.
php artisan queue:work --tries=3
А так же можем указать в каждом задании отдельно, сколько раз оно может быть повторено, и сколько времени оно должно ожидать перед повтороной попыткой:
namespace App\Jobs;
class ProcessPodcast implements ShouldQueue
{
/**
* Количество попыток выполнить задание.
*
* @var int
*/
public $tries = 5;
/**
* Количество секунд ожидания перед повторной попыткой задания.
*
* @var int
*/
public $backoff = 3;
}
Мы также можем определить метод tries
, метод backoff
или использовать подход на основе времени, определив метод retryUntil
в классе задания.
Подробнее можно посмотреть в документации:
- https://laravel.su/docs/10.x/queues#dealing-with-failed-jobs
- https://laravel.su/docs/10.x/queues#max-job-attempts-and-timeout
Даже после нескольких попыток выполнить задание некоторые из них могут остаться неудачными из-за ошибок, для исправления которых нужно внести изменения в код или конфигурацию. Laravel сохраняет такие неудавшиеся задания в специальной таблице базы данных failed_jobs
, что бы после изменений мы могли повторно выполнить задания.
С помощью команды php artisan queue:retry
мы можем повторно выполнить как одно, несколько или все задания.
Подробнее можно посмотреть в документации:
Универсальная функция
Laravel предоставляет отличный вспомогательную функцию под названием retry
, который можно использовать для повтора любого участка кода. Это действительно полезно, когда мы хотим повторить какую-то логику с разными параметрами.
Вот простой пример для генерации уникального числа, которое еще не используется на основе некоторых критериев:
function getUniqueNumber()
{
return retry(5, function () {
$number = rand(1, 100);
// 'isUsed' - вымышленный метод, который проверяет какую-то логику
if (isUsed($number)) {
throw new Exception('Число уже используется');
}
return $number;
});
}
Подробнее можно посмотреть в документации:
Режим обслуживания
Хотя это не является частью механизма повтора, но заслуживает упоминания.
При запуске php artisan down
мы также можем указать параметр retry
, который будет установлен как значение заголовка HTTP Retry-After
:
php artisan down --retry=60
Некоторые браузеры будут автоматически обновлять страницу с указанным интервалом, освобождая пользователя от необходимости постоянно нажимать “F5”.
Подробнее можно посмотреть в документации: