Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.7k
[Messenger] Add a middleware that wraps all handlers in one Doctrine transaction.#26647
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
Uh oh!
There was an error while loading.Please reload this page.
Conversation
| ->end() | ||
| ->end() | ||
| ->end() | ||
| ->arrayNode('doctrine') |
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'd suggest making it more explicit:doctrine_transaction.
| ->arrayNode('doctrine') | ||
| ->canBeEnabled() | ||
| ->children() | ||
| ->scalarNode('entity_manager')->info('The name of the entity manager to use')->defaultValue('default')->end() |
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.
entity_manager =>entity_manager_name to clarify is not a service name?
| class WrapsMessageHandlingInTransactionimplements MiddlewareInterface | ||
| { | ||
| /** | ||
| * @var ManagerRegistry |
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.
This can be removed.
| private$managerRegistry; | ||
| /** | ||
| * @var string |
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.
This can be removed as well.
| private$entityManagerName; | ||
| /** | ||
| * @param ManagerRegistry $managerRegistry |
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.
All of these as well :)
| publicfunctionhandle($message,callable$next) | ||
| { | ||
| /** @var $entityManager EntityManagerInterface */ |
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'd suggest to check withinstanceof the type, rather than assuming it is always going to beEntityManagerInterface. If not, we can simply throw anInvalidArgumentException.
| $result =null; | ||
| try { | ||
| $entityManager->transactional( |
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.
It looks like to me that using thebeginTransaction,commit androllback methods would be much more explicit that this callback-based transaction that forces you to do this weirdresetManager later :)
Nyholm commentedMar 23, 2018
Thank you for the review |
| $container->getDefinition('messenger.sender_locator')->replaceArgument(0,$senderLocatorMapping); | ||
| $container->getDefinition('messenger.asynchronous.routing.sender_locator')->replaceArgument(1,$messageToSenderIdsMapping); | ||
| if ($config['doctrine']['enabled']) { |
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.
Array keys mismatch after recent rename.
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.
Thanks!
| $container->getDefinition('messenger.asynchronous.routing.sender_locator')->replaceArgument(1,$messageToSenderIdsMapping); | ||
| if ($config['doctrine']['enabled']) { | ||
| $container->getDefinition('messenger.middleware.doctrine_transaction')->replaceArgument(1,$config['doctrine']['entity_manager']); |
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.
$config['doctrine'] should be$config['doctrine_transaction'] and['entity_manager'] should be['entity_manager']['name'], we will need tests for this if it's not already covered
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.
Thank you
Nyholm commentedMar 23, 2018
@chalasr The tests currently fails due invalid xml config. Do I need to modify the |
chalasr commentedMar 23, 2018 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@Nyholm yes |
Nyholm commentedMar 23, 2018
I've got an error I cannot figure out:
Any help would be appreciated. |
| $entityManager =$this->managerRegistry->getManager($this->entityManagerName); | ||
| if (!$entityManagerinstanceof EntityManagerInterface) { | ||
| thrownew \InvalidArgumentException(sprintf('The ObjectManager with name "%s" must be an instance of EntityManagerInterface',$this->entityManagerName)); |
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.
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.
Hm.. But the doc block says ObjectManager..
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.
Ah! I see, NVM, sorry for the noise.
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.
Do not be sorry. Thank you for challenging this =)
| ->arrayNode('doctrine_transaction') | ||
| ->canBeEnabled() | ||
| ->children() | ||
| ->scalarNode('entity_manager_name')->info('The name of the entity manager to use')->defaultValue('default')->end() |
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.
Could we make itnull by default and let Doctrine determine the default name? (see)
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.
Excellent idea. Thanks
| * | ||
| * @author Tobias Nyholm <tobias.nyholm@gmail.com> | ||
| */ | ||
| class WrapsMessageHandlingInTransactionimplements MiddlewareInterface |
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.
shouldnt this be moved tot the doctrine bridge instead? Otherwise i expect naming likeDoctrineMessageWrappingMiddleware or so :)
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.
What aboutDoctrineTransactionMiddleware ?
sroze commentedMar 26, 2018
@Nyholm could you move the middleware to the DoctrineBridge and let Doctrine decide the default manager name? |
Nyholm commentedMar 26, 2018
Done |
| <xsd:complexTypename="messenger_doctrine_transaction"> | ||
| <xsd:sequence> | ||
| <xsd:elementname="entity-manager-name"type="xsd:string" /> |
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.
this one could be an attribute rather than a child element IMO
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.
Could you please assist me here? I've had a hard time making this work. That is why the tests are failing.
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.
Fixed!
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.
Last thing I guess: can you move this to be an attribute? It would meanthis XML would look like that instead:
<framework:config> <framework:messenger> <framework:doctrine-transactionentity-manager-name="foobar" /> </framework:messenger> </framework:config>
Nyholm commentedMar 26, 2018
Finally done with the config. It took me way more time than I want to admit. Anyhow. I learnt something =) Example XML: <framework:config> <framework:messenger> <framework:routingmessage-class="App\Bar"> <framework:senderservice="sender.bar"></framework:sender> <framework:senderservice="sender.biz"></framework:sender> </framework:routing> <framework:routingmessage-class="App\Foo"> <framework:senderservice="sender.foo"></framework:sender> </framework:routing> </framework:messenger> </framework:config> Example Yaml: framework:messenger:routing:'App\Bar':['sender.bar', 'sender.biz']'App\Foo':'sender.foo' |
| if ($config['doctrine_transaction']['enabled']) { | ||
| if (!class_exists(DoctrineTransactionMiddleware::class)) { | ||
| thrownewLogicException('You must install symfony/doctrine-bridge to use the "DoctrineTransactionMiddleware"'); |
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'd suggest the following:
The Doctrine transaction middleware is only available when the doctrine bridge is installed. Try running "composer require symfony/doctrine-bridge".
| <xsd:complexTypename="messenger_doctrine_transaction"> | ||
| <xsd:sequence> | ||
| <xsd:elementname="entity-manager-name"type="xsd:string" /> |
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.
Last thing I guess: can you move this to be an attribute? It would meanthis XML would look like that instead:
<framework:config> <framework:messenger> <framework:doctrine-transactionentity-manager-name="foobar" /> </framework:messenger> </framework:config>
| <framework:config> | ||
| <framework:messenger> | ||
| <framework:routingmessage-class="App\Bar"> | ||
| <framework:senderservice="sender.bar"></framework:sender> |
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 guess you can end them with/> instead of the full XML closing tag.
Nyholm commentedMar 27, 2018
Thank you for the reviews. I've updated accordingly and rebased the PR on master. |
| framework: | ||
| messenger: | ||
| doctrine_transaction: | ||
| entity_manager_name:'foobar' No newline at end of file |
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.
missing eol :}
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.
Thank you
chalasr commentedMar 27, 2018
Just wondering, should this new config be wrapped in a |
Nyholm commentedMar 27, 2018
Good idea. I've updated the PR |
| $config['middlewares'] =array(); | ||
| return$config; | ||
| }) | ||
| ->end() |
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.
Is this the proper way of making sure that there always is a config key named "middlewares"?
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.
No, it is not. We should of course use->addDefaultsIfNotSet()
Nyholm commentedMar 27, 2018
Example Yaml: framework:messenger:routing:'App\Bar':['sender.bar', 'sender.biz']'App\Foo':'sender.foo'middlewares:doctrine_transaction:~ |
sroze commentedMar 27, 2018
@chalasr I think we might want to do this in the future. I guess it really depends on the "demand" as it isn't that hard to register your own bus services yourself either. |
sroze commentedMar 27, 2018
Thank you@Nyholm. |
Nyholm commentedMar 27, 2018
Thank you for merging |
fabpot left a comment
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.
This one has been merged too fast. I don't want to have Doctrine specific code in FrameworkBundle. It should be moved to DoctrineBundle.
| $entityManager =$this->managerRegistry->getManager($this->entityManagerName); | ||
| if (!$entityManagerinstanceof EntityManagerInterface) { | ||
| thrownew \InvalidArgumentException(sprintf('The ObjectManager with name "%s" must be an instance of EntityManagerInterface',$this->entityManagerName)); |
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.
missing dot at the end of the exception message.
| ->arrayNode('middlewares') | ||
| ->addDefaultsIfNotSet() | ||
| ->children() | ||
| ->arrayNode('doctrine_transaction') |
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.
This should not be configured via Framework, but via DoctrineBundle. We don't have any other Doctrine config here AFAIR.
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.
@fabpot are you happy about the middleware being in the bridge, right?
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.
yes
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.
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.
#26684 is in.
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 also think this makes sense as is. Moving it to doctrine bundle would mean configuring messenger middlewares in two different places, I can't think of a good final result.
We enable logging features for some components via framework config in the same way
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 agree with@chalasr , it's only a feature flag. It does no harm having this in the fwb whereas having to configure middlewares in 2 different config keys is really bad, DX wise. WDYT?
fabpot commentedMar 27, 2018
/cc@sroze |
sroze commentedMar 27, 2018
@fabpot OK, PR on the way. |
…on from the FrameworkBundle (sroze)This PR was merged into the 4.1-dev branch.Discussion----------[Messenger] Remove the Doctrine middleware configuration from the FrameworkBundle| Q | A| ------------- | ---| Branch? | master| Bug fix? | yes| New feature? | no| BC breaks? | no| Deprecations? | no| Tests pass? | yes| Fixed tickets |#26647| License | MIT| Doc PR | øAs per@fabpot's request, remove the enabling feature of the DoctrineMiddleware from the FramworkBundle./cc@NyholmCommits-------27a8b1d Remove the Doctrine middleware configuration from the FrameworkBundle
This is greatly inspired by SimpleBus. This middleware will rollback a transaction if the message handler throws an exception.