Skip to content

Retry And DLQ Lifecycle

Retry and DLQ lifecycle

Retries и DLQ - это разные решения.

Retry означает: "Это задание может успешно выполниться позже."
DLQ означает: "Автоматическое выполнение нужно остановить; задание должен проверить человек или repair workflow."

ChokaQ оставляет это различие явным, чтобы transient outages не превращались в операторский шум, а permanent failures не сжигали worker capacity бесконечно.

Нормальный retry path

  1. Воркер переводит задание из Fetched в Processing.
  2. Handler выбрасывает exception.
  3. Processor классифицирует ошибку.
  4. Если ошибка retryable и retry budget еще не исчерпан, ChokaQ вычисляет будущий ScheduledAtUtc.
  5. Строка остается в JobsHot.
  6. Воркеры пропускают строку, пока не наступит время запуска.
  7. Следующее выполнение увеличивает AttemptCount, когда задание переходит в Processing.

Retry scheduling хранится в SQL. Delay не принадлежит никакому спящему thread'у.

DLQ path

Задание перемещается в JobsDLQ, когда автоматическая обработка больше не является правильным решением. Типичные причины:

ReasonMeaningOperator action
MaxRetriesExceededRetry budget исчерпан.Проверить ошибку, исправить dependency или payload, затем resurrect, если безопасно.
FatalErrorОшибка классифицирована как non-transient.Исправить code/data перед повторным запуском.
CancelledOperator или runtime path отменил задание.Подтвердить, что отмена была намеренной.
ZombieHeartbeat выполнения истек.Проверить side effects перед resurrection.
CircuitBreakerOpenRuntime отказал в выполнении, потому что circuit открыт.Исправить downstream или дождаться восстановления.
ThrottledDownstream попросил систему замедлиться.Учитывать retry-after или настроить traffic.
RetryLifetimeExpiredВозраст задания превысил policy.Рассматривать как stale work.

DLQ - это не просто таблица ошибок. Это операционная очередь для работы, которая требует инженерного решения.

Входные данные для retry delay

Retry delay использует policy из ChokaQ:Retry:

SettingPurpose
MaxAttemptsОграничивает, сколько выполнений может стартовать.
BaseDelayНачальная задержка после transient failure.
BackoffMultiplierКоэффициент экспоненциального роста.
MaxDelayЖесткий верхний предел retry delay.
JitterMaxDelayСлучайный разброс, чтобы избежать синхронных retry storms.
CircuitBreakerDelayDelay, когда выполнение заблокировано открытым breaker'ом.
MaxJobAgeНе дает очень старым заданиям retry'ться бесконечно.

Зачем нужен DLQ

Без DLQ у queue engine остаются два слабых варианта:

  • бесконечно retry'ть плохую работу;
  • тихо выбрасывать плохую работу.

ChokaQ выбирает третий вариант: сохранить failed job вместе с payload, failure reason, error details, attempt count, queue, type key и timestamps. После этого оператор может inspect, edit, resurrect, purge или оставить строку для audit.

Архитектурное решение

Почему этот pattern?

At-least-once системам нужен durable terminal state для работы, которую automatic execution не может безопасно завершить. DLQ дает такой terminal state без уничтожения evidence.

Trade-offs

DLQ создает операционную ответственность. Кто-то должен следить за ним и принимать решения. Выигрыш - явные evidence и operator control вместо бесконечного retry churn вокруг одного poison message.

Рассмотренные альтернативы

AlternativeBenefitCost
Infinite retriesПросто реализовать.Retry storms, лишняя нагрузка на воркеры, скрытые poison jobs.
Drop failed jobsОчереди остаются чистыми.Теряется принятая работа и уничтожаются audit evidence.
External error topic onlyХорошо работает в broker systems.Все равно нужны storage, tooling и operator workflow.

Когда не использовать этот подход

Для одноразовых telemetry events, где потеря допустима, durable DLQ может быть слишком тяжелым. Для business workflows, payments, emails, reports и integrations DLQ безопаснее как default.

Дополнительные вопросы

Почему не retry forever?
Потому что permanent failures превращаются в retry storms и потребляют capacity, нужную здоровым заданиям.

Почему zombies идут в DLQ, а не в retry?
Потому что handler мог уже произвести side effects. Blind retry может продублировать emails, charges или external writes.

Что делает retry безопасным?
Только idempotency handler'а и downstream idempotency делают external side effects безопасными. ChokaQ может сохранить и перепланировать работу, но не может сделать произвольные side effects exactly-once.

Лицензия Apache 2.0