The poison message problem

RabbitMQ Course · lesson 9 of 15 · 5 min read

One message that can never succeed will saturate a consumer forever unless you count attempts.

Open this lesson in the learning hub

Key points

  • Rejecting with requeue true puts a bad message straight back - that is a hot redelivery loop.
  • The redelivered flag only says it was delivered before; it is a boolean, not a counter.
  • Quorum queues count attempts with x-delivery-limit, which defaults to 20 in RabbitMQ 4.0.
  • Classic queues have no delivery counter, so count in a header or read the x-death count yourself.
  • A parking lot queue holds what will never succeed and should page a human rather than retry forever.
  • Spring AMQP listener retry is off by default; enable it and add a RepublishMessageRecoverer.

Example

@Bean
RetryOperationsInterceptor retryInterceptor() {
    return RetryInterceptorBuilder.stateless()
        .maxAttempts(4)                                  // 1 try + 3 retries
        .backOffOptions(1_000, 2.0, 20_000)              // 1s, 2s, 4s ... cap 20s
        .recoverer(new RepublishMessageRecoverer(
             rabbitTemplate, "orders.dlx", "parking-lot"))
        .build();
}

// and refuse to requeue by default, or every failure loops:
factory.setDefaultRequeueRejected(false);

Bound the attempts somewhere - x-delivery-limit, an x-death count or a header - or one message runs forever.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the RabbitMQ Course course, and every lesson in it is listed on the RabbitMQ Course contents page.