Skip to content

Documented redis consumer group relation #11869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions messenger.rst
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,32 @@ config and start your workers:

See the `Supervisor docs`_ for more details.


.. tip::

To get a deterministic worker name like for e.g. the Redis transport, you may pass the process number as an
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I miss information why I would like to do this.

We do this to leverage the Redis stream "consumer group" feature, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. To me the why doesn't really matter here. It's just a tip and as we're in the Subervisor section here I don't want to get specific about Redis. I'm sure there are other use cases where you may want to set an environment variable.

environment variable.

.. code-block:: ini

;/etc/supervisor/conf.d/messenger-worker.conf
[program:messenger-consume]
[…]
environment = REDIS_CONSUMER=consumer-%(process_num)d

You can then use it in your messenger configuration:

.. configuration-block::

.. code-block:: yaml

# config/packages/messenger.yaml
framework:
messenger:
transports:
redis: 'redis://localhost:6379/messages/symfony/%env(REDIS_CONSUMER)%'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wound use a transport value like this. I would have that in an environment variable, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand your question here, can you elaborate maybe? 😊

Copy link

@sneakyvv sneakyvv Oct 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @Nyholm meant that redis: 'redis://localhost:6379/messages/symfony/%env(REDIS_CONSUMER)%' would actually be redis: '%env(MESSENGER_TRANSPORT_DSN)%' or something like that.
However, for the purpose of this doc it's more appropriate as it is now.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REDIS_CONSUMER Environment variable not found? How I can get this variable from supervior configuration?

Copy link
Member

@Nyholm Nyholm Jul 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nyholm What I did is: I added the line environment = REDIS_CONSUMER=consumer-%(process_num)d to the configuration file /etc/supervisor/conf.d/messenger-worker.conf, now my problem is in Symfony application the variable REDIS_CONSUMER is not defined, so how I can defined it?



.. _messenger-retries-failures:

Retries & Failures
Expand Down Expand Up @@ -900,6 +926,40 @@ serializer How to serialize the final payload ``Redis::SERIALIZER_PHP`
``Redis::OPT_SERIALIZER`` option)
================== =================================== =======

.. tip::

At some point you'll likely want to scale the number of workers working on your queue.
Make sure you assign the correct ``consumer`` and ``group`` values in that case.
The more likely case is that you want every worker to work on the queue independently, reducing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it likely?

reducing the time needed to process the pending messages

I just googled about redis stream and their consumer groups. Maybe a link to redis docs should be added here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was hesitant getting too much into how Redis streams work because that's already documented in the Redis docs (e.g. https://redis.io/topics/streams-intro). Actually that's the first link you get when you google for "redis streams" :)

Why is it likely?

Why scaling is likely? If one worker was enough, a simple database queue would probably be enough. I don't think one would choose Redis if the load wasn't bigger :)

the time needed to process the pending messages. In that case, every single worker
must have a different ``consumer`` option value so Redis can identify the different workers.
Your workers should thus be configured e.g. as follows:

* Worker 1: redis://localhost:6379/messages/symfony/consumer-1
* Worker 2: redis://localhost:6379/messages/symfony/consumer-2
* Worker 3: redis://localhost:6379/messages/symfony/consumer-3
* […]

The less likely case would be if you wanted to have every single worker to process every single message.
That means messages would be processed multiple times. In that case, you must have different ``group``
configurations:

* Worker 1: redis://localhost:6379/messages/symfony-1/consumer
* Worker 2: redis://localhost:6379/messages/symfony-2/consumer
* Worker 3: redis://localhost:6379/messages/symfony-3/consumer
* […]

.. caution::

Be careful about the ``consumer`` and ``group`` names you choose. It might be tempting to use e.g. the ``HOSTNAME``
environment variable in orchestrated, containerized environments (Docker Swarm, Kubernetes, etc.) to get a unique
host name. However, they usually contain a random unique identifier which means if you destroy a container while it was
working on a message, this message will remain in pending state forever as it is very unlikely there's ever going
to be another worker with exactly the same ``HOSTNAME`` as the one you destroyed. In other words, you have to
make sure you're using deterministic values such as ``worker-1``, ``worker-2`` etc.
In case you are using Kubernetes to orchestrate your containers, consider using a ``StatefulSet`` rather than
a ``Deployment`` for example.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s shorten this :). These cautions are great for the few people who have this case, but they tend to “weigh down” the docs over time.

I think adding a comment in the code blocks above might be enough:

For Kubernetes, use a StatefulSet to avoid randomly host names

Copy link
Contributor

@toooni toooni Jul 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This caution block can be removed since abandoned messages are now claimed: symfony/symfony#35384


In Memory Transport
~~~~~~~~~~~~~~~~~~~

Expand Down