Add the ability to install multiple modules and only do a single container rebuild to ModuleInstaller

Created on 23 January 2024, 9 months ago
Updated 11 September 2024, about 1 month ago

Problem/Motivation

When you install a site (from config or the regular installer) - the vast majority of the time is spent rebuilding the container. We rebuild the container during every module install.

This issue is going to experiment with another approach.

Steps to reproduce

Install Drupal.

Proposed resolution

Rebuild the container a single time during ModuleInstaller::install() and adjust install loop accordingly.

Remaining tasks

User interface changes

Tweaks to user facing strings when installing multiple modules via configuration synchronisation.

API changes

New setting core.multi-module-install which sets how many modules be installed in one cycle of ModuleInstaller::install() during site install and configuration install.

Add argument to \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() so that we can install configuration in a modules config/install, config/optional, and other installed modules config/optional directories in separate operations. This is necessary as optional configuration has a lot of soft dependencies. New enum \Drupal\Core\Config\DefaultConfigMode to support this.

Rework \Drupal\Core\Config\ConfigInstaller::checkConfigurationToInstall() and \Drupal\Core\Config\ConfigInstaller::findDefaultConfigWithUnmetDependencies() to work with a list of modules. This has a side effect of fixing an existing bug in the config installer that hook_modules_installed is not called for already installed modules when a module is found that triggers an pre-exisiting configuration exception.

New \Drupal\Core\DrupalKernel::resetContainer() that removes instantiated services from the container. This allows for any service properties that are being used for caching to be refreshed after module install. This has a potential impact for any KernelTestBase test that extends \Drupal\KernelTests\KernelTestBase::register() and adds a synthetic service without marking the service as synthetic.

Data model changes

None

Release notes snippet

TBD

πŸ“Œ Task
Status

Needs review

Version

11.0 πŸ”₯

Component
BaseΒ  β†’

Last updated about 1 hour ago

Created by

πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

