DLQ и обработка ядовитых сообщений
Dead Letter Queue (DLQ) — это изолированная очередь/топик для сообщений, которые не удалось обработать штатным консьюмером после заданного числа попыток или по явным техническим/бизнес-причинам (невалидная схема, таймаут, конфликт версий и т. п.). Ядовитое сообщение (poison message) — запись, повторная обработка которой стабильно завершается ошибкой и угрожает стабильности пайплайна.
Цель DLQ: сохранить SLO, локализовать сбой, не допустить блокировки основного потока и гарантировать возможности анализа и безопасного повторного воспроизведения (редрайва).
1) Откуда берутся ядовитые сообщения
Схемы/контракты: несовместимые изменения, отсутствующие обязательные поля, неверные типы.
Бизнес-валидации: дубликаты, нарушенные инварианты, просроченные события.
Порядок и причинность: пришло «Update» до «Create», пропущенные корреляции, out-of-order.
Идемпотентность: повторная обработка порождает побочные эффекты.
Внешние зависимости: ограниченные лимиты/таймауты, недоступность API.
Данные: коррупция полезной нагрузки, неверная кодировка, превышение размеров.
2) Критерии отправки в DLQ
Сообщение попадает в DLQ, если выполнено одно или несколько условий:- Превышен maxAttempts обработки у консьюмера/воркера.
- Ошибка классифицирована как неисправимая (non-retryable): невалидная схема, отсутствие критичного ресурса, бизнес-запрет.
- Истек deadline сообщения (TTL/expiration).
- Сработала политика circuit breaker или admission control для данного ключа/тенанта.
- Явное решение оператора (ручной «eject» из основного потока).
3) Топологии и паттерны DLQ
Per-queue DLQ: у каждой очереди/топика есть свой DLQ. Просто и прозрачно.
Central DLQ (parking lot): общий «паркинг» для сложных случаев, удобен для единых инструментов анализа.
DLT (Dead Letter Topic): для лог-ориентированных шины (event log) — отдельный топик с метаданными причины отказа.
Quarantine: карантинный буфер с жестким доступом и PII-санитацией для ручного анализа.
Shadow-stream: дублирование проблемных сообщений в «тень» для безопасных экспериментов с фиксом.
4) Метаданные, которые обязаны сопровождать DLQ
Минимальный набор:- Причина отказа: код/класс ошибки, stack/trace id.
- Контекст ретраев: `attempt`, `maxAttempts`, `first_seen_ts`, `last_attempt_ts`.
- Корреляция: `trace_id`, `span_id`, `tenant_id`, `entity_id`, ключ партиционирования.
- Оригинальный offset/partition/sequence (для логовых шин) или message-id.
- Контракт/схема/версия полезной нагрузки.
- Idempotency-key / Request-id (если есть).
- Источник маршрутизации: имя очереди/топика, консьюмер-группа.
5) Политики ретраев до DLQ
Перед отправкой в DLQ используйте корректные повторные попытки:- Короткие ретраи консьюмера: `maxAttempts` 2–5, exponential backoff + джиттер, caps на concurrency.
- Кооперативный backpressure: уменьшение конкуренции при росте ошибок.
- Классификация ошибок: retryable (`5xx`, таймаут) vs non-retryable (валидация, schema mismatch).
- Отложенные очереди (delay queue): 5s → 30s → 2m для временных сбоев.
- Per-key изоляция: если «шумит» конкретный ключ, не блокируйте весь партишен.
6) Безопасный редрайв (повторная доставка из DLQ)
Редрайв — это контролируемый возврат сообщений из DLQ в обработку.
Принципы:1. Проверка фикса: редрайв только после исправления кода/конфигурации/схемы или после восстановления внешних зависимостей.
2. Идемпотентность: обработчики должны быть устойчивы к повтору (upsert, эффект-толурантные операции).
3. Дедупликация: по `idempotency_key`/`message_id`/`business_key`.
4. Дозирование и окна: batches по N сообщений, rate-limit на редрайв, «окна» по времени/партициям.
5. Локальная валидация: быстрая проверка схемы перед редрайвом (reject ранних невалидных кейсов).
6. Приоритет: редрайв не должен вытеснять продовый трафик (низкий приоритет воркеров/отдельный пул).
7. Наблюдаемость: отдельные метрики и трейсы для редрайва; отчет о результатах (успех/повторный DLQ/потеря).
7) Семантика доставки и порядок
At-least-once — самый частый режим: необходимы идемпотентность и дедупликация.
At-most-once — DLQ может быть отключен; риск потери. Используйте только при допустимых потерях.
Exactly-once (эффективное): достигается транзакциями и дедупом в бизнес-хранилище; дорого и специфично.
Порядок: DLQ обычно разрывает порядок для конкретного ключа/партиции. Если порядок критичен, редрайвите по ключу и последовательно.
8) Схемы, контракты и валидация
Schema registry/контракты: четкое версионирование, эволюция с backward/forward-совместимостью.
Валидация на входе: дешевая проверка через JSON Schema/Protobuf/Avro перед тяжелыми шагами.
Политика несовместимости: при «ломающем» поле — немедленно в DLQ с кодом `SCHEMA_INCOMPATIBLE`.
Redaction PII: в DLQ храните только необходимое; чувствительные поля маскируйте.
9) Идемпотентность и дедупликация
Idempotency-key: формируйте на продьюсере из «бизнес-смысла» (tenant+entity+operation+ts-bucket).
Дедуп-журналы: храните последние `N` ключей с TTL; запоминайте «эффект» операции.
Upsert/merge: избегайте «insert-only» без ограничений.
Сайд-эффекты: для внешних вызовов — журналируйте и проверяйте «повтор» перед вызовом.
10) Наблюдаемость и SLO
Метрики (по очереди/тенанту/ключу):- DLQ rate (msg/s), доля сообщений, средний/медианный «возраст» в DLQ.
- Успех редрайва (%), повторная DLQ-доля.
- Классификация причин: schema, validation, timeout, dependency, unknown.
- p95/p99 латентность обработки основного потока vs в редрайве.
- Размер DLQ, риск переполнения.
- Обязательные теги: `message_id`, `entity_id`, `tenant_id`, `attempt`, `reason`, `redrive_batch_id`.
- Трассировка «ветки DLQ»: от источника до повторного успеха.
- Доля успешно обработанных сообщений ≥ X% за T минут.
- Время расследования и исправления для DLQ-кейса ≤ Y часов.
- Максимальный «возраст» сообщения в DLQ ≤ Z часов (с алертом).
11) Безопасность и соответствие
Доступ по принципу наименьших привилегий: редрайв — только операторам/плейбукам.
Аудит: кто и когда триггерил редрайв/удаление/редактирование метаданных.
Санитация: при переносе в central DLQ удаляйте лишние PII/секреты.
Retention: отдельные сроки хранения и политика удаления для DLQ.
12) Мульти-тенантность
Теги `tenant_id/plan`: различайте лимиты, приоритеты редрайва, отчеты.
Пер-тенантные DLQ или партиции: чтобы «шумный» клиент не забивал общий DLQ.
Биллинг/квоты: учитывайте объем DLQ и стоимость редрайва в usage.
13) Конфигурационные шаблоны (пример)
yaml consumer:
max_attempts: 4 backoff:
strategy: exponential_full_jitter initial_ms: 200 max_ms: 5000 classify_errors:
retryable: [TIMEOUT, DEP_UNAVAILABLE, 5xx]
nonretryable:[SCHEMA_INCOMPATIBLE, VALIDATION_FAILED, DUPLICATE]
concurrency_caps:
per_partition: 8 per_tenant: 50
dlq:
type: topic name: myapp. events. dlq metadata:
include: [reason, stack, attempt, first_seen_ts, last_attempt_ts, schema_version,
tenant_id, entity_id, trace_id, source_topic, partition, offset]
retention_hours: 168 pii_redaction: true
redrive:
mode: batch batch_size: 500 rate_limit_per_sec: 50 priority: low validate_schema_before_redrive: true idempotency:
dedup_ttl_hours: 24 ordering:
by_key: true
14) Операционные плейбуки (runbooks)
1. Аномальный рост DLQ: включить throttling прод-консьюмера, проанализировать топ-причины, проверить релизы/схемы.
2. Schema mismatch: откат/фиксация схемы, миграция адаптера, redrive после проверки.
3. Внешняя зависимость недоступна: пауза ретраев, включить delay-очередь, redrive после восстановления.
4. Повторные DLQ после редрайва: включить «теневой» обработчик/симулятор, проверить идемпотентность, сузить batch.
5. Переполнение DLQ: эвакуация в архив-storage, включить селективный редрайв по ключам/причинам.
15) Тестирование и хаос
Инъекция ошибок: schema-break, валидация, таймауты, 1-на-N «липкие» ошибки.
Массовый редрайв: проверка дозирования и влияния на прод.
Out-of-order последовательности: ensure корректная обработка по ключам.
Коррупция полезной нагрузки: валидация и безопасный отказ.
Восстановление после падения редрайв-воркера: идемпотентность batch-операций.
16) Типичные ошибки
Отсутствие метаданных в DLQ → невозможно кластеризовать причины и безопасно редрайвить.
Массовый редрайв без лимитов → повторная деградация продакшена.
Нет идемпотентности/дедупа → дубли и побочные эффекты.
Смешивание PII в central DLQ без санитации.
Отсутствие схем/контрактов → «сюрпризы» при эволюции сообщений.
Единственный общий DLQ без партиционирования по тенантам/ключам.
Бесконечные ретраи вместо раннего DLQ для non-retryable ошибок.
17) Быстрые рецепты
Обычный бизнес-поток: 3–4 попытки, экспоненциальный backoff c джиттером, ранняя классификация ошибок, DLQ с полными метаданными.
Критичные события (платеж): строгая идемпотентность, короткие таймауты, минимум попыток, быстрый DLQ и ручной разбор.
Массовый редрайв после фикса: small batches (100–500), rate-limit, отдельный пул воркеров, мониторинг успеха > 95% перед увеличением скорости.
Мульти-тенант: пер-тенантные лимиты на редрайв, отчет по топ-клиентам-генераторам DLQ.
Заключение
DLQ — это не «мусорная корзина», а контролируемый контур надежности. Четкие правила попадания, богатые метаданные, идемпотентность и дедупликация, безопасный дозированный редрайв, дисциплина схем и наблюдаемость превращают ядовитые сообщения из угрозы для SLO в управляемый инженерный процесс — с понятными плейбуками, прогнозируемыми затратами и минимальным воздействием на пользователей.