Problem/Motivation
Spin-off from #3416522: Add the ability to install multiple modules and only do a single container rebuild to ModuleInstaller.
In that issue we are defaulting container_rebuild_required to FALSE if it's not set in a module's .info.yml to preserve bc with modules that modify the container in a way that affects the install of other modules.
However, given the memory leak we've discovered in attribute discovery, I think we should consider being more aggressive and defaulting it to TRUE when it's not set instead. This will dramatically reduce the memory requirements and time to install Drupal (Berdir tested with his install profile and it went down from 1.6gb to ~200mb). We would need to find as many of the affected modules proactively as we can and open issues against them.
Steps to reproduce
Proposed resolution
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
Issue fork drupal-3492235
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3492235-rebuild-required
changes, plain diff MR !10494
- 3492235-only-change-default
changes, plain diff MR !10495
Comments
Comment #2
longwaveWondering if we can do this in a safe way. If a module calls
\Drupal::service()inhook_install()for a service provided by a just-installed dependency then we need to rebuild the container, but that's the problem of the second module - the container only needs to be rebuilt if both are installed at the same time.Do we know what other scenarios require a container rebuild?
If
hook_install()and similar code required dependency injection, we could perhaps figure this out dynamically.Comment #3
catchThe example @alexpott gave was one of the config trickery modules in contrib which affects how config is installed, I don't think it's config_split but that general area. We could maybe look for the magic service provider class since that is consistently named, if that's a good indication of the problem.
Drupal::service() calls in hook_install() is a different problem I had not even really thought of, but there is no way for modules to know that another module is going to do that, so container_rebuild_required won't help. However that also seems pretty fragile anyway. I guess we could try to detect when we've just installed a module that another module we're about to install depends on, and rebuild the container then?
So we could make a second array of modules specified as dependencies that are either in the module list passed in, or added to the list of modules to install, and then treat all of those as needing a container rebuild. That bit shouldn't be too bad, we've still got magic service trickery to worry about though.
Comment #4
catchFound the reference #3416522-22: Add the ability to install multiple modules and only do a single container rebuild to ModuleInstaller
Comment #5
longwaveFor #4 I'm trying to think how we could automatically detect that but it's tricky because the decoration is done in a service provider class.
Comment #6
catchhttps://git.drupalcode.org/project/config_selector/-/blob/8.x-2.x/src/Co... does exist, so
This from DrupalKernel:
So a combination of checking dependencies and whether this class exists might reduce any potential disruption by a lot.~
edit: crossposted with #5 - I think we could rebuild the container when we find a service provider class at all, unless the module has opted into container_rebuild_required: false. This won't catch decorated services in services.yml but it feels like the sort of edge cases we're worried about are more likely to be in modules with ServiceProvider classes?
We can still use container_rebuild_required: true for any modules that aren't caught by this.
Comment #7
longwaveBuilding on #3, maybe we can just rely on dependencies? If we build a graph of dependencies then we can guarantee a container rebuild between a module and any dependency, but we can still combine the branches of the graph in a way that minimises the total number of rebuilds that is required.
If this works then all that modules have to do is ensure their dependencies are correctly declared.
Comment #8
catch#7 I think will be fine for the 'module calls a service of another service during installation'. If we put all the depended-upon modules at the start, that would be enough to minimise the number of container rebuilds.
However it won't solve the problem of #6 where modules are impacting the installation process itself - so maybe we can do the dependencies here, and then split installer-modification out to its own issue, or just not fix that and open issues against modules like config_selector?
Comment #9
longwaveI haven't looked but I don't understand in the config_selector case how it ensures it is installed first when multiple modules are installed simultaneously, if it affects the install process itself.
Comment #10
catchComment #12
catchI started thinking about this but I think we might be fine without any special logic.
The thing we need to guarantee that is after a module that is a dependency of a module we're also installing is installed, we do a container rebuild.
We could potentially optimize things by grouping together modules that are dependencies of other modules, and then only rebuild the container after those are installed.
However if we do that, we need to account for modules that depend on one module and are also depended upon by another module.
We could handle that by just in time rebuilding the container when a module that specifies dependencies is installed, but we only need to do that if another module with the same dependency wasn't just installed - e.g. if two modules depend on views, we only need to container rebuild once.
If we just find all the modules that are dependencies of other modules, rebuild the container after each one, then we still get a good distribution without anything extra on top.
Let's say three modules that are dependencies and three that specify those as dependencies. xxx and yyy.
If we do xxx yyy - that's four container rebuilds (3 + 1 at the end).
If we do xy xy xy that's also four container rebuilds.
Comment #13
longwaveModuleHandler::buildModuleDependencies()already builds a graph of dependencies so I was hoping we could use that.I think that we need install modules in the order of the longest possible route through the graph, which will ensure that all dependencies are met by the time each subsequent module gets to be installed.
Comment #14
mikelutzIs this an issue though? From a brief reading of the issues and code it seems like we rebuild the container for the batch prior to running any of the install hooks for the batch anyway. We also rebuild the container before the config installer is used, so I'm not even sure that config_selector would be an issue, as we would be using the updated config installer for the batch containing config_selector too. Seems like what would be different would be on hook install you would have items in the container that wouldn't have been there previously, i.e. some service might be decorated from a module dependent on this module that wouldn't have otherwise been decorated when this module is being installed.
So if module a depends on module b, and module a decorates some service, currently it knows that when module b is installed it is using the original service and not the decorated service. With this change it can't be sure of that, and the flag won't help. But I can't think of a scenario or example that would make that an issue? Possible though.
Comment #16
catchI've put a simpler MR up at https://git.drupalcode.org/project/drupal/-/merge_requests/10495 which only changes the default and doesn't try to handle dependencies, because as @mikelutz points out, hook_install() looks to be already covered.
Postponing on #3492705: Install modules with container_rebuild_true set by themselves though since that will ensure that when a module specifies this manually, it really does get its own container rebuild.
edited to fix the issue reference.
Comment #17
catchComment #18
mikelutzI think you meant to postpone this on #3492705: Install modules with container_rebuild_true set by themselves, no?
Comment #19
catchOops yes I did, edited the comment.
Comment #20
catch#3492705: Install modules with container_rebuild_true set by themselves is in. Rebased https://git.drupalcode.org/project/drupal/-/merge_requests/10495
Comment #21
mikelutzAfter all the discussions here, and in slack I think it really is this simple. I've updated the change record here. I really think the best thing is to get this into 11.2 now, as early as possible, just in case there are any unexpected patterns in contrib that this might negatively affect. This will give us as much time as possible to identify and adjust.
Comment #22
longwaveIf the default is now false we can remove
container_rebuild_required: falsefrom all .info.yml files again.Comment #23
catchRemoved.
Comment #24
mikelutz+1 to back to RTBC
Comment #26
longwaveCommitted 58be5f1 and pushed to 11.x. Thanks!