Problem/Motivation
#3259188: Extend post update system to provide themes a way to install newly-required dependencies had a big impact on site install and test performance on sites with a large number of modules as it exponentially increased the number of times extensions are scanned and update files are loaded.
While patch #10 in #3294299: Regression in functional test performance with a large number of modules brought some partial improvement, it couldn't get our running times back to what they were before #3259188 was introduced. Even though file cache for ExtensionDiscovery was enabled, it keeps spending a lot of time scanning for individual extensions (exponentially to the number of extensions installed).
This means that either the cache isn't working as expected or there are other underlying issues, because when refactoring the code in a way that scanning of extensions is being skipped (see patch), I saw significant improvements in performance (saving 2 minutes on an otherwise 6min long job).
Steps to reproduce
Run a site install or InstallTest with 150+ modules enabled. Especially in an environment with slower I/O like docker/WSL2.
Proposed resolution
- Optimizing code by decoupling scanning for extensions and loading update files.
- Fix any issues with caching in case there are.
Remaining tasks
Get some real world testing as to how bad the problem actually is (maybe it's an issue on our end?). Find a solution that adds less complexity and is cleaner than the workaround (see patch).
User interface changes
None
API changes
Loading update files is possible without scanning for extensions.
Data model changes
None
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #21 | Screenshot from 2026-08-24 10-47-36.png | 371.15 KB | catch |
| #21 | Screenshot from 2026-08-24 10-47-32.png | 388.79 KB | catch |
Issue fork drupal-3303751
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:
Comments
Comment #2
phma commentedFixing issue links.
Comment #3
phma commentedComment #6
nicxvan commentedI'd love ideas for how to optimize this.
When testing an issue I output the number of scans for a single kernel test and it was dozens.
Comment #7
nicxvan commentedI think this might qualify for major.
Comment #9
nicxvan commentedComment #10
nicxvan commentedRerolled the patch so we can review the tests.
I didn't review it just applied it and resolved conflicts.
Comment #12
nicxvan commentedPostponing on #3618545: Always use infoParser in ExtensionDiscovery
Comment #13
catchI tried profiling this in combination with #3618545: Always use infoParser in ExtensionDiscovery and it's not reducing the extension scanning yet. Still 9 calls to ExtensionDiscovery::scanDirectory() when running a curtailed version of node module's GenericTest.
It might be different with a real install via dr or drush (or the UI) which I did not try to profile yet.
Comment #16
longwaveTried an alternative approach in MR!16818 which avoids threading a boolean flag through several methods, and includes test coverage to prove that we discover once and then cache.
Comment #17
nicxvan commentedThat does look a lot better, is there a reason we shouldn't use a memory cache though?
I hid the other MR.
Comment #18
nicxvan commentedGot a related issue here too.
Comment #19
longwaveSwitched to a memory cache, which means we can remove the explicit invalidation and use cache tags. Also rearranged the test coverage a bit to show that installing a module invalidates the cache correctly.
Comment #20
longwaveComment #21
catchProfiled a curtailed version of node module's GenericTest again and this time looked for the right method in the profiling data, #13 was just looking for the wrong thing.
As far as I can tell, this is saving 20 calls to ExtensionDiscovery::scan(), which works out about 15ms or half the time spent in UpdateRegistry::getUpdateFunctions(). Uploading xhgui screenshots.
On a real test run we're doing this hundreds/thousands of times so it will add up.
Comment #22
nicxvan commentedGot a few suggestions I added to the MR, I switched to MemoryCacheInterface since #3590964: Update/Install-time service overrides for cache should support MemoryCacheInterface landed.
I also removed the constructor comment.
The bigger change though was I moved the caching logic to
discoverExtensionsso that if we ever call that elsewhere the calls are cached as well.I also had one question on the test I'm not sure about.
Comment #23
longwaveComment #24
nicxvan commentedI think you might have accidentally force pushed over the changes we made this morning,
UpdateRegistryis back toCacheBackendInterfaceand the cache set is back in the call site instead of indiscoverExtensions.Comment #25
longwaveSorry for making a mess here, trying to do too many things at once, hopefully this is better.
Comment #26
nicxvan commentedNo worries, we've all done it, I use
--force-with-leasenow to prevent overwriting commits upstream.There is a failing test though:
Comment #27
longwaveComment #28
nicxvan commentedThis looks great now!
I don't think we need a CR for this.
Comment #29
nicxvan commentedComment #30
longwaveWondering if we need constructor BC because I think that drush also uses the update registry for deploy hooks.
Comment #31
nicxvan commentedProbably, which means we would need a CR:
https://github.com/drush-ops/drush/blob/92fa219135a89a8aab5a30309af0655b...
Comment #32
longwaveComment #33
nicxvan commentedConstructor parameters usually become required in the next version so 12, not 13.
Comment #34
longwaveNW for #33 and the change record.
Comment #35
nicxvan commentedI took a pass at the CR, it could use another set of eyes.
I added a suggestion for the CR update and version.
If the CR looks good I think this is RTBC assuming that test failure is random.
Comment #36
longwaveAdded a line to the CR that explicitly mentions who is affected, accepted the suggestion, edited the test for the same change.
Comment #39
catchCommitted/pushed to main and 11.x, thanks!
Comment #43
amateescu commentedCore tests are failing since this was merged, opened a quick followup MR.
Comment #44
nicxvan commentedThat change looks right, I actually came here to open the follow up fix.
Sorry I don't think I clear in 35, when I said:
I was trying to flag it was failing and if it passed on rerun it was random since I couldn't rerun it.
Comment #47
alexpottI pushed the test fix to 11.x and main - thanks @amateescu and @nicxvan
Comment #49
catchAh sorry I missed the failing test but also thanks for fixing it while I was out at the shops for 20 minutes...
Comment #50
quietone commentedPublished change record