Problem/Motivation
\Drupal\Core\Config\StorageComparer::__construct() wraps each storage it is passed in a cached storage with a memory cache. During normal runtime this offloads queries from the database. However during the installer we replace all caches with memory caches and this results in a two memory caches storing the same data. This highlighted a problem due to the storage comparer keeping a stale copy of the active configuration storage. This results in lots of unnecessary work while installing a site from configuration due to the way \Drupal\Core\Update\UpdateRegistry::onConfigSave() determines what to do when it thinks the core.extension configuration is changing.
Remaining tasks
How to test...
- Install standard
- rename standard_install to _standard_install
- run drush config-export
- run drush si --existing-config
You can repeat the last step with and without the patch to see if it is different.
User interface changes
None
API changes
New method on the StorageComparer and its interface to put it into write mode.
Data model changes
None
Release notes snippet
N/a - while the StorageComparer has an interface - that's really bogus because it is not a service and we always instantiate using new.
| Comment | File | Size | Author |
|---|---|---|---|
| #36 | 3414349-10.2.11-36.do-not-test.patch | 7.47 KB | tgoeg |
| #35 | 3414349-10.2.x-35.do-not-test.patch | 7.43 KB | georob |
| #32 | 3414349-10.1.x-32.do-not-test.patch | 71.66 KB | alexpott |
| #5 | CleanShot 2023-12-08 at 02.59.44@2x.png | 132.14 KB | alexpott |
Issue fork drupal-3406929
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 #3
alexpottSomewhat surprisingly this seems to speed up installing standard from config by a second!
WITH MR
HEAD
Comment #4
alexpottHere's more evidence of why we're saving so much time...
Comment #5
alexpottSorry - I tried to paste html it... but it doesn't work so this is an image... comparing installing standard install with an without the MR applied.
As you can see there is a significant difference in the number of function calls. I've not quite managed to explain why this is so but I think it is because the storage comparer expects to be able to do stuff to the memory backend but it can't when there's yet another memory backend hidden from it.
Comment #6
phenaproximaI mean...I see no reason not to do this. That's a simple change and it only affects the installer.
Comment #7
wim leers6% speed decrease in function calls and execution time for a Standard profile install! For complex install profiles, this would likely be more.
❌
Let's find out (each the fastest of 3 attempts):I tested the wrong thing, see the issue summary!time vendor/bin/drush si standard -y)time vendor/bin/drush si minimal -y)✅ Then I tested the right thing: 🙈
time vendor/bin/drush si --existing-config -yHEADtime vendor/bin/drush si --existing-config -yMRIndeed a very significant difference! 👏
Suggested some explanatory comments on the MR.
Comment #8
bircherI am wondering if we could also get some improvement if in the storage comparer we only wrap the storages if they are not instanceof memory or cached storage.
But yes! this is huge for all CI which installs sites. so drupal sites that test with existing site test traits will see a difference!
Comment #9
bircherIe this bit:
But that could be a follow up issue too.
Comment #10
alexpottSo the more I think about it the more I wonder about problems caused by wrapping the target storage even when not at install time. I think any secondary config writes are going to cause problems because the memory cache storage that the storage comparer has added is going to be unaware.
Also I don't like making the underlying active storage public during the install.
Comment #11
bircheron second thought..
we can do that for memory storages and that would improve tests that use memory trorages in tests since the storage comparer is used in the storage copy trait.
For cached storages we would have to check what it is cached with. since on some this could still result in more queries than necessary and that is harder to check (ie we would have to add a method there that tells us the class name for the cache.. ) so the solution proposed here will get a big win without that extra complexity.
Comment #12
alexpottWe introduced this in #2411689: Use a MemoryBackend in StorageComparer so that configuration import validators don't have to reread data from disk or the db I think the use-case is still valid. So I think what needs to happen is that we disable the memory cache on the target storage in storage comparer once we enter the write phase of a configuration import.
Comment #13
alexpottWe also need to care about what happens in install_config_revert_install_changes() too.
Comment #14
borisson_I don't really understand #12, does that mean this issue needs to go back to needs work so we can implement the disabling of the memory cache, or is that something that would be considered a followup?
Comment #15
alexpott@borisson_ I working on understanding the problem here. I think we need to address the underlying problem - it's actually not just about the ConfigImporter during the installer (although it is most definitely worse there).
Here's what's happening as far as I can tell. The config importer is using the storages injected into the storage comparer for reading a writing. When the config importer installs a module, the module installer will rebuild the container and write to core.extension using the newly created
config.storageservice. At this point the storage in the storage comparer and the container are out of sync. Then once all the modules are installed we go into the importing config module. Normally this only handles configuration entities but simple config config is dealt with by making the module installer use the storage comparer's source storage. Thecore.extensionconfiguration is special however, because it does not belong to any module. For some reason the target storage's version of the core.extension configuration is completely out-of-date so when we re-save it, it looks like we're installing all the modules again - so any event listener that reacts to a module install via the config save is doing a lot of unnecessary work. An example of this is \Drupal\Core\Update\UpdateRegistry::onConfigSave().Comment #18
alexpottI've posted a new MR that results in the same speed-ups for installing from config but will also speed up importing configuration during regular runtime.
HEAD
WITH MR
Comment #19
alexpottNow to write a test...
Comment #20
bircherI think the analysis in #15 is spot on!
And the solution in the new MR makes sense to me. I left a comment about the interface.
Comment #21
alexpottComment #22
alexpottI've tweaked the issue summary and title to be about what is actually being fixed here. We still have double memory caches in the installer but I think we should fix that after we fix this as this is more important.
Comment #23
alexpottCreated #3407802: Using the ConfigImporter during installation results in a double memory cache to address the original issue.
Comment #24
borisson_I don't think needs a Change Record, because it is such an internal piece of the puzzle, the new Issue Summary really explains the problem well, because I now understand the actual problem. The fix seems like a good solution and the test coverage seems sufficient as well.
Comment #26
quietone commentedSince the changes I made involved adding return type hints I was going to set this to Needs review. However, the new test additions are failing, bisect tells me it failing at this commit.
Therefor setting to NW.
Comment #27
alexpottFixed the test. As all the changes were to test code setting back to RTBC.
Comment #28
alexpottFYI: We plan to remove the interface - see #3410037: Deprecate StorageComparerInterface
Comment #29
bircherI just checked again and: RTBC +1!
I am wondering now if this would also fix a bug in Config Split I have had trouble getting to the bottom of. I think it might!
Comment #31
catchI asked @alexpott whether trying to move these two classes into the service container would help with this situation (because the memory cache would be destroyed on container rebuild) and he said no it would be worse, and worse than the module installer. The logic and comments here all look fine and as good as we can do, and the speed-up is great. Committed/pushed to 11.x (which will also become 10.3.x), thanks!
Comment #32
alexpottHere's a patch for 10.1 and probably 10.2...
Comment #34
joelpittetI'm using the patch in comment #32, thanks @alexpott for posting it for 10.2
Comment #35
georob commentedPatch in #32 works great on 10.2.7, however our team noticed that there is a .cspellcache in the patch that references the patch exporters directory structure, so this patch is the patch in #32 without the .cspellcache. Also great that this is fixed in D11! Looks like that MR did not get the same file.
Comment #36
tgoeg commentedI needed to adapt the patch a little to apply to 10.2.11
Attaching it.
Comment #37
tgoeg commented