-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[EventDispatcher] Freeze events #34988
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
Conversation
d5309f3
to
a9fce70
Compare
a9fce70
to
2ae8211
Compare
@derrabus very nice! Event sorting always looks "heavy" when profiling Symfony apps. About the Also, can you please tell us if this could have any impact in third-party bundles? Thanks. |
Applications might add event listeners at runtime, e.g. $dispatcher->addListener(KernelEvents::REQUEST, function (RequestEvent $event): void {
// do something
}); This is not possible anymore if we freeze this event. All listeners have to be registered through the container in that case. The setting lets you decide whether you want to be able to add listeners at runtime or not.
Enabling the setting is definitely risky and the application should be tested thoroughly afterwards. Whether this setting enables a migration path (which means it’s always on in Symfony 6) or whether it remains permanently as a performance tweak is something we could decide on: I usually write my applications in a way they would work with this setting enabled. But I’ve seen quite a few applications that dynamically add listeners and I’m not 100% sure if migrating them to frozen events would be worth the effort. We could also think about flipping the default in Symfony 6, going from opt-in to opt-out.
In theory, enabling this setting could break third-party bundles if they register own event listeners for kernel events and not use the |
Thanks for the detailed explanation. I have another question/comment: do you think that we could detect all this automatically so we don't have to define a config option? The idea would be:
The problem of differentiating when we call to these methods during compilation and when do others call them during runtime could be solved by adding |
@javiereguiluz When compiling the dispatcher, I cannot know if someone might add a listener to a specific event at runtime later on. Also, my current implementation throws away all information on the priorities of the listeners. While I could certainly keep that information somehow, it would make the implementation more complex and we will certainly lose a bit of the performance boost gained here. |
I agree with Javier we cannot make this optional - that's almost impossible to decide when you should set this option or not from an end user pov. |
What I could try is to compile the initial state of the event dispatcher into the container. One big array structure loaded via the constructor instead of many But that would basically mean rewriting Apart from that, I still think that if we want to move towards immutability, we should allow to configure this for each event individually. An immutable event dispatcher would be a performance gain, but it would also make the learning curve steeper for newcomers and break existing applications in non-trivial ways. |
To me, considering the |
Additional idea: we add a parameter to opt-in for a really immutable dispatcher in 5.1, and we turn that param on in the recipe. |
I agree, yet people have done just that.
I have worked with a team recently that made heavy use of the event dispatcher for domain events. Because their application needed to be extensible for customization, they had developed a plugin mechanism that would add event listeners at runtime. I myself wouldn't have designed it that way, but it worked for them. Now, if we would make the whole dispatcher immutable in 6.0, we'd probably lock them in 5.4 for quite a long time. I can imagine that other teams will have that problem as well. This is why with this PR I took the approach to only make certain events immutable, in this case the kernel events. |
By sending the deprecations in 5.1, people will have 4.5 years to figure out how to improve their design. |
The upgrade path is pretty much clear. All I‘m saying is that it might be a hard hit for certain applications.
I agree. I‘ll have a look once I‘m back in Germany. |
…ispatcher (derrabus) This PR was merged into the 5.1-dev branch. Discussion ---------- [HttpKernel] Make ErrorListener unaware of the event dispatcher | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | Deprecations? | no | Tickets | N/A | License | MIT | Doc PR | N/A Under certain circumstances, HttpKernel's `ErrorListener` class might dynamically register and unregister a listener with the dispatcher. If our goal is to make the dispatcher immutable, that specific behavior would be in our way. Also, #34988 would break this workflow. This PR provides an alternative. The listener is always registered, but I'm using the request to piggyback a flag that activates/deactivates the listener. Commits ------- a9d1ded [HttpKernel] Make ErrorListener unaware of the event dispatcher.
@derrabus Is it something you're still working on? |
Not actively, but I'm still planning to resume my work later this year. Especially the changes to the Security components could make immutable event dispatching easier. |
Thank you for the feedback. Let's close this PR for now then. I'm very much looking forward to reviewing a new PR in a few months implementing this feature. |
This PR is an attempt to improve the event dispatcher's runtime performance through immutability. Making the whole event dispatcher immutable would be a heavy task, especially when it comes to events in the form component.
In this PR, the event dispatcher allows to freeze specific events. This means that we provide an initial ordered list of listeners for each of those events which cannot be modified through
addListener()
et al afterwards. This way, potentially heavy sorting work can be moved to the container compilation and some runtime checks become obsolete. A new config optionframework.freeze_kernel_events
is introduced that enables this mode for all kernel events (defaults tofalse
).I'm making use of PSR-14-style listener providers for the frozen events. This could (in a later PR) be extended further to be more PSR-14 compatible. If we decide that PSR-14 listener provider are never going to happen in Symfony, we can of course simplify that part.
I've tested this change on a rather extreme application with a thousand event listeners on a hello world application.
Test repository with 1000 listeners on
kernel.request
:https://github.com/derrabus/heavy-event-dispatcher-test
Blackfire comparison master vs this branch with
freeze_kernel_events
enabled:https://blackfire.io/profiles/compare/46257d02-db67-4c54-9bfb-e98c5ec2d942/graph
TODO: