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
Introduce background workers #5405
Conversation
a627bce
to
c347813
Compare
Notes for myself:
|
# Conflicts: # core-bundle/src/Cron/Cron.php # core-bundle/tests/Cron/CronTest.php
d617450
to
872da11
Compare
872da11
to
084bb04
Compare
8d9e90b
to
e0ad5c3
Compare
fc6f012
to
63eecb3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I'm able to fully review this without any messenger knowledge, but it looks good in general. One thought though:
Why do we need the full worker configuration in the Contao bundle? I mean we are setting up three priority queues for devs that "just want it to work". If you have other needs, use your own transport. Why would I ever reconfigure the workers? Also, if I want to run workers differently, because I might have special needs, wouldn't I just want to disable the cron jobs and run the actual messenger:consume
command with my priority etc?
Not sure I understand all the questions here but let me try.
Why do we need it? Because Contao uses it itself as of this PR. The
You mean reconfigure the
I think you're mixing up things. "Priority" is not a thing that exists in Symfony Messenger. It's just a different transport and we make it a priority thing because |
I don't think that's what I meant 😂 I meant why do we need the stuff in |
Sorry but I have absolutely no clue what you're talking about. |
core-bundle/src/Messenger/Transport/AutoFallbackTransportFactory.php
Outdated
Show resolved
Hide resolved
core-bundle/src/Messenger/MessageHandler/SearchIndexMessageHandler.php
Outdated
Show resolved
Hide resolved
core-bundle/src/Messenger/MessageHandler/SearchIndexMessageHandler.php
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice 😎
Thank you @Toflar. |
This introduces real background workers based on cronjobs and Symfony Messenger.
Motivation
Modern applications often require background workers in order to move more heavy processes in the background. We already have things in place that should better be done asynchronously in order to speed up the front end:
This PR also opens up the opportunity for extension developers to e.g. put other heavy workloads in the background. Here are some ideas:
It's also relevant for us, if we want to work on a more complex search index because it might (we don't know yet) increase the time it takes to index a page for example.
Concept
The concept builds on top of Symfony Messenger. This means you can technically route your messages to RabbitMQ or any other transport. Contao ships with the Doctrine based transport by default which means there are 0 additional requirements.
In order to make this work, however, we need real Cronjobs. Which we don't want to make a system requirement because for simple/small sites, we probably don't need real cronjobs. Also, we don't need them for simple development/testing instances etc.
Here's how it works from a developer's perspective (default Contao Managed Edition):
LowPriorityMessageInterface
,NormalPriorityMessageInterface
andHighPriorityMessageInterface
) and pass it to the message bus (MessageBusInterface
). Then you're good to go and build your message handler. Contao takes care of the rest.SearchIndexListener
, for example, this means that nothing changes at all if you do not have any worker running. Contao detects that there's no worker and indexes synchronously on every request like it does today. If, however, you configured a real worker, things will automatically happen asynchronously in the back end, making your front end considerably faster and freeing up your FPM childs etc.Here's how it works from a Contao user's perspective (default Contao Managed Edition):
contao:cron
) as documented. Done, Contao does the rest - magic! 🥳Technical details and remarks:
messenger:consume
commands and started via our new async cronjobs support.As you can see, we configure the routing for the Symfony Messenger in such a way that, our interfaces map to one of our three queues (
contao_prio_low
,contao_prio_normal
andcontao_prio_high
). This has a major advantage: As a developer, you don't need to register any config. You can just implement the desired interface and that's it. So you can haveand
and that's it. Contao automatically does the rest for you and ensures that if there is no worker running, the
sync
transport will be used. So your message is processed - either in the optimized way when workers are running - or not.Using the interface is a very nice defaul. It provides the mentioned DX and it still allows manual configuration adjustment if needed. So you could addjust your
framework.messenger.routing
config like so, if you like:The concrete class name always wins over the interface implemented.
Even the transport logic is completely independent from Contao. The automatic fallback to sync is implemented by a custom
contao_auto_fallback
which you are free to use. So instead of using the Contao worker framework and auto detection magic, you can disable it all and configure Contao the way you like. Or you can combine as you like:Maximum flexibility! 😎
To finish, let's look at our default workers configuration in the Managed Edition:
As you can see, I've configured autoscaling for all 3 queues but I've configured them in a way, that there are never more than
10
workers per queue. Also, you can see that--sleep
differs. Important note:sleep
does not define the sleep time between all messages. Instead, it only sleeps when idle. This means it only sleeps e.g.20
seconds if the last time it asked, there was no message. If there was one, it will ask for the next message immediately after it finished processing. Hence, I configured them to5
,10
and20
seconds. In other words: In worst case, it takes 5 seconds for a message to be processed if it was on the "high prio" queue and 20 on the "low prio".Pretty proud of this implementation, I hope it will serve us well and allow for some cool new use cases for extensions 😎