Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Messenger] Move container resetting after receiver acknowledging#43133

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fabpot merged 1 commit intosymfony:5.4fromupyx:fix-container-resetting
Sep 27, 2021

Conversation

@upyx
Copy link
Contributor

QA
Branch?5.4
Bug fix?yes
New feature?no
Deprecations?no
TicketsFix#43114
LicenseMIT
Doc PRnope

It fixes the#43114 issue and similar potential bugs. TheResetServicesListener lean on events sequence: theAbstractWorkerMessageEvent to save the actual receiver name, then theWorkerRunningEvent to check that name and reset the container. It may look fragile, so let's discuss it in the issue.

okwinza reacted with thumbs up emoji
@okwinza
Copy link
Contributor

okwinza commentedSep 22, 2021
edited
Loading

Waiting until the worker is completely done handling a message before actually resetting services sounds good to me. 👍

Things to keep in mind:

  • Services should only be reset after the worker is done with a message.
  • Services should not be reset more once between each message.

As for the implementation part, this could also benefit from#42335 to easily check the worker transport name.
So you could just do something like this:

$event->getWorker()->getWorkerMetadata()->getTransportName() ===$this->transportName;
upyx reacted with thumbs up emoji

lyrixx added a commit that referenced this pull requestSep 22, 2021
…kwinza)This PR was merged into the 5.4 branch.Discussion----------[Messenger] Add `WorkerMetadata` to `Worker` class.| Q             | A| ------------- | ---| Branch?       | 5.4| Bug fix?      | no| New feature?  | yes| Deprecations? | no| Tickets       |Fixes#37736| License       | MIT| Doc PR        | -At the moment, there is no clean way to access the values of `transportNames` or recently introduced `queueNames` that the worker was configured with, although such data might be quite useful for logging/monitoring or other tasks.This PR attempts to fix that by adding a new and extensible way to provide additional information about a particular `Worker` object.So far, the following PRs could benefit from this change:-#43133-#42723**Use case example:**----- As I developer- When a message was consumed from transport with name `async`.- And the worker state is `idle`.- Then I want to reset services.**Before this PR**, the only solution not relying on using Reflection API would look like this:```php    private $servicesResetter;    private $receiversName;    private $actualReceiverName = null;    public function __construct(ServicesResetter $servicesResetter, array $receiversName)    {        $this->servicesResetter = $servicesResetter;        $this->receiversName = $receiversName;    }    public function saveReceiverName(AbstractWorkerMessageEvent $event): void    {        $this->actualReceiverName = $event->getReceiverName();    }    public function resetServices(WorkerRunningEvent $event): void    {        if (!$event->isWorkerIdle() && \in_array($this->actualReceiverName, $this->receiversName, true)) {            $this->servicesResetter->reset();        }        $this->actualReceiverName = null;    }    public static function getSubscribedEvents(): array    {        return [            WorkerMessageHandledEvent::class => ['saveReceiverName'],            WorkerMessageFailedEvent::class => ['saveReceiverName'],            WorkerRunningEvent::class => ['resetServices'],        ];    }```**With this PR**, one could simply use this to retrieve the transport name.```php$event->getWorker()->getWorkerMetadata()->getTransportName() === $this->transportName;```So the whole solution would look like this:```php    private $servicesResetter;    private $receiversName;    public function __construct(ServicesResetter $servicesResetter, array $receiversName)    {        $this->servicesResetter = $servicesResetter;        $this->receiversName = $receiversName;    }    public function resetServices(WorkerRunningEvent $event): void    {        $actualTransportName = $event->getWorker()->getWorkerMetadata()->getTransportName();        if (!$event->isWorkerIdle() || !in_array($actualTransportName, $this->receiversName, true)) {            return;        }        $this->servicesResetter->reset();    }    public static function getSubscribedEvents(): array    {        return [            WorkerRunningEvent::class => ['resetServices'],        ];    }```Commits-------583f85b [Messenger] Add WorkerMetadata to Worker class
@lyrixx
Copy link
Member

lyrixx commentedSep 22, 2021
edited
Loading

I just merged#42335. Can you rebase to use the new feature? Thanks

And please, fix fabpot issue :)

okwinza reacted with thumbs up emoji

@upyx
Copy link
ContributorAuthor

$event->getWorker()->getWorkerMetadata()->getTransportName() === $this->transportName;

@okwinza
It won't work as expected because theWorkerMetadata has an array of transport names. The part of transports can be not resettable. We can either restrict using resettable and not resettable transports in the same worker or rely on the event's sequence. I'd prefer to use new metadata, and I think it will be good to restrict resetting services on a receiver when the different one doesn't expect that behavior.

