Home > Software engineering >  Spring-amqp with empty queue name is awaiting_declaration
Spring-amqp with empty queue name is awaiting_declaration

Time:01-12

I want to create a queue with empty name so that the name can be generated by RabbitMQ -

var queue = QueueBuilder
    .durable("")
    .exclusive()
    .autoDelete().build

var binding = BindingBuilder.bind(queue).to(exchange).with(bindingKey).noargs();
Declarables d = new Declarables(queue, binding);

But then calling getActualName returns: spring.gen-vuiRwjOmRkihAE8C72rbmw_awaiting_declaration

d.getDeclarablesByType(Queue.class).get(0).getActualName();

while in rabbitMQ the name is: amq.gen-wpaYnybu9vOdD5v2ej66IQ

In spring-amqp core the Queue constructor declares:

    public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete,
            @Nullable Map<String, Object> arguments) {
    
        super(arguments);
        Assert.notNull(name, "'name' cannot be null");
        this.name = name;
        this.actualName = StringUtils.hasText(name) ? name
                : (Base64UrlNamingStrategy.DEFAULT.generateName() 
  "_awaiting_declaration");
        this.durable = durable;
        this.exclusive = exclusive;
        this.autoDelete = autoDelete;
    }

Why is spring Queue using Base64UrlNamingStrategy and adding "awaiting_declaration" when we want the rabbitMQ name? How can we get the rabbitMQ name and not the spring generated name?

Queue defintion: https://github.com/spring-projects/spring-amqp/blob/d4e0f5c366a7ffae073f608c3766c82064cab3d1/spring-amqp/src/main/java/org/springframework/amqp/core/Queue.java#L98

Reason for this use-case is because of race-condition on queues: "When auto-delete or exclusive queues use well-known (static) names, in case of client disconnection and immediate reconnection there will be a natural race condition between RabbitMQ nodes that will delete such queues and recovering clients that will try to re-declare them. This can result in client-side connection recovery failure or exceptions, and create unnecessary confusion or affect application availability."

https://www.rabbitmq.com/queues.html#properties

Spring suggests using broker-based queues which can result in race condition: https://docs.spring.io/spring-amqp/docs/current/reference/html/#containers-and-broker-named-queues

EDIT: We are are not initiating the connection ourselves, but the admin bean initiates it after d.setAdminsThatShouldDelcare(admin)

    public Declarables someEventsDeclarables(
    @Qualifier("rabbitAdmin") RabbitAdmin admin,
    @Qualifier("AmqpExchange") Exchange exchange
) {
    final var bindingKey = somePrefix  ".*."  someSuffix
    final var cfg = new OurEventsDeclarables(
        exchange,
        "", // no queue name - RabbitMq generates it
        bindingKey,
        true
    );

    final var declarables = cfg.declarables();
    for (Declarable d : declarables.getDeclarables()) {
        d.setAdminsThatShouldDeclare(admin);
        admin.declareQueue();
    }
    return declarables;
}

Running the integration-test which uses the queue results in

org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[spring.gen-QUh8ffN0TimELGG_kF1wFw_awaiting_declaration]

CodePudding user response:

It is not clear what and where you do with that Queue object. You must have a RabbitAdmin bean in the application context. As well as all those Declarable instances has to be declared as beans as well. Then when application context starts, that RabbitAdmin takes all the Declarable beans and declares them against the broker. The further logic around Queue is like this:

DeclareOk declareOk = channel.queueDeclare(queue.getName(), queue.isDurable(),
                            queue.isExclusive(), queue.isAutoDelete(), queue.getArguments());
if (StringUtils.hasText(declareOk.getQueue())) {
    queue.setActualName(declareOk.getQueue());
}

Just creating variables for those types does not initiate any connection to the broker to perform objects creation and bindings between them.

CodePudding user response:

You are using admin.declareQueue() (and discarding its result) instead of admin.declareQueue(Queue queue).

The first method just declares a broker named queue; the update to the actual name requires the Queue object (so it can update the name).

  • Related