-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DX] Promote phppm and add support for resettable services #23984
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
TL;DR, you propose adding a This This might be something for PHP-FIG thought. For the 2 issues you spotted, I think we have to find more specific solutions - just not this one... |
the fact that this forces any package containing a memomizing class to depend on the package defining the interface is precisely what I highlighted in #23975 as major pain point to implement such an idea. |
@stof @nicolas-grekas I got it now and you are right, it should something for PHP-FIG. Given that Symfony is leader in PHP world, does it make sense to implement it here and when PSR is created one day, move to it like ContainerInterface was? It would work only for bundles at beginning but I think it is a good start. @nicolas-grekas About
Can a simple |
with a doctrine listener that would call it on terminate? you should give it a try in a PR yes |
A component should not care about whether it's constructed and managed by a container or not. Also, a library like Doctrine for instance should not have an opinion on what kind of data needs to be flushed for a new request. This is why I also would oppose such an interface. However, for the bundle that registers a component as a set of services, it's a different story. DoctrineBundle could reset the EM, FrameworkBundle could reset the form type you mentioned, WebProfilerBundle could flush data collectors that would leak memory otherwise. So if a generic cleanup mechanism would be implemented, the framework should imho hand over the responsibility to the bundles. Bundles could register event handlers that to do the cleanup work. I could see the following approaches:
The event handlers in both approaches have the downside that they might unnecessarily reset a service that has not been constructed yet. Alternatively, we could try to solve this with the container. The container builder would receive a mechanism to configure method calls that reset a service. services:
some_service:
class: Acme\SomeService
on_reset:
- myResetMethod
- ['@some_other_service', anotherResetMethod] The container would have a |
@derrabus it might be worth pursuing the generic idea (the per-bundle-listener-style has many drawbacks you spotted so it doesn't look worth it to me.) So, about the generic one:
why a new event?
I would try so resist as much as possible to the need for a new config option, but the concern is valid. Another option would be to have a new type of service arguments (similar to Would that make sense? Would you like to give it a try? |
Just out of curiosity: shouldn't it be enough to reset all instantiated services by setting |
In theory it could, but this wouldn't clear the static properties (if any leak comes from there). It wouldn't also clear circular references - unless an explicit call to the garbage collector is done. But planning for resetting all services + triggering the GC might defeat most of the perf benefit of php-pm-like strategies. |
@NicoHaase Yes, you could do that, but the idea of phppm is to gain speed by not throwing everything away between requests. So keeping constructed services and only deleting request-related data from them is the desirable behavior here. One could also think about just unsetting those services that carry request-related data, but then we might get strange behavior in services that have already consumed the service we've just unset. @nicolas-grekas For me, the question whether we solve this with a config option or a tag is the question if we make this a feature of the container itself (=> config option) or a feature that FramworkBundle adds to the component (=> tag + compiler pass). I don't want to bloat the container, but I think this feature could be useful for projects that use the container without the framework.
If we don't take the approach of event handlers registered by bundles, we don't need a new event. I simply wanted to move the decision whether cleanup event handlers are necessary or not out of the bundles. After all, preparing the application for another request was not the original purpose of
I don't see a generic use-case in such a feature. Also, the content of that iterator might change after each request. This could make its behavior unpredictable and volatile.
I'll work on a prototype and see how it works out. |
if we can do it with frameworkbundle, without patching DI, it means anyone can do it also. about uninitialized services, you still need a way to (not) deal with them, how would you do it then? |
See #24033 for uninitialized services |
Hello, this post may be a little bit out of topic but it's about the part of "promoting phppm". I don't see how phppm could replace the About the rest: that's awesome trying to move on a real support for phppm. This is really interesting. Thanks for your work Symfony Team!!! 😄 |
@Nek- Yes, php-pm does support "hot reloading". |
…EFERENCE (nicolas-grekas) This PR was merged into the 3.4 branch. Discussion ---------- [DI] Add ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - As spotted in #23984 and #18244, supporting php-pm and the likes requires us to have a way to reset services, typically on `kernel.terminate`. Yet, only actually instantiated services need to be resetted. Knowing if a service is initialized or not is doable for public ones thanks to `Container::initialized()`. But for private ones, there is no way to achieve it. I propose to add a new way to reference services via this new const. When a reference has this behavior, two things happens: - at runtime, if the referenced service is not initialized, it behave the same as "ignore on invalid", pretending the service doesn't exist - at compile time, such references are weak, meaning their referenced services *can* be removed, but *cannot* be inlined Commits ------- 0db3358 [DI] Add ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE
@derrabus #24033 is now merged. To sum up the plan we talked about on Slack: Once ready, we could leverage that feature and review components per components the services that would need this resettable behavior to comply with php-pm&the-likes. We could also add a "ResettableInterface" in HttpKernel, so that this behavior could be easily autoconfigured. |
@nicolas-grekas Please have a look at #24155. |
…e services (derrabus) This PR was merged into the 3.4 branch. Discussion ---------- [FrameworkBundle][HttpKernel] Add DI tag for resettable services | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #23984 | License | MIT | Doc PR | TODO This PR uses #24033 to introduce a DI tag for resettable services. TODO after merge: * Add an interface, make the "method" attribute optional and enable autoconfiguration. * Consider adding a config option to enable/disable this feature. * Configure leaking services of the core bundles as resettable. Commits ------- d9a6b76 A DI tag for resettable services.
…rrabus) This PR was merged into the 3.4 branch. Discussion ---------- [FrameworkBundle] Reset stopwatch between requests | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #23984 | License | MIT | Doc PR | N/A Follow-up to #24155. This PR ensures that the stopwatch is reset between requests. Commits ------- 7c3bdd9 Reset stopwatch.
See #24232 and doctrine/DoctrineBundle#695 that should fix the DoctrineEntity-related issue. |
Thank you guys, this |
…nicolas-grekas) This PR was merged into the 3.4 branch. Discussion ---------- [Bridge\Doctrine] Add "DoctrineType::reset()" method | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #23984 | License | MIT | Doc PR | - Works with doctrine/DoctrineBundle#695 Commits ------- dce1eb9 [Bridge\Doctrine] Add "DoctrineType::reset()" method
Happy to close this one, that's a great step forward :) |
…n requests (derrabus) This PR was merged into the 3.4 branch. Discussion ---------- [SecurityBundle] Reset the authentication token between requests | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #23984 | License | MIT | Doc PR | N/A Follow-up to #24155. This PR resets the token storage, making sure there's no user logged in at the beginning of a new request. Commits ------- e46b366 Reset the authentication token between requests.
Intro
phppm is a library that makes php application run 10-15 times faster compared to
./bin console server:run
. And unlike internal php web server, phppm supports concurrent requests which makes developing angular state machines much faster.Problem
Because services are shared between requests, any memoized data can/will affect next request. Doctrine identity map is the best example but it is easy solvable; only do
$em->clear()
inkernel.terminate
.I am using it for about a month and so far, I spotted only 2 problems. One is data collectors but it a problem only in debug mode so not something of big relevance.
The second is EntityType service. It has very nice optimization of queries but keeps results in $choiceLoaders property. Parent class
DoctrineType
takes care of it, check the code.This creates 2 problems when used in phppm enviroment, first one is described here but closed in favor of this ticket. The second problem is that memory will keep pilling up for every form with EntityType and it cannot be solved using DTO objects.
The solution
It should be pretty easy to solve it. I made a bundle that will collect all services with
ResettableServiceInterface
implemented and callreset()
whenkernel.terminate
event is triggered.So for example, if this RFC is accepted, DoctrineType would only have this extra code:
Any other service that does memoization can be easily upgraded to work with phppm.
Proposal
Symfony already promotes popular bundles and while phppm is not technically a bundle, I think it should be placed there. I am willing to write docs for that.
This solution should be part of core, not a third-party bundle. More people will adopt it that way because not all developers are familiar with phppm but also because it is part of core and people prefer opinionated solutions.
Technical
You guys are far more skilled than me but if you check my code and think it is good enough, I could make a PR. Please keep in mind that would be my first contribution so be gentle 😄
I would suggest one more thing, the new config value:
If false, than services would not be reset to avoid instantiating them.
The text was updated successfully, but these errors were encountered: