rabbitmq when to use basic reject over basic nack? - rabbitmq

Basic nack provides facility to return negative acknowledgement for one or multiple messages.
Basic reject has facility to return negative acknowledgement for only one message.
Do we have any use case where we definitely need basic reject?

With rabbitmq you can always nack over reject. Logically there is no explicit need to use reject, since nack can do anything reject can. As you said, both of these work for one message, but nack also for multiple.
Also nack is introduced by rabbitmq (so it's an extension), and is not part of amqp 0.9.1 specs.

The answer by #cantSleepNow is correct, I would also like to add one more difference which is in their default behaviour.
By default, nack will put the message back in the queue for later handling. You can change the setting to not re-queue with nack.
With reject, by default, the message is not re-queued by RabbitMQ but will drop the message from the queue entirely.

Related

RabbitMQ publisher confirm order

I am writing a wrapper around the .NET RabbitMQ library and have a question that isn't answered in the documentation as far as I can see, and I can't think of a way to verify it experimentally.
The documentation here states the following:
In most cases, RabbitMQ will acknowledge messages to publishers in the
same order they were published (this applies for messages published on
a single channel). However, publisher acknowledgements are emitted
asynchronously and can confirm a single message or a group of
messages. The exact moment when a confirm is emitted depends on the
delivery mode of a message (persistent vs. transient) and the
properties of the queue(s) the message was routed to (see above).
Which is to say that different messages can be considered ready for
acknowledgement at different times. This means that acknowledgements
can arrive in a different order compared to their respective messages.
Applications should not depend on the order of acknowledgements when
possible.
So confirmations can be received out of order which I can cope with OK. However, how does this play with the Multiple flag that can be used?
Imagine the following scenario: I send messages 1,2,3,4 and 5. 1 fails and 2-5 succeed. The broker sends me a Nack for 1 and a single Ack for 5 with Multiple set to true. If order were guaranteed then I know I would get the Nack for 1 first and then I would know that the Ack was for 2-5. However, if they could be out of order, then I would assume, as I hadn't had a confirm for 1, that the Ack was for 1-5, and then when the Nack came in, it would be too late.
Are there guarantees somewhere where this scenario cannot happen?
This is the answer from the Google Group mentioned by Alex in the comments. Basically, this scenario can't happen.
Hi,
If you get a single nack for 1 then an ack for 5 with multiple=true this means 2-5 were all successful.
If 1 is delayed but 2-5 are not you should get 4 multiple=false acks with sequences 2-5 then a nack for 1.
It would be nicer if ack frames used ranges instead of the multiple flag but we are where we are.
Cheers
Karl

How to achieve at-least-once delivery guarantee with RabbitMQ and Spring AMQP without using transactions?

This document https://www.rabbitmq.com/reliability.html says it is possible to achieve at-least-once guarantee with RabbitMQ without using transactions. The problem with transactions is that they are slow, so the throughput drops drastically (see sections Publisher Confirms in https://www.rabbitmq.com/confirms.html). The option is to use Consumer Acknowledgements and Publisher Confirms.
On the other hand, Spring AMQP is able to handle Consumer Acknowledgements automatically using this configuration:
spring.rabbitmq.listener.acknowledgeMode=AUTO
For Publisher Confirms this is the configuration:
spring.rabbitmq.publisherConfirms=true
My doubt is whether these two properties are enough to guarantee at-least-once delivery or if I need to do anything else.
You will need to add a confirm callback to the rabbit template (either a custom one or the one configured by boot) to get the confirmations. You can add correlation data to the message sends so that you can correlate the confirm with the send.
See the documentation.
For Publisher Confirms (aka Publisher Acknowledgements), the template requires a CachingConnectionFactory that has its publisherConfirms property set to true. Confirms are sent to to the client by it registering a RabbitTemplate.ConfirmCallback by calling setConfirmCallback(ConfirmCallback callback). The callback must implement this method:
void confirm(CorrelationData correlationData, boolean ack, String cause);
The CorrelationData is an object supplied by the client when sending the original message. The ack is true for an ack and false for a nack. For nack s, the cause may contain a reason for the nack, if it is available when the nack is generated. An example is when sending a message to a non-existent exchange. In that case the broker closes the channel; the reason for the closure is included in the cause. cause was added in version 1.4.
Only one ConfirmCallback is supported by a RabbitTemplate.

Returning NACKed requests in RabbitMQ work queues

I'm trying to implement a work queue architecture using RabbitMQ. I have a single sender application and multiple consumers.
I use manual ack on the consumers, so in case of failure in handling a request, it will be re-queued for another consumer to handle.
I was wondering what would happen if all the consumers return nack on a specific request. Is there a way to recognize this behavior and mark the request as 'dead' so it's rerouted to the dead letter exchange? In such a case, I'd like to have a separate consumer open on the queue bound to the dead letter exchange and receive all the messages that failed to be handled by any consumer (for logging purposes or executing this request's task locally, without distributed consumers).
Another question I had. When requeueing the request upon receiving NACK from a consumer, will it try to send this request to other consumers or will it try to send to the first available, even if it's the one that already nacked the request?
Thanks
no there is no such a feature in RabbitMQ. You may handle exceptions, and for specific exception send message to the dead queue or if know maximum time that message must live, configure TTL on queue.
if you nack message, it will go to the next AVAILABLE consumer

Does EasyNetQ support nack?

What I'm really trying to do is leave the message on the queue in the case where it is rejected by the current consumer. In RabbitMQ I could send a NACK to accomplish this. Is NACK supported in EasyNetQ? Is there another way to achieve the behavior I'm looking for?
Update: not a lot of responses, so I'm wondering how people are generally handling the lack of NACK in EasyNetQ. Not having the equivalent of basic.reject limits consumers to "I can always process every message" scenarios. I suppose consumers could throw a specific "rejected" exception to cause EasyNetQ to dequeue the message to the error queue, and I could requeue messages with those errors. Anyone else have other workarounds in place?
I used EasyNetQ for almost a year, but no matter how we tweaked it (amongst other things added our own implementation of IConsumerErrorStrategy) I never really got it to work the way I wanted. The fact that it is single threaded gave us some unexpected behaviour (sometimes deadlocks) when performing RequestAsync while in a SubscribeAsync handler.
The solution for us was to move from EasyNetQ. After working with the official RabbitMq Client for a while, I spent a few days writing a super thin client on top of that. It is influenced by EasyNetQ and supports most of the concepts that EasyNetQ has. However, I added some neat features like pluggable message contexts. I think that the Nack feature of IAdvancedMessageContext that I just added can be something for you:
var client = service.GetService<IBusClient<AdvancedMessageContext>>();
client.RespondAsync<BasicRequest, BasicResponse>((req, ctx) =>
{
ctx?.Nack(); // the context implements IAdvancedMessageContext.
return Task.FromResult<BasicResponse>(null);
}, cfg => cfg.WithNoAck(false));
If you're interested you can read more about it at the Github page (especially the NackTests.cs).
I think you can change the behavior by implementing your own IConsumerErrorStrategy:
https://github.com/EasyNetQ/EasyNetQ/blob/master/Source/EasyNetQ/Consumer/DefaultConsumerErrorStrategy.cs
But if you need that kind of control you might consider just using the RabbitMQ client directly?
It sounds like you are trying to handle failures. You can NACK a message, but that means it sits at the head of the queue. Great, but then it means that you could end up with a bunch of messages that are truthfully unable to be processed, and you will be unable to actually process real messages.
The solution that I have always used when using RabbitMQ is to utilize the default error handling of EasyNetQ, and have a separate application to resend messages. That is, when an exception is captured in RabbitMQ, it routes the message to a queue called "EasyNetQ_Default_Error_Queue". You are able to override this name and have different queues go to different error queues, but for now let's stick with the default. You can then have a Windows Service/Azure Worker role reading these messages, and working out what to do. That may include having a "RetryCount" on your message envelope/wrapper to make sure that it only loops around so many times. All in all, it's going to be a bit of work.
What you are finding, is what many people run into when using RabbitMQ/EasyNetQ. She's pretty raw.

RabbitMQ - an example for a "Nack" need

When do I need to respond with a nack on my RabbitMQ listener?
So far I have never witness an error in the message over the network.
I guess the network layer should take care of it, no?
If I get an error message from the publisher a nack will just leave it in the queue, wont it?
Thanks.
A NACK could be sent in case the consumer was (maybe temporarily) not able to process the delivery. The messages will be redelivered by the server to any available consumer (potentially including the original) and processing could be tried again.
It is different from REJECT in the sense that rejected deliveries are not resend to the rejecting consumer (redelivery happens on a different channel).