Should a RabbitMQ consumer nack-and-requeue or dead-letter a message when local processing times out?
0 reputation · 24 Nov 2023, 01:45 UTC
I am designing a RabbitMQ consumer (RabbitMQ 3.12+, .NET and Java clients) that processes messages with manual acknowledgements and a bounded basic.qos prefetch. Each delivery may occasionally exceed a local processing deadline, and I need an explicit, deterministic policy for what the consumer does at that point rather than leaving messages unacked.
The tension I cannot resolve: a nack with requeue fits transient faults (a slow downstream dependency), but if the failure is deterministic the message can loop hot and indefinitely. Rejecting to a dead-letter exchange fits poison messages, but RabbitMQ does not natively count delivery attempts, so a transient timeout could prematurely park a message that would have succeeded on retry. I know the redelivered flag exists and that x-death headers on the DLX can be inspected, but the broker cannot infer my business intent.
Constraints: processing is idempotent, side effects complete before ack, and connection recovery is enabled so duplicates are already possible.
Which criteria should decide between requeue and dead-letter on a local timeout? Is bounding retries via x-death inspection on the DLX the accepted way to cap attempts, or should attempt counting live in application headers? How do I verify the policy behaves correctly when a consumer is killed mid-processing?