So it'll take a while to implement.

@lyrixx
Copy link
Member

lyrixx commentedSep 24, 2021
edited
Loading

What about moving the ack before the dispatch ?

Edit: this is a bad idea. But I think the best solution is to create a new event (same for nack)

@upyx
Copy link
ContributorAuthor

@lyrixx:

What about moving the ack before the dispatch ?

Edit: this is a bad idea. But I think the best solution is to create a new event (same for nack)

We have the event already. We have metadata, transport names, and other things.

I've been investigating the topic more. I've realized that resetting services is a worker's job rather than a transport's because service leak is a consumer problem and isn't dependent on transport. And there aren't any consumers on the "sync" transport.

In my opinion, the best alternative is resetting services after every message. The only drawback is decreasing performance, so we can provide an option to disable it. It also will break compatibility, so we should enable that option by default and add an inversed one.

I consider removing thereset_on_message parameter from a configuration and adding two options to themessenger:consume command:--reset-services-on-message and--no-reset-services-on-message. It's also good to deprecate running the command without explicitly indicating which one to use and change defaults in 6.0.

@upyxupyx marked this pull request as draftSeptember 24, 2021 21:15
@upyxupyxforce-pushed thefix-container-resetting branch 2 times, most recently from987baef to214a63aCompareSeptember 24, 2021 23:31
@upyx
Copy link
ContributorAuthor

I've made a working draft as an example. If it's OK, I'll continue.

Copy link
Member

@lyrixxlyrixx left a comment
edited
Loading

Choose a reason for hiding this comment

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

I like it. But I realize we can simplify the work for end user.
We can move the command configuration to the bundle:
Like that, user don't have to update all consumer recipies (ansible, helm, etc). Only the code needs to be updated.

framework:messenger:reset_on_message:true# add a deprecation if not settransports:....

WDYT?

I agree with you on the fact that is should be the default, and I don't see a use case where a transport should be reset but not another one.

And BTW Fab wanted to reset the container all the time too:#41163 (comment)

@okwinza
Copy link
Contributor

framework:messenger:reset_on_message:true# add a deprecation if not settransports:....

Can't we just set it tofalse by default for BC and then change it totrue and make it mandatory in 6.0?
Also it might be useful to add a runtime--no-reset option tomessenger:consume as well.
WDYT?

@lyrixx
Copy link
Member

Can't we just set it tofalse by default for BC and then change it totrue and make it mandatory in 6.0?

it's not the best way to handle migration path.
5.4: deprecation if not set, telling it'll be true in 6.0, default to false for BC
6.0: default to true, no deprecation, optin

Also it might be useful to add a runtime--no-reset option tomessenger:consume as well.

do you have a use case for overriding the value? (less code == less bug)

okwinza reacted with thumbs up emoji

@okwinza
Copy link
Contributor

okwinza commentedSep 25, 2021
edited
Loading

5.4: deprecation if not set, telling it'll be true in 6.0, default to false for BC
6.0: default to true, no deprecation, optin

👍 Agreed.

Also it might be useful to add a runtime--no-reset option tomessenger:consume as well.

do you have a use case for overriding the value? (less code == less bug)

Since container resetting affects conformance, itmight make sense to be able to override it for additional performance gain in some particular consumers.

But this is just an idea and it's totally out of scope of this PR and should be added later down the road, if at all.

@lyrixx
Copy link
Member

Okay let's go 👍 do you wanna to finish the pr ?

Thanks for discussion 😊

@upyx
Copy link
ContributorAuthor

upyx commentedSep 25, 2021
edited
Loading

@lyrixx
The only use case for disabling service resetting is to gain performance for specific consumers; therefore, the option should be in a consumer but not in configuration.

less code == less bug

Absolutely! So we do not need a configuration option (in prospect). What we need are backward compatibility and migration strategy. Some users may use "service leak" as a side effect for their scenarios; however, it's a poor solution. So the decision we should make: how to migrate it.

I think you are right about a deployment. A new plan:
5.4:

  • add themessenger.reset_on_message config with defaultfalse
  • deprecate emptymessenger.reset_on_message with hint about defaults
  • add--no-reset (or--no-reset-services or--no-reset-services-on-message ?) option tomessenger:consume command.

6.0:

  • change default ofmessenger.reset_on_message totrue
  • deprecatemessenger.reset_on_message to remove it in 7.0

@lyrixx@okwinza do you agree?

lyrixx and okwinza reacted with thumbs up emoji

