-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Segmentation fault with the container, Doctrine and the EntityManagerInterface #34200
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
Comments
The dependency checker is fine: only the constructor's arguments should be considered as any other kind of circular loop is solvable. There are many test cases that prove it already. About this specific loop that happens at runtime would you be able to provide a reproducing test case in |
About the doc PR, please do of course! |
@nicolas-grekas I've been working on this issue this morning and I need some help to create the reproducer: why are variables like From what I understand from https://github.com/symfony/symfony/blob/master/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container_almost_circular.php, it is only used for services (like The closer code to the current issue in src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php is:
But the service is defined in => In my bug's code, the methods are called before the instantiation of the service, and the instantiation is done on the |
@nicolas-grekas actually, we used to have such protections against more complex circular references in Symfony 3.2 and older, where all internals service references were using |
All related issues arise when services are inlined. To prevent a service from being inlined, the reproducer should use public services. I'd recommend trying to reproduce the graph you have at hand using dummy services: all services that are dumped as methods should be public, all the others private. Then, why the variables are dumped this way is complex, I cannot give you a simple answer. @stof adding the check would not solve anything I fear. It might actually break solvable graphs as sometimes there need to be two calls to a service factory to create the fully configured service (there are tests doing so already). In the end, either the circular loop is legit and should be detected at compile time - but to my current understanding, as soon as a loop involves a setter or a property, it should not turn into a runtime loop but can be solved instead, by creating the services (especially the inlined ones) in the correct order. A way to see this is: how would I instantiate this graph if I were to create the code by hand? If there is a solution to this that is not found currently, that's the bug. |
@nicolas-grekas there is also another issue: the dumper assumes that any lazy reference won't trigger a circular reference. But that is a valid assumption only as long as they are not resolved during the instantiation. I see in the dumped code that some of the calls to instantiate inline services are followed by a check whether the requested service is now instantiated (due to some circular reference). I think that the safe logic would be to have such check after all method calls. Or at least after any such method call that involves a soft reference to the current service (i.e. including lazy references, references through method calls, and potential references through injecting the
That is actually the root of the bug, not a feature. If we call the factory twice, we will have 2 different instances of the service in different parts of the object graph if assignments to The big benefit of these runtime checks is that you have a (more or less) clear exception message here, rather than having a broken object graph (like having 2 different instances of your entity manager in different parts of the project). And you know that you have an issue in your service instantiation. |
I think I've grown gray hairs from this issue so I would be very happy if anyone else could take over and do the improvements :) |
btw, re-adding these |
I've just bumped into the exact same problem, circular dep via doctrine subscriber and serializer led to segfault, running with xdebug showed a proper trace to track this down. The interesting bit was that it only showed up when I tried to install symfony/proxy-manager-bridge. I never looked into the internals of DI, so can't really contribute here, just making this comment in case it will be useful for somebody. I solved it by replacing the doctrine subscriber with an entity listener. |
does #39799 fix your issue ? |
…russe) This PR was merged into the 4.4 branch. Discussion ---------- [DoctrineBridge] Fix circular loop with EntityManager | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | fix #39619 | License | MIT | Doc PR | - This PR fix a segfault in EntityManager by making the LazyEventManager handle EventSubscriber in a lazy way. Maybe #34200 too Commits ------- 23d6921 Fix circular loop with EntityManager
Symfony version(s) affected: 4.3.5
Description
@nicolas-grekas Following up on our discussion at the meetup, I've researched the segmentation fault / memory limit error.
The bug comes from the way the method
getDoctrine_Dbal_DefaultConnectionService()
in the dumped container instantiates$this->services['doctrine.dbal.default_connection']
forDoctrine\DBAL\Connection
The method first instantiate all dependencies as expected:
and ends by defining
$this->services['doctrine.dbal.default_connection']
as expected.BUT in our case there is one dependency
$b = ($this->services['search.search_indexer_subscriber'] ?? $this->getSearch_SearchIndexerSubscriberService());
to defineAlgolia\SearchBundle\EventListener\SearchIndexerSubscriber
.This dependency is used as an event subscriber:
$f->addEventSubscriber($b);
The dependency chain is:
Algolia\SearchBundle\EventListener\SearchIndexerSubscriber
Algolia\SearchBundle\IndexManager
Symfony\Component\Serializer\Serializer
The Serializer lists all the Normalizers for its first argument:
\Symfony\Component\Serializer\Serializer::__construct(array $normalizers = [], array $encoders = []))
One of our normalizers depends on a service depending on
EntityManagerInterface
which requires the actual serviceDoctrine\ORM\EntityManager
which depends onDoctrine\DBAL\Connection
.So in the end,
$this->services['doctrine.dbal.default_connection']
is never defined and we have an infine loop.Bottom line, I think there are different bugs here:
EntityManagerInterface
but rely onDoctrine\Common\Persistence\ManagerRegistry
" as you were saying.For the 2nd one, https://symfony.com/doc/current/doctrine.html states:
How to reproduce
I haven't written yet a reproducer but I'm not sure it's useful here.
Possible Solution
I can submit a doc PR if you want.
And if updating the circular-dependency checker is too difficult, an alternative may be to create a state checker in the dumper container for each method:
The text was updated successfully, but these errors were encountered: