Описать проблему ↗

Дубли и данные

Laravel выполняет job дважды: как защитить результат

Laravel выполняет одно задание повторно и создаёт лишние действия. Объясняем, как отличить две постановки в очередь от повторной попытки той же job.

Редакция «починимсайт» · Обновлено · 3 мин. чтения

Что происходит и с чего начать

Очередь может повторить задание после сбоя или потери подтверждения выполнения. Также приложение иногда само ставит два разных задания для одного события. Эти причины нужно различать по идентификаторам и истории попыток. Начните с конкретного результата: два письма, два заказа или два изменения. Сам факт повторного запуска не всегда ошибочен; ошибка возникает, когда повтор небезопасно дублирует уже выполненное действие. Поэтому важно понять, что успело произойти до прерывания первой попытки.

Что можно проверить самостоятельно

  1. Сравните идентификаторы заданий и число попыток. Запишите время начала, завершения и внешний результат, чтобы понять, повторилась одна job или были созданы две самостоятельные записи.
  2. Уточните длительность обычного выполнения и используемое хранилище очереди. Настройки ожидания различаются, поэтому перенос значений из другого проекта без проверки может дать неверный результат.
  3. Проверьте, какие действия успели выполниться до ошибки. Не повторяйте все неудачные задания автоматически, если они отправляют сообщения, создают документы или обращаются к платёжному сервису.

Как понять результаты проверки

Специалист согласует ограничения времени worker и механизм повторной выдачи задания для выбранного драйвера. Но правильные тайм-ауты не заменяют идемпотентность: процесс может аварийно завершиться в любой момент. Нужно хранить устойчивый ключ бизнес-операции и подтверждённый результат. Для внешнего действия полезны предусмотренные сервисом средства безопасного повтора. Простая блокировка параллельного запуска решает только часть задачи: она не объясняет, как восстановиться после завершения эффекта, если отметка об успехе не была сохранена приложением.

Что проверяет разработчик

Для этой части понадобятся доступ к настройкам, журналам ошибок или коду. Её можно передать специалисту вместе с результатами предыдущих шагов.

  1. Сопоставьте идентификатор задания, попытки и время работы worker. Проверьте, не ставит ли приложение две отдельные jobs для одного события.
  2. Сверьте timeout, retry_after или visibility timeout используемого драйвера. Если задание снова становится доступным раньше завершения, другой worker может взять его повторно.
  3. Найдите момент сбоя относительно побочного действия. Письмо могло уйти до исключения, а повтор отправит его снова.

На что обратить внимание

Механизм очереди не гарантирует однократность внешнего действия при любых сбоях. Блокировка параллелизма помогает, но не заменяет сохранение бизнес-результата.

Как исправляют причину

Согласуйте тайм-ауты по выбранному драйверу и добавьте идемпотентность операции. Храните ключ и состояние эффекта; используйте возможности внешнего сервиса для безопасных повторов там, где они предусмотрены.

Как убедиться, что проблема решена

Остановите тестовый worker после эффекта, но до завершения job, и проверьте повтор. Заказ, уведомление или платёж не должны создаваться второй раз.

В тестовой среде проверьте повтор после частичного выполнения и обычный параллельный запуск, если он допустим. Заказ или другое действие не должно создаваться повторно без основания. При этом новое независимое событие обязано обрабатываться нормально. Отдельно разберите накопившиеся ошибки очереди: исправленный код не определяет автоматически, какие старые внешние действия уже состоялись и требуют сверки перед восстановлением.