@upyxupyxforce-pushed thefix-container-resetting branch from214a63a tobaa42f9CompareSeptember 25, 2021 18:11
@upyxupyx marked this pull request as ready for reviewSeptember 25, 2021 18:25
@upyxupyxforce-pushed thefix-container-resetting branch frombaa42f9 tob75dd03CompareSeptember 25, 2021 18:25
@upyxupyxforce-pushed thefix-container-resetting branch fromb75dd03 tofc85aefCompareSeptember 26, 2021 13:45
@upyx
Copy link
ContributorAuthor

I've realized that servicesmust not be reset on idle. It's not optional because of counters and sequences that can be used. Possible memory leaks in transports should be fixed in transports.

@lyrixx
Copy link
Member

I've realized that servicesmust not be reset on idle. It's not optional because of counters and sequences that can be used.

What are you refering to?

I think something is not clear here. you (@upyx and@okwinza) talks about performance penalty. Even if thiscould be true, this is not always the case. I'm not sure, but it looks like you thinkall symfony (and app) services will beunset. This is not the case. Service will bereset, it means theServicesResetter service will take all services taggedkernel.reset and callreset() method on it. On the current application I'm working one (SF 5.3, APIP, Doctrine). There are 41 services impacted in dev.

For reference:

 ---------------------------------------------------------- ---------------------- ----------------------------------------------------------------------------------------  Service ID                                                 method                 Class name                                                                              ---------------------------------------------------------- ---------------------- ----------------------------------------------------------------------------------------  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx        reset                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  Symfony\Bridge\Monolog\Processor\ConsoleCommandProcessor   reset                  Symfony\Bridge\Monolog\Processor\ConsoleCommandProcessor  Symfony\Component\Cache\Psr16Cache                         reset                  Symfony\Component\Cache\Psr16Cache  cache.app                                                  reset                  Symfony\Component\Cache\Adapter\FilesystemAdapter  cache.system                                               reset                  Symfony\Component\Cache\Adapter\AdapterInterface  cache.validator                                            reset                  Symfony\Component\Cache\Adapter\AdapterInterface  cache.serializer                                           reset                  Symfony\Component\Cache\Adapter\AdapterInterface  cache.annotations                                          reset                  Symfony\Component\Cache\Adapter\AdapterInterface  cache.property_info                                        reset                  Symfony\Component\Cache\Adapter\AdapterInterface  cache.messenger.restart_workers_signal                     reset                  Symfony\Component\Cache\Adapter\FilesystemAdapter  agent_rules.cache                                          reset                  Symfony\Component\Cache\Adapter\RedisAdapter  cache.array                                                reset                  Symfony\Component\Cache\Adapter\ArrayAdapter  doctrine.result_cache_pool                                 reset                  Symfony\Component\Cache\Adapter\FilesystemAdapter  doctrine.system_cache_pool                                 reset                  Symfony\Component\Cache\Adapter\AdapterInterface  form.choice_list_factory.cached                            reset                  Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator  messenger.transport.in_memory.factory                      reset                  Symfony\Component\Messenger\Transport\InMemoryTransportFactory  cache.validator_expression_language                        reset                  Symfony\Component\Cache\Adapter\AdapterInterface  debug.stopwatch                                            reset                  Symfony\Component\Stopwatch\Stopwatch  security.token_storage                                     disableUsageTracking   Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage   (same service as previous, another tag)                   setToken  cache.security_expression_language                         reset                  Symfony\Component\Cache\Adapter\AdapterInterface  monolog.handler.main                                       reset                  Monolog\Handler\FingersCrossedHandler  monolog.handler.console                                    reset                  Symfony\Bridge\Monolog\Handler\ConsoleHandler  webpack_encore.tag_renderer                                reset                  Symfony\WebpackEncoreBundle\Asset\TagRenderer  cache.webpack_encore                                       reset                  Symfony\Component\Cache\Adapter\AdapterInterface  webpack_encore.entrypoint_lookup[_default]                 reset                  Symfony\WebpackEncoreBundle\Asset\EntrypointLookup  doctrine                                                   reset                  Doctrine\Bundle\DoctrineBundle\Registry  form.type.entity                                           reset                  Symfony\Bridge\Doctrine\Form\Type\EntityType  cache.easyadmin                                            reset                  Symfony\Component\Cache\Adapter\AdapterInterface  api_platform.cache.route_name_resolver                     reset                  Symfony\Component\Cache\Adapter\AdapterInterface  api_platform.cache.identifiers_extractor                   reset                  Symfony\Component\Cache\Adapter\AdapterInterface  api_platform.cache.subresource_operation_factory           reset                  Symfony\Component\Cache\Adapter\AdapterInterface  api_platform.cache.metadata.resource                       reset                  Symfony\Component\Cache\Adapter\AdapterInterface  api_platform.cache.metadata.property                       reset                  Symfony\Component\Cache\Adapter\AdapterInterface ---------------------------------------------------------- ---------------------- ----------------------------------------------------------------------------------------

