0

I use Symfony Messenger with the RabbitMQ adapter in my application. The logic of the application is that messages from the same queue must be executed sequentially, one after the other (the order is not important), even if the queue is listened to by multiple consumers. In other words, the queue should not give out other messages if at least one is unacked.

To solve the problem, I found in the documentation an option such as x-single-active-consumer (https://www.rabbitmq.com/consumers.html#single-active-consumer).

The configuration is like this:

  my-queue:
    dsn: amqp://guest:guest@localhost:5672/%2f/my-queue
    options:
      queues:
        my-queue:
          arguments:
            x-single-active-consumer: true

The UI shows that the queue was created with the SAC (single-active-consumer) flag, which confirms that the configuration is correct. However, when several consumers are started, they receive tasks in parallel, without waiting for the previous task to finish, in fact, as if the single-active-consumer flag wasn't set at all.

Can you tell me what I'm doing wrong?

WindBridges
  • 693
  • 2
  • 11
  • 23

1 Answers1

0

Preconditions:

  • symfony/messenger 4.4.*
  • RabbitMQ 3.8 +

Only what I needed to do was to remove queue in Rabbit Admin and let consumer to register new one.

It is not possible to enable single active consumer with a policy. Here is the reason why. Policies in RabbitMQ are dynamic by nature, they can come and go, enabling and disabling the features they declare. Imagine suddenly disabling single active consumer on a queue: the broker would start sending messages to inactive consumers and messages would be processed in parallel, exactly the opposite of what single active consumer is trying to achieve. As the semantics of single active consumer do not play well with the dynamic nature of policies, this feature can be enabled only when declaring a queue, with queue arguments

https://www.rabbitmq.com/consumers.html#single-active-consumer

You can verify setup from rabbitmq container (or to use rabbitmqadmin whenever you have your rabbit)

rabbitmqadmin export rabbit.definitions.json
...
"queues": [
    {
        "name": "some-queue",
        "vhost": "/",
        "durable": true,
        "auto_delete": false,
        "arguments": {
            "x-single-active-consumer": true <--------------
        }
    }
],
"exchanges": [
    {
        "name": "my-exchange",
        "vhost": "/",
        "type": "direct", <---------------------------------
        "durable": true,
        "auto_delete": false,
        "internal": false,
        "arguments": {}
    }
}
...

edit #1: You many also want to check that exchange is type of direct. Default by symfony/messenger is fanout, which distributes messages to all available consumers.

The fanout exchange is very simple. As you can probably guess from the name, it just broadcasts all the messages it receives to all the queues it knows.

https://www.rabbitmq.com/tutorials/tutorial-three-python.html

config/messenger.yml

...
my-binding:
    dsn: 'some-dsn'
    options:
        exchange:
           name: my-echange
           type: direct <-----------------------------

mimros
  • 523
  • 6
  • 8