Дубли и данные
Laravel выполняет job дважды: как защитить результат
Laravel выполняет одно задание повторно и создаёт лишние действия. Объясняем, как отличить две постановки в очередь от повторной попытки той же job.
Что происходит и с чего начать
Очередь может повторить задание после сбоя или потери подтверждения выполнения. Также приложение иногда само ставит два разных задания для одного события. Эти причины нужно различать по идентификаторам и истории попыток. Начните с конкретного результата: два письма, два заказа или два изменения. Сам факт повторного запуска не всегда ошибочен; ошибка возникает, когда повтор небезопасно дублирует уже выполненное действие. Поэтому важно понять, что успело произойти до прерывания первой попытки.
Что можно проверить самостоятельно
- Сравните идентификаторы заданий и число попыток. Запишите время начала, завершения и внешний результат, чтобы понять, повторилась одна job или были созданы две самостоятельные записи.
- Уточните длительность обычного выполнения и используемое хранилище очереди. Настройки ожидания различаются, поэтому перенос значений из другого проекта без проверки может дать неверный результат.
- Проверьте, какие действия успели выполниться до ошибки. Не повторяйте все неудачные задания автоматически, если они отправляют сообщения, создают документы или обращаются к платёжному сервису.
Как понять результаты проверки
Специалист согласует ограничения времени worker и механизм повторной выдачи задания для выбранного драйвера. Но правильные тайм-ауты не заменяют идемпотентность: процесс может аварийно завершиться в любой момент. Нужно хранить устойчивый ключ бизнес-операции и подтверждённый результат. Для внешнего действия полезны предусмотренные сервисом средства безопасного повтора. Простая блокировка параллельного запуска решает только часть задачи: она не объясняет, как восстановиться после завершения эффекта, если отметка об успехе не была сохранена приложением.
Что проверяет разработчик
Для этой части понадобятся доступ к настройкам, журналам ошибок или коду. Её можно передать специалисту вместе с результатами предыдущих шагов.
- Сопоставьте идентификатор задания, попытки и время работы worker. Проверьте, не ставит ли приложение две отдельные jobs для одного события.
- Сверьте timeout, retry_after или visibility timeout используемого драйвера. Если задание снова становится доступным раньше завершения, другой worker может взять его повторно.
- Найдите момент сбоя относительно побочного действия. Письмо могло уйти до исключения, а повтор отправит его снова.
На что обратить внимание
Механизм очереди не гарантирует однократность внешнего действия при любых сбоях. Блокировка параллелизма помогает, но не заменяет сохранение бизнес-результата.
Как исправляют причину
Согласуйте тайм-ауты по выбранному драйверу и добавьте идемпотентность операции. Храните ключ и состояние эффекта; используйте возможности внешнего сервиса для безопасных повторов там, где они предусмотрены.
Как убедиться, что проблема решена
Остановите тестовый worker после эффекта, но до завершения job, и проверьте повтор. Заказ, уведомление или платёж не должны создаваться второй раз.
В тестовой среде проверьте повтор после частичного выполнения и обычный параллельный запуск, если он допустим. Заказ или другое действие не должно создаваться повторно без основания. При этом новое независимое событие обязано обрабатываться нормально. Отдельно разберите накопившиеся ошибки очереди: исправленный код не определяет автоматически, какие старые внешние действия уже состоялись и требуют сверки перед восстановлением.