Live updates comments and jobs are added and updated live.
  • Performance

    It affects performance. It is often combined with the Needs profiling tag.

  • Needs tests

    The change is currently missing an automated test that fails when run with the original code, and succeeds when the bug has been fixed.

  • Needs change record

    A change record needs to be drafted before an issue is committed. Note: Change records used to be called change notifications.

Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @alexpott
  • Merge request !6289Resolve #3416522 "10.1.x" β†’ (Open) created by alexpott
  • Pipeline finished with Failed
    9 months ago
    #81456
  • Pipeline finished with Failed
    9 months ago
    #81464
  • Pipeline finished with Failed
    9 months ago
    #81481
  • Pipeline finished with Failed
    9 months ago
    Total: 578s
    #87887
  • Pipeline finished with Failed
    9 months ago
    Total: 568s
    #87991
  • Pipeline finished with Failed
    9 months ago
    Total: 573s
    #88034
  • Pipeline finished with Failed
    9 months ago
    Total: 442s
    #88322
  • Pipeline finished with Failed
    9 months ago
    Total: 620s
    #88364
  • Pipeline finished with Failed
    8 months ago
    #88623
  • Pipeline finished with Success
    8 months ago
    Total: 478s
    #89534
  • Status changed to Needs review 8 months ago
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Okay proved this is possible. The tests are green. Will update the issue summary with more detail.

  • Pipeline finished with Failed
    8 months ago
    Total: 545s
    #89579
  • Pipeline finished with Success
    8 months ago
    Total: 561s
    #89721
  • πŸ‡¬πŸ‡§United Kingdom catch

    This makes an install via drush take 10 seconds instead of 90 seconds. I haven't compared the UI installer yet and that's a bit harder to time, but if it's even a third faster that makes this a significant UX improvement for new installs. Also looks like it shaves anywhere from 30-60s off test runs (and maybe 10 minutes of actual runner time if you add up the shorter test runs for all functional and functional js tests together), and probably opens up new possibilities from there too like shifting distribution of the jobs slightly etc.

  • Pipeline finished with Success
    8 months ago
    Total: 642s
    #89827
  • Pipeline finished with Running
    8 months ago
    #89849
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Added some details about the API changes to the issue summary.

  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    🀩 Wow, epic undertaking! πŸ‘

    We rebuild the container during every module install.

    This issue is going to experiment with another approach.

    I think that means you expect this to provide a noticeable speed-up of test runs?

  • πŸ‡¬πŸ‡§United Kingdom catch

    I think that means you expect this to provide a noticeable speed-up of test runs?

    The fastest pipeline on this MR is 7m6s https://git.drupalcode.org/project/drupal/-/pipelines/89610, the fastest I've seen for core otherwise is about 7m30s. It also looks like all of the functional and functional javascript tests are finishing 30-60s quicker than usual. So it should be both a headline speed improvement for pipelines finishing, and also a reduction in overall runner time. I've opened πŸ“Œ Mark some more tests with @group #slow Needs review for some overhanging tests that the pipeline here identified, which might squeeze out a few more seconds or so from the longest running jobs once both are in.

  • πŸ‡¦πŸ‡ΊAustralia acbramley

    I'm very hopeful that this will fix πŸ› Memory usage increases linearly when (un)installing modules via config import Active

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @acbramley I don't think it will. I think πŸ› Memory usage increases linearly when (un)installing modules via config import Active is likely due to do something like [#3416294[ - I know you are not using the plugin module but the amount of memory you are using points to something going wrong.

  • πŸ‡¦πŸ‡ΊAustralia acbramley

    @alexpott that's a shame :(

    but the amount of memory you are using points to something going wrong

    Do you have any ideas of threads I could pull on? Pretty keen to get to the bottom of it!

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Great work @alexpott πŸ™Œ

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    I've created πŸ“Œ Fix Container::reset() and provide DrupalKernel::resetContainer() Needs review to split of the DrupalKernel / Container changes to allow resetting.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Here's patches for 10.2.x and 10.1.x - to make testing on existing sites easier. These patches include πŸ› ConfigInstaller::isSyncing() is not reset before module preinstall Fixed due to conflicts.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    alexpott β†’ changed the visibility of the branch 3416522-10.1.x to hidden.

  • last update 8 months ago
    Custom Commands Failed
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    I've testing this on a site that's built from commerce kickstart. I've found two issues:

    1. View data causes problems with modules/payment/config/install/views.view.commerce_order_payments.yml and modules/log/config/install/views.view.commerce_activity.yml
    2. The config_rewriter module has a bug in it when it does not detect a config sync correctly.

    I will open a issue against the config_rewriter module as it should not be rewriting configuration ever during a config sync. Funnily enough πŸ› ConfigInstaller::isSyncing() is not reset before module preinstall Fixed helps with this.

    For the views data issue - need to do 2 things - work why we're using views data when creating config during an install and if this is 100% necessary I think the way to go is to split simple config from config entity install while installing configuration from config/install.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Oh and re #16 once I'd fixed that... recreating the site locally went from 67 secs to 34 secs and I have a highly optimised setup...

  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    🀯

    recreating the site locally went from 67 secs to 34 secs and I have a highly optimised setup...

    πŸ₯΄

    Just RTBC'd the blocker πŸ“Œ Fix Container::reset() and provide DrupalKernel::resetContainer() Needs review .

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Fixed the issue with views and commerce kickstart install. Need to add a test for this case. I've tried to cause what I'm seeing with kickstart with core test modules and so far no dice.

    Adding patches for 10.2 and 10.1 testing.

  • Pipeline finished with Failed
    8 months ago
    Total: 490s
    #90610
  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Couple of tiny fixes to the 10.1 patch for testing ... hopefully should allow us to run the old Drupal CI tests...

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Hmm #20 / the fixes to install simple config and config entities separately was not quite right here are some new patches.

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    @daniel.bosen has tried the patch out on Thunder and has run into a problem with the ConfigInstaller::installDefaultConfig() change because Thunder uses the config_selector module. That module decorates the config installer and changes the behaviour of installDefaultConfig(). See https://github.com/thunder/thunder-distribution/actions/runs/7832035165/...

    So we need to have a think about that.

  • last update 8 months ago
    29,640 pass, 2 fail
  • Pipeline finished with Canceled
    8 months ago
    #90672
  • Pipeline finished with Success
    8 months ago
    #90680
  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    Install went fine on our install profile (138 installed modules and themes), didn't run tests yet.

    before:
     [notice] Performed install task: install_profile_themes [35.95 sec, 91.52 MB]
    ....
     [notice] Performed install task: install_finished [62.81 sec, 178.25 MB]
    
    after:
     [notice] Performed install task: install_profile_themes [24.95 sec, 99.71 MB]
    ...
     [notice] Performed install task: install_finished [50.77 sec, 166.01 MB]
    

    Total install time wennt from 62s to 50s, but as you can see, around 50% of that time is installing default content and stuff like that. module install itself went from 35s to 24s.

  • Status changed to Needs work 8 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Seems to have some open threads if those could be closed or check marked

    For it's worth I applied locally and installed a starterkit profile I use for new projects (maybe 40ish modules) and everything imported just fine.

  • πŸ‡¬πŸ‡§United Kingdom catch

    @daniel.bosen has tried the patch out on Thunder and has run into a problem with the ConfigInstaller::installDefaultConfig() change because Thunder uses the config_selector module. That module decorates the config installer and changes the behaviour of installDefaultConfig().

    Could we introduce a .info.yml key that forces a container rebuild on install? I think that would be OK in a minor release with a change record. Or worst case we could assume it's set in 10.x (so the behaviour doesn't actually change), then flip it in 11.x so that the performance improvement kicks in.

  • Pipeline finished with Success
    8 months ago
    Total: 488s
    #106884
  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    πŸ“Œ Fix Container::reset() and provide DrupalKernel::resetContainer() Needs review is in … oh I see @alexpott is already on this! 😁

  • πŸ‡³πŸ‡±Netherlands kingdutch

    This looks really interesting! Would this also fix the issue discussed in πŸ› Optional configuration from dependence modules is installed before installing module when installing profile Needs work ? That's something we're running into at Open Social. It seems like possibly the following mentions that it does?

    Rework \Drupal\Core\Config\ConfigInstaller::checkConfigurationToInstall() and \Drupal\Core\Config\ConfigInstaller::findDefaultConfigWithUnmetDependencies() to work with a list of modules. This has a side effect of fixing an existing bug in the config installer that hook_modules_installed is not called for already installed modules when a module is found that triggers an pre-exisiting configuration exception.

    However, the initial "split up in multiple parts" would suggest that we go the opposite direction and make the site installer more different from how individual module installs work rather than bringing the two closer together.

  • Pipeline finished with Failed
    2 months ago
    Total: 397s
    #247302
  • Pipeline finished with Failed
    2 months ago
    Total: 117s
    #247320
  • Pipeline finished with Failed
    2 months ago
    Total: 111s
    #247325
  • Pipeline finished with Success
    2 months ago
    Total: 433s
    #247326
  • πŸ‡¬πŸ‡§United Kingdom catch

    Merged in 11.x (after a very quickly aborted attempt to rebase), and back to green - a couple of small merge conflicts, recipe required module install needed to move to multiple to fix that test.

    Going back to #3416522-22: Add the ability to install multiple modules and only do a single container rebuild to ModuleInstaller β†’ I think we should consider making this opt-in per module at least to start with.

    - add a .info.yml that is something like 'multiple_install: true' or 'container_rebuild_required: true', and default it so that the container is rebuilt immediately after module install if it's not set.

    - add multiple_install: true/container_rebuild_required: false to all core modules
    - then we can open issues against the top 100-200 modules to add it there too.

    That should get us the bulk of the performance improvements here with very little risk of regressions.

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Sounds like a plan to me.

  • Status changed to Needs review 2 months ago
  • πŸ‡¬πŸ‡§United Kingdom catch

    Added support for container_rebuild_required: true (which also starts a new group if it's not set).

    Then in a couple of separate commits, added container_rebuild_require: false to every core (non-test) module.

    Less than three minute test run on https://git.drupalcode.org/project/drupal/-/jobs/2424744 shows the optimisation still working.

    This should fix the situation in #3416522-22: Add the ability to install multiple modules and only do a single container rebuild to ModuleInstaller β†’ because every module that doesn't specify container_rebuild_required: false will be installed at the end of each batch of modules (and if there are lots of them sequentially, in their own individual single module batch).

    I also addressed some of the outstanding minor feedback on the MR and closed a couple of threads where the status quo seemed OK.

    Needs review again for the new approach and the contents of the MR in general.

    This is still tagged for needs tests. We could add a testing module that increments a counter every container rebuild, then add assertions after install on how many times the container was rebuilt. Then we can add different combinations of test modules, with and without container_rebuild_required: true to different test install profiles and check the count. The problem with this is I can see it breaking due to changes that don't involve breaking the logic (like increasing or decreasing the default batch size, or adding/removing a module dependency somewhere), so it's not perfect. Better ideas would be better.

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Could we open up follow ups for the todos? Just so they don't get lost in the code shuffle.

  • First commit to issue fork.
  • Pipeline finished with Failed
    about 1 month ago
    Total: 166s
    #279250
  • Pipeline finished with Failed
    about 1 month ago
    Total: 474s
    #279259
  • πŸ‡­πŸ‡ΊHungary mxr576 Hungary

    I see a new container_rebuild_required in the MR#6290, that needs a change record, maybe a documentation update about info.yml keys as well.

  • πŸ‡¬πŸ‡§United Kingdom catch
  • πŸ‡ΊπŸ‡ΈUnited States moshe weitzman Boston, MA

    That test approach sounds reasonable to me. Its OK that it may occasionally need updating if default batch size changes, and so on.

  • The Needs Review Queue Bot β†’ tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

Production build 0.71.5 2024