Search code examples
javaspring-bootrabbitmqspring-rabbit

How to know if a message has been ack-ed / nack-ed?


I'm trying to know when a message has been accepted (ack) or not (nack) using RabbitMQ and Spring Boot.

I want to send a message into a queue (via exchange) and check if the queue has been accepted the message. Actually I want to send to two different queues, but it is not important, I'm assuming if it works for one of them will work for the other too.

So I've tried something like this using CorrelationData:

public boolean sendMessage(...) {
    CorrelationData cd = new CorrelationData();
    this.rabbitTemplate.convertAndSend(exchange, routingKey, message, cd);
    try {
        return cd.getFuture().get(3, TimeUnit.SECONDS).isAck();
    } catch (InterruptedException | ExecutionException | TimeoutException e ) {
        e.printStackTrace();
        return false;
    }
}

The line cd.getFuture().get(3, TimeUnit.SECONDS).isAck() should get false is value has not been ack into the queue I think. But this is always true, even if routingKey doesn't exists.
So I'm assuming this piece of code is checking the message has been send into the exchange and exchange says "yes, I've recived the message, it has not been routed, but I've recived it".

So, I've looked for other ways into Rabbit/Spring documentation but I can't get the way.

And, explaining a little more, that I want is:

Into Spring Boot code I receive a message. This message has to been send to other queues/exchange, but can't be removed from the current queue (i.e. acked) until other two queues confirm the ack.

I have manual ack and as a little pseudo-code I have this:

@RabbitListener(queues = {queue})
public void receiveMessageFromDirect(Message message, Channel channel,
                                     @Header(AmqpHeaders.DELIVERY_TAG) long tag){
    boolean sendQueue1 = sendMessage(...);
    boolean sendQueue2 = sendMessage(...);

    if(sendQueue1 && sendQueue2){
        //both messages has been readed; now I can ack this message
        channel.basicAck(tag, false);
    }else{ 
        //nacked; I can't remove the message util both queue ack the message
        channel.basicNack(tag,false,true);
    }

I've tested this structure and, even if the queues don't exists, values sendQueue1 and sendQueue2 are always true.


Solution

  • The confirm is true; even for unroutable messages (I am not entirely sure why).

    You need to enable returned messages (and check that it is null in the CorrelationData after the future completes - correlationData.getReturnedMessage()). If it's not null, the message wasn't routable to any queue.

    You only get nacks if there is a bug in the broker, or if you are using a queue with x-max-length and overflow behavior reject-publish.