0

I'm working with Spring Cloud Stream and Rabbit, and I used the config defined here to set up a dead-letter queue (DLQ) and it works very nicely.

What I'd like to do is set a maximum amount of times a message goes to the DLQ before being discarded - is is possible to set this via config? If so, how? If not, what should I do to achieve this behaviour?

I'm looking for a code sample for the best answer, preferably in Kotlin (if relevant)

orirab
  • 2,915
  • 1
  • 24
  • 48

1 Answers1

0

That depends whether you're using qurorum queues or not. I don't believe there's a default config for that without quorum queues. However, you should be able to store the redelivery count within a custom header or in the message itself.

Then if you set a MAX_REDELIVERY_COUNT constant in your application, you can check if the message exceeds the maximum number of redeliveries.

If you're not using quorum queues, I'd take a look at this answer: How do I set a number of retry attempts in RabbitMQ?. This answer has quite some good options.

However, when using quorum queues, you can set the delivery-limit option. More info on that can be found here: https://www.rabbitmq.com/quorum-queues.html#feature-matrix.

enter image description here

Edit 1: using custom headers

In order to publish a message with custom headers:

Map<String, Object> headers = new HashMap<String, Object>();
headers.put("latitude",  51.5252949);
headers.put("longitude", -0.0905493);

channel.basicPublish(exchangeName, routingKey,
             new AMQP.BasicProperties.Builder()
               .headers(headers)
               .build(),
               messageBodyBytes);

As found on https://www.rabbitmq.com/api-guide.html#publishing.

The problem is that the headers can't be simply updated. However, you could do this with a workaround. Let's say you want a maximum of 5 retries per message. If the message can't be processed, send it to a DLX. If the message doesn't exceed the maximum retries, read the original headers of the message, update the custom retry count header and resend it to the original queue.

If the message gets in de DLX and does exceed the maximum retry count, send the message as is to the DLX with a different routing key, which is bound to a queue for the "definitive" dead messages.

That'd mean that you would get something like this in a simplified diagram: enter image description here

This is just an idea, I don't know if it'll work for sure, but it's the best that I can think of in your situation.

Edit 2: using the autoBindDlq

It seems like the Spring Cloud Stream Binder for RabbitMQ has this option. In the docs as found on https://github.com/spring-cloud/spring-cloud-stream-binder-rabbit, it says the following:

By using the optional autoBindDlq option, you can configure the binder to create and configure dead-letter queues (DLQs) (and a dead-letter exchange DLX, as well as routing infrastructure). By default, the dead letter queue has the name of the destination, appended with .dlq. If retry is enabled (maxAttempts > 1), failed messages are delivered to the DLQ after retries are exhausted. If retry is disabled (maxAttempts = 1), you should set requeueRejected to false (the default) so that failed messages are routed to the DLQ, instead of being re-queued. In addition, republishToDlq causes the binder to publish a failed message to the DLQ (instead of rejecting it). This feature lets additional information (such as the stack trace in the x-exception-stacktrace header) be added to the message in headers. See the frameMaxHeadroom property for information about truncated stack traces. This option does not need retry enabled. You can republish a failed message after just one attempt. Starting with version 1.2, you can configure the delivery mode of republished messages. See the republishDeliveryMode property.

Jordy
  • 320
  • 2
  • 15
  • Sadly, we don't use quorum queues at the moment. Can you give a code sample for spring java/kotlin to work with the header? – orirab May 09 '22 at 10:22
  • I updated my answer with a new part. I don't know if it'll work for sure in your situation. I hope I helped getting you in the right direction. – Jordy May 09 '22 at 12:23
  • Thanks for adding the impl. Do you think you can add something that is more idiomatic to spring cloud stream? I currently don't have in my code any reference to rabbit at all (just in the config) – orirab May 10 '22 at 08:44
  • You're welcome! I currently do not have any experience with Spring Cloud Stream unfortunately. However, when I quickly take a look at the Github Docs, I see the another simpler option. I don't know if it'll work for your situation, but I updated my answer. – Jordy May 10 '22 at 08:59
  • I'm already using this (as stated in the question), but how do I set up the amount of retries in the DLQ? (that's not the `maxAttempts`, that is for the service itself before routing to the DLQ) – orirab May 10 '22 at 09:31
  • Sorry, I missed that. Then I think we're back on my first edit. I can't help you with any further implementation unfortunately since that isn't my field of knowledge. I hope it works out for you! – Jordy May 10 '22 at 09:35