Dead-letter Queue Monitor
Overview
When a message has exhausted its 3 retries or was classified as a permanent error from the start, it is dead-lettered into <queue>.dlq. Left unattended, those messages simply pile up unnoticed.
The DLQ Monitor is a dedicated consumer running on the main profile. It subscribes to every monitored DLQ, logs the full detail of each dead message (payload, x-death, x-retry-count), and batches those events into chat webhook alerts with a cooldown so the channel is never spammed.
Business Flow
- When
runMainstarts, it constructs aDLQMonitorand runs it as a goroutine separate from the main registrar. - The monitor opens one consumer per DLQ, following the
MonitoredDLQQueueslist of 37 queues — all ofAllQueuesexceptmessage_received_trigger, which is intentionally excluded. - Each time a message lands in a DLQ, the monitor:
- Unpacks and logs the source queue name, the payload, the error, and the
x-deathandx-retry-countheaders - Appends an entry to
pendingAlertsrecording the queue, payload, and error
- Unpacks and logs the source queue name, the payload, the error, and the
- Once
DLQ_ALERT_COOLDOWN_MS(5 minutes by default) has elapsed since the previous alert, all pending entries are combined into a single message, sent toDLQ_ALERT_WEBHOOK_URL, and the buffer is cleared. - The monitor always acks, whether or not the payload parsed successfully, so that no endless redelivery loop can form inside the DLQ.
Key Files & Functions
internal/mq/dlqmonitor.goClient.NewDLQMonitor()— builds the monitor from configuration (DLQ_ALERT_WEBHOOK_URL,DLQ_ALERT_COOLDOWN_MS)DLQMonitor.Run(ctx)— starts consumers for every DLQ concurrentlyDLQMonitor.consume(ctx, dlqQueue, suffix)— the consume loop for a single DLQdlqAlert— the struct representing one alert entry
internal/mq/topology.go—MonitoredDLQQueues(), the set of monitored queuescmd/worker/main.go— the part ofrunMain()that constructs and starts the monitor- Subscribed queues are every
<queue>.dlq, where the suffix comes fromRABBITMQ_DLQ_SUFFIX(default.dlq), all bound to theline_exchange_dlqexchange
Connections to Other Services
- RabbitMQ — consumes from all DLQs; the topology must be asserted first
- Chat webhook (
DLQ_ALERT_WEBHOOK_URL) — an HTTP POST with a{"text": ...}body, compatible with both Google Chat and Slack, using a 5-second client timeout. If the URL is not configured, the monitor logs only and sends no alerts. - No database access — this feature is purely observability
- Directly related to RabbitMQ Queue Topology & Retry / Dead-letter Mechanics