A poison message is a message that fails every time it is processed. Common causes:
- invalid JSON
- an unknown schema version
- a reference to data that does not exist
- input that triggers a bug
If you retry it forever, it causes damage:
- In Kafka, it blocks its partition.
- In RabbitMQ, it loops in the queue.
- Consumers may crash again and again.
First, classify the error.
- Transient failures are temporary, such as timeouts, a downstream 503, or a deadlock. Retry them with exponential backoff (double the wait each time) and jitter (a small random extra wait).
- Permanent failures will never succeed, such as validation errors, deserialization errors, or business rule violations. Send them to the dead-letter queue (DLQ) at once. A DLQ is a separate queue that holds failed messages for later inspection.
Then protect the system:
- Cap retries with a maximum attempt count.
- Send messages that still fail to the DLQ, together with the error, stack trace, attempt count and source.
- Alert on DLQ depth (how many messages are in it).
- Investigate, fix the cause, then redrive the messages (send them back to be processed again).
- Catch errors per message, so one failure does not crash the whole consumer.
- A poison message fails on every attempt.
- Retry transient errors with exponential backoff and jitter.
- Send permanent errors straight to the DLQ.
- Cap the number of attempts, and attach error details to dead-lettered messages.
- Alert on DLQ depth, fix the cause, then redrive the messages.
Read more
- Dead letter channel (https://www.enterpriseintegrationpatterns.com/patterns/messaging/DeadLetterChannel.html)enterpriseintegrationpatterns.com
- Dead letter exchanges (https://www.rabbitmq.com/docs/dlx)rabbitmq.com