@upyx
Copy link
ContributorAuthor

upyx commentedSep 27, 2021
edited
Loading

@lyrixx
I meant that what services do on reset. It can be a custom service that increases some counter on every message. For example, Monolog'sUidProcessor generates random UID on every reset. I'm not too fond of randoms (because of collisions), so I'd like to store a sequence in APCu or Redis. It isn't a hot queue (5 messages per day), and I'd like to make numbers as short as possible, and the sequence is short. So forwarding sequence on idle would quickly lead to sequence overflow. It's a simplified example just for clarity. Performance isn't an issue when the queue is idle. We mustn't reset services on idle not because of the cost but because of side effects.

I think it would be good to add documentation that describes what the "services resetting" is and what it is for, how to use thekernel.reset tag and theResetInterface, where to enableautoconfigure, and where don't.

@lyrixx
Copy link
Member

so I'd like to store a sequence in APCu or Redis

if the sequence is in APCu or Redis, it's safe.

IMHO, store a state between 2 messages in PHP memory (so in a service) is a bad idea. It does not work when a worker restart or when there are many worker.

So this is not a valid use case since it's harmful.

The plan you propose inthis comment is really good. Let's stick to it.

Copy link
Member

@lyrixxlyrixx left a comment

Choose a reason for hiding this comment

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

Very good work. Thanks 👍🏼

@fabpot
Copy link
Member

Thank you@upyx.

@fabpotfabpot merged commit8653b33 intosymfony:5.4Sep 27, 2021
@upyx
Copy link
ContributorAuthor

@lyrixx@fabpot you are welcome 🤝 🤝

Should I make another PR to the 6.0 branch in which deprecations are removed?

if (null ===$config['reset_on_message']) {
trigger_deprecation('symfony/framework-bundle','5.4','Not setting the "framework.messenger.reset_on_message" configuration option is deprecated, it will default to "true" in version 6.0.');

$container->getDefinition('console.command.messenger_consume_messages')->replaceArgument(5,null);
Copy link
Member

Choose a reason for hiding this comment

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

I will debug further and create a proper issue/PR later but this service may not exist:

Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "console.command.messenger_consume_messages".

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Yep, I've found where it is. It's possible whenmessenger is installed withoutconsole. I will fix it, but I don't know how to test that fix because ofclass_exists(Application::class) expression.

@lyrixx
Copy link
Member

Should I make another PR to the 6.0 branch in which deprecations are removed?

yes, and a PR on the doc too :)

@upyx
Copy link
ContributorAuthor

Should I make another PR to the 6.0 branch in which deprecations are removed?

yes, and a PR on the doc too :)

#43202 has some fixes
#43203 is a draft for 6.0

fabpot added a commit that referenced this pull requestSep 29, 2021
…nsole (upyx)This PR was squashed before being merged into the 5.4 branch.Discussion----------Fix framework configuration when messenger uses without console| Q             | A| ------------- | ---| Branch?       | 5.4| Bug fix?      | yes| New feature?  | no| Deprecations? | yes| Tickets       |Fix#43133 (review)| License       | MIT| Doc PR        |  laterWe hurried, an addition to#43133Itfixes#43133 (review)It adds the forgotten CHANGELOG & UPGRADEI'm not sure about the CHANGELOG format. I've done as people do but... [it says](https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry) "New entries must be added on top of the list." However, nobody does it 🤷~I don't know how to test the fix, because of `class_exists()` call that cannot be easily mocked.~`src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php:245`:```phpif (class_exists(Application::class)) {    $loader->load('console.php');    /* ... */}```Commits-------d098ef8 Fix framework configuration when messenger uses without console
@upyxupyx deleted the fix-container-resetting branchOctober 6, 2021 22:59
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@kbondkbondkbond left review comments

@fabpotfabpotfabpot approved these changes

@lyrixxlyrixxlyrixx approved these changes

@srozesrozeAwaiting requested review from sroze

Assignees

No one assigned

Projects

None yet

Milestone

5.4

Development

Successfully merging this pull request may close these issues.

Too early resetting services in messenger

6 participants

@upyx@okwinza@lyrixx@fabpot@kbond@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp