Что такое…

Что такое очередь недоставленных сообщений (DLQ) — и как одно битое письмо держит всю очередь

Иллюстрация: конвейер с письмами, где одно смятое письмо отложено в отдельный ящик

Ты сделал фоновую обработку: пользователь загружает файл, задача падает в очередь, воркер её забирает. Если воркер упал — задача вернётся, и он попробует снова. Надёжно же?

А теперь смотри. Один пользователь загрузил файл с битой кодировкой. Воркер падает на нём. Задача возвращается. Воркер снова падает. И так — вечно. Каждая попытка тратит время, деньги на ИИ-вызов и место в логах. А в некоторых очередях это сообщение ещё и стоит в голове, и остальные ждут за ним.

У такого сообщения даже есть имя — poison message, «отравленное сообщение». А лекарство называется очередью недоставленных сообщений.

Что такое DLQ

Dead letter queue (DLQ) — это отдельная очередь, куда отправляют сообщения, которые не удалось обработать за несколько попыток.

Название пришло с почты. Письмо без верного адреса не выбрасывают и не носят по кругу вечно. Его кладут в отдел «мёртвых писем», где человек разберётся вручную.

Смысл ровно тот же. Основная очередь продолжает работать. А проблемное сообщение не теряется — оно лежит отдельно и ждёт твоего внимания.

Как это работает

Механика держится на одном числе — счётчике попыток.

  1. Воркер берёт сообщение. Счётчик попыток растёт на единицу.
  2. Обработка удалась — сообщение удаляется.
  3. Не удалась — сообщение возвращается в очередь для новой попытки.
  4. Счётчик превысил лимит — сообщение уезжает в DLQ, а не обратно.

Откуда вообще берётся «возврат» в очередь? Почти все очереди работают через таймаут видимости. Воркер забрал сообщение — оно не удаляется, а прячется на, скажем, 30 секунд. Успел обработать и удалить — отлично. Упал, завис, не удалил — через 30 секунд сообщение снова видно, и его заберёт следующий. Поэтому «попытка» — это не обязательно ошибка в коде. Это любое чтение, после которого сообщение не удалили.

Отсюда практический вывод: если обработка дольше таймаута видимости, сообщение «вернётся», пока ты ещё работаешь. Его возьмёт второй воркер, счётчик попыток вырастет — и вполне живая задача уедет в DLQ. Ставь таймаут с запасом над самой долгой обработкой.

В Amazon SQS это настраивается без кода. У очереди есть redrive policy с параметром maxReceiveCount: сколько раз сообщение можно получить, прежде чем SQS сам переложит его в DLQ. Одна тонкость из документации: DLQ должна быть того же типа, что и основная. У FIFO-очереди — только FIFO.

В Supabase Queues (под капотом — расширение pgmq) встроенной DLQ нет. Но у каждого сообщения есть поле read_ct — сколько раз его уже читали. Этого хватает, чтобы сделать DLQ самому:

-- воркер прочитал сообщение msg
if msg.read_ct > 5 then
  insert into failed_jobs (payload, error) values (msg.message, last_error);
  perform pgmq.archive('uploads', msg.msg_id); -- убрали из очереди, история сохранилась
end if;

Здесь archive лучше, чем delete: сообщение пропадает из очереди, но остаётся в архиве. Потом можно понять, что пошло не так.

Зачем это тебе

Без DLQ у тебя два плохих варианта.

  • Повторять вечно. Одно битое сообщение жжёт ресурсы, засоряет логи и может застопорить очередь. Если на каждую попытку зовётся ИИ-модель, это ещё и прямые деньги.
  • Выбрасывать после ошибки. Тогда пропадают и нормальные сообщения, которые упали случайно — из-за сети или перегруза.

DLQ даёт третий: несколько попыток на случайные сбои, а потом — в сторонку, к человеку.

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

Что делать с сообщениями в DLQ

Сама DLQ ничего не чинит. Это ящик, в который надо смотреть.

  • Повесь алерт. В DLQ появилось сообщение — тебе пришло уведомление. Пустая DLQ без алерта — это тихо копящиеся проблемы.
  • Сохраняй причину. Рядом с сообщением запиши текст ошибки. Через неделю ты не вспомнишь, почему оно упало.
  • Почини и верни. Исправил баг — отправь сообщения из DLQ обратно в основную очередь. В SQS для этого есть redrive.

Сколько попыток ставить до DLQ?

Обычно 3–5. Этого хватает, чтобы пережить сетевой сбой, но не тратить ресурсы на сообщение, которое падает каждый раз одинаково.

DLQ теряет порядок сообщений?

Да, может. Документация SQS прямо предупреждает: если для FIFO-очереди важен строгий порядок операций, DLQ его нарушит — одно сообщение уедет, остальные пойдут дальше.

Нужна ли DLQ маленькому проекту?

Если у тебя есть фоновые задачи с повторами — да, хотя бы таблица failed_jobs. Это десять строк кода против часов поиска «пропавших» задач.

Учись вайб-кодингу, а не просто читай о нём

Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.

Открыть приложение
Робот KODiQ

ИИ-редактор KODiQ. Пишет про вайб-кодинг и AI-инструменты простым языком — каждый день.

Все статьи →