Needs work
Project:
Drupal core
Version:
main
Component:
request processing system
Priority:
Major
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
25 Sep 2014 at 06:25 UTC
Updated:
30 Aug 2022 at 03:38 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
larowlannote @sun's proposal in this comment #1509164-29: Use Symfony EventDispatcher for hook system
magic numbers aren't good, before/after then module dependencies is better
Comment #2
chx commentedTotes, it should be
$events[KernelEvents::REQUEST][] = array('before', 'another_service_id')then we can add a nice little topological sort into #1972300: Write a more scalable dispatcher and this would be resolved.Comment #3
chx commentedComment #4
chx commentedComment #5
chx commentedComment #6
chx commentedComment #8
chx commentedThe patch was just a demo.
Comment #9
catchThis will require a container rebuild, so tagging D8 upgrade path per #2341575: [meta] Provide a beta to beta/rc upgrade path.
Also more descriptive title.
Comment #10
znerol commentedUh, the title is misleading. This is not a problem with the YAML or the definition order but a problem with priorities missing for the events in
AuthenticationSubscriber::getResponse()vs.FinishResponseSubscriber().Comment #11
catchI don't think the title is misleading.
For hooks we order them by module weight, name - that gives a reliable execution order which won't change unless you set a different weight or rename a module - which are conscious actions known to affect hook ordering.
Having the order affected by the YAML definition is considerably more brittle. If we didn't have test coverage this could have been done in a 'clean up' patch with no obvious side effects.
We might well want to set explicit weights for those two event listeners but that's not what the critical issue is for - could split that into a separate critical issue if necessary.
Comment #12
chx commentedNot only that but none of the YAML parser, the registration pass, the container dumper, the event dispatcher contract include this sort of pass through. It just happens that all of them does. Indeed, if you implement the event dispatcher differently as done in #1972300-14: Write a more scalable dispatcher then Drupal breaks in this very subtle, insanely hard to debug way. To give you an example: the entity_load contract (in Drupal 7 at least) definitely included that the returned nodes are in the order the nids were passed in and there's code to ensure this happening. There's nothing in code or doxygen in any of the links of this long chain (although the event dispatcher code actually might hint vaguely at it because array_multisort would be simpler than the current code but surely it is not documented on the interface that the order of addListener calls must be kept).
Comment #13
znerol commentedThe Symfony event dispatcher maintains a list of callables (listeners) ordered by priority. Listeners can also be closures. Like pointed out by chx the order of the event listeners with the same priority depends on the implementation. Given that listeners can basically be any callable we cannot rely on any metadata (module name, class name, function name) which could be used as a second sort-key.
Instead I propose that event subscribers like the authentication subscriber or the access aware router subscriber can be tagged as exclusive in the service definition. This information could be used to ensure from within the compiler pass, that no other subscribers are registered with the same priority as an exclusive subscriber.
Comment #14
catch@znerol, yes it's an unreliable model, and we may have to implement only a subset of the features to make it work with a modular system like Drupal - for example throwing an exception if no explicit weight is defined, or adding before/after instead of weights and using that, then documenting the behaviour properly.
Comment #15
znerol commentedPlease keep in mind that the service definition of a subscriber is not the only place where listeners/subscribers can be added to the dispatcher. It is also possible to manipulate the list of listeners for an event at runtime using the methods defined in the interface.
I agree that it might be much easier if we did not have to define priorities but instead just would declare dependencies. Such a system might calculate the priorities from within a compiler pass before adding the listeners to the dispatcher. But that would make it difficult to add/remove listeners at runtime through the Symfony interface because priorities are not easily determinable anymore. That interface is the base line, let's just accept that.
Throwing an exception when priorities clash is the way to go - but not for all listeners/subscribers. Instead let's just do that for "milestones". Adding a subscriber with the same priority as the router subscriber is just a plain stupid idea and that should be punished. Other events are much less suspectible to execution order, like e.g. the TERMINATE event.
Talking about priorities, I just want to point out that
git grep KernelEvents::REQUESTreveals the following pattern:If there is only one
1in the binary representation of the priority, then this is a milestone...Comment #16
chx commented> Throwing an exception when priorities clash is the way to go
Nope, removing events from D8 is the way to go. Am I clear?
There should be 1 listener for Symfony events firing hooks and hooks should be extended so that classes can implement hooks as well for autoloading.
Drupal\module\Hook\hookname::__invokeor somesuch. This is still doable, there are only 68 event subscribers in core. Releasing in this state where both hooks and events are used is madness.Comment #17
chx commentedcatch, what do you think of the battle plan outlined in the issue summary?
Comment #18
xjmComment #19
chx commentedComment #20
chx commentedComment #21
chx commentedComment #22
Crell commentedSince this issue was assigned to me at one point...
We've already made the statement, including straight from Dries' mouth, that longer-term (D9) we should be moving away from hooks to events. Therefore any statement of "we can't have hooks and events together" must imply that we eliminate hooks, not that we eliminate events. We have already backed off on even partial hook-to-event conversions in the interest of not introducing "unnecessary" changes (a decision I have long disagreed with). So D8 living with hooks and events coexisting is, really, no longer a negotiable situation.
Weird ordering of things is hardly a new concept in Drupal; hook order has always been a challenge, to the point that we kept trying to add per-hook weights for cases where module weights were insufficient. Well, events now have per-hook weights, essentially. So rather than use them, the proposal is to avoid events? That does not make even the slightest bit of sense.
If the order of a given event matters, specify a weight. If it doesn't, well, it doesn't. If you don't think it does but it actually does, add a weight when you figure that out.
While a before/after ordering would be lovely, that's far out of scope for Drupal 8, and really ought to be done upstream in Symfony anyway.
This issue is a won't-fix for me.
Comment #23
chx commented> We've already made the statement, including straight from Dries' mouth, that longer-term (D9) we should be moving away from hooks to events.
Exactly. We should remove the HttpKernel and the routing system from D8 as they are not ready and postpone them to D9. That's what I suggest too.
Comment #24
tim.plunkettThis issue is ballooning out of control. First it was fixing event subscribers, then it was removing events, now you're suggesting removing HttpKernel and the routing system?
Do you think we're going to reintroduce hook_menu?!
Let's get this issue back on track, where it was in #1 and #2.
Comment #25
Crell commentedLet's try a new issue with more focus and less hyperbolic silliness.
Comment #26
chx commented> Do you think we're going to reintroduce hook_menu?!
No but I think that reintroducing menu.inc is realistic, I will try this afternoon. However, keeping this open for removing event subscribers at least.
Comment #27
fabianx commentedSo that might be a stupid question but:
- We still have modules
- We still have weights
- We have module names
- We have the class name
D7 ordering (without hook_mod_impl_alter):
- Module Weight
- Module Name
=> consistent, only one hook per module
Proposed D8 ordering:
Use Drupal or System as "module name" for any events not belonging to a module.
Because we have consistent namespaces, we should be able to figure out to what module an event subscriber belongs to.
- Priority
- Module Weight
- Module Name
- Class Name
=> Consistent and working, changeable and predictable
As we persist services during the compiler pass and we have all modules loaded there (obviously), I cannot see why we cannot store the order per EventSubscriber?
Such module implements alter to change weights is kinda implemented as in Drupal 7, but just by instead changing the tagged services in the compiler pass.
Like I recently saw in a patch for the AcceptNegotiation Middleware where some format was subscribed to by changing compiler kernel run pass.
Is there anything I am missing here?
Comment #28
znerol commentedIt is important to understand that listeners / subscribers can be added at runtime - not only from within the compiler pass. If something uses
addListenerthe event dispatcher cannot possibly determine its origin.Comment #29
fabianx commentedIt is okay for them to be added at runtime:
But thanks for telling me how to implement BigPipe in core cleanly :-D - that was what I wondered how to do.
Anyway back to the issue:
If they have a unique priority, they get ordered by priority, if they don't we run them in arbitrary order either before or after all our compiled event subscribers.
I would vote for after, because if you need before, just increase priority.
And if that dynamic order is a problem => upstream symfony problem
but as you can set priorities that is as low a use case problem as can get, so I don't think we will ever hit that case.
Any more concerns?
Comment #30
Crell commentedLet me make sure I understand the issue:
In D7, the order of hook invocations is deterministic but sometimes obtuse, based on module weight, then module name.
In D8, the order of event invocations is deterministic but sometimes obtuse, based on priority, then module weight, then module name, then order of the event subscriber in services.yml. (Because we still parse through modules in the same order when parsing the container.) Right?
First off, there's no way that's critical. I'm half tempted to call it minor.
Secondly, I am inclined to agree with FabianX that if you find yourself in a situation where the order matters that much, use a priority and your problems go away. So I'm not convince there's an actual bug to address here.
If we do want to actually do something, finagling the service order to be alphabetic by class in via a compiler pass seems like a reasonable and still deterministic solution.
Comment #31
fabianx commentedThis is critical as moving services in services.yml should not break something - unless there is a contract somewhere that that is not allowed.
If a service is not used always, this can lead to very difficult to find bugs and its a region where you don't expect change to break something. (I would totally accept a patch as a GK that reordered services.yml to group some services more logically and 2 weeks later I am bitten on production that some service fails ... )
If we support module weight implicitly due to the order how the services.yml is parsed, this should be fine and all we need to do is to additionally sort within system / the same module by class name.
So just a little sort adding and we should be done.
The only reason this is a new problem in D8 is that modules never did have a hook twice.
Comment #32
dawehnerStarted with just writing down the tests how its expected to work. While doing that I realized that the current class is horrible named.
I think we could get rid of the module name if we just consider the full namespace + classname for the sorting.
Comment #33
fabianx commentedLet the tests begin to fail :)
Comment #35
dawehnerThere we go
Comment #37
dawehnerThere we go.
Comment #39
dawehnerSo sadly the order of the events change which seems to let them all fail.
Comment #40
dawehner.
Comment #42
dawehnerInteresting bug.
Comment #44
dawehnerWorked a bit on it, and figured out that we have still some inconsistency in
DrupalKernel::$moduleList.It is sometimes considered to be a hashmap of module extensions keyed by module name, and sometimes a hashmap of module weights, keyed by module weight.
Let's see how much failures we do have left ... at least one of the web tests pass already.
Comment #45
dawehner.
Comment #47
dawehnerFixed nearly all notices, working on the last remaining one (probably).
Comment #51
dawehnerComment #52
znerol commentedPlease note that if definition-dependent ordering is really an issue, then there is much more stuff affected by it. Not only the event dispatcher but basically anything that implements tagged services, i.e. most of the stuff implementing a chain-of-responsibility pattern.
If we really want to rely on the alphabetic order of modules, then it should be enough to sort the service definitions by module name before compiling the container.
Comment #53
dawehnerAlright, looked at the resulting order, and fixed it.
Comment #54
aspilicious commentedCan someone fix the summary?
Comment #55
dawehnerTotally valid point!
Comment #56
chx commentedComment #57
damiankloip commentedWhat is this change about? And why not use the module handler (which is assigned below/after atm)?
A comment here that you are using the set weight for a module if available would be good.
then
class name
Isn't that what's happening above in the compiler pass?
These could probably jsut be rolled into one line?
Comment #58
dawehnerWell, it uses the kernel as before. Note: We fixed the parameters of updateModules
Comment #59
znerol commentedI pointed out to @dawehner in IRC and also summarized in #52 that this approach is invalid. I've named several reasons, but the most important is that it only addresses one particular case of where definition order influences execution order.
If we really care about definition order influencing execution order, then it is not enough to "fix" the event listener compiler pass. We'd need to apply the same approach on most of the other tagged services also.
This issue has been started in a rage and subsequent discussion was loaded with considerable emotion. Please let us reconsider the scope and the severity of this issue from pure a technical perspective.
Comment #60
catchAdding triage-needed tag. I personally think we should see if #52 is doable.
If we can't find a good resolution here, then we at least need to clearly document somewhere that the order of the services in the file affects the order of execution and get that clarified upstream.
Comment #61
dawehnerThe main difference is that other tagged services are basically always independent, in contrast to our various event subscribers
I bet upstream would say: use a priority if the order of execution matters.
Comment #62
catchI'm sure upstream would say that, they don't have a module system though.
Thinking about this more, I think we should do the following:
1. Document somewhere that this is the behaviour so at least it's somewhere other than this issue.
2. Downgrade this to major, but if we can fix the ordering I'd probably allow that to happen right up until release candidate since any pain from contrib modules then would be less than people are going to run into later.
Comment #63
chx commentedThis means #2264049: Create an Events topic is now even more critical.
Comment #64
catchAdding RC deadline tag, don't think we want to change event order after release.
Comment #65
xjmComment #66
chx commentedErm. Nope. You only get to remove that tag if you'd removed events.
Comment #67
larowlantagging for review during critical hours today
Comment #68
larowlanStill needs doing? Looks like the test includes module weights so perhaps old comment?
I don't see that 'service_collector' tag is being addressed yet - is that in scope here or not?
Comment #69
dawehnerCan you describe what you mean by that point?
You are right, in
testProcessWithMultipleSubscribersWithModuleWeightAndSamePrioritieswe deal with it already.Also renamed from EventListeners to EventSubscribers, from former feedback of @chx
Comment #70
larowlanSo two breadcrumb builders in the same file with the same priority.
I think the answer is use a priority, but not sure how that differs from the overall OP.
Comment #71
dawehnerAh I see, well yeah, better use priority. They might also not conflict with each other in the first place though.
Comment #72
larowlanLet's do it
Comment #74
larowlanSomeone can re-roll in office hours
Comment #75
larowlanstraight re-roll
Comment #79
znerol commentedThe latest patch still does not address #59 / #60.
Comment #80
catchNot sure why this is assigned to me.
Comment #81
Anonymous (not verified) commented69 rerolled against 8.0.x-dev, hope it helps!
Comment #83
effulgentsia commentedSince this issue is no longer critical, untagging for critical office hours. Don't know if we have or should start a "Major Office Hours" or similar tag.
Comment #86
xjmThis issue was marked as a beta target for the 8.0.x beta, but is not applicable as an 8.1.x beta target, so untagging.
This sounds disruptive still, so moving to 8.2.x.
Comment #89
xjmComment #100
quietone commentedUpdating tag.