Update container namespaces reference instead of hard set

Created on 4 April 2023, over 1 year ago
Updated 5 April 2023, over 1 year ago

Problem/Motivation

When an extension is (un)installed, DrupalKernel runs a container rebuild. During container rebuild in \Drupal\Core\DrupalKernel::compileContainer it updates a container parameter container.namespaces by a hard set with new values.

This parameter is used by container.namespaces service which is used by Plugin API (usually Plugin Managers) and maybe other code.

When you install and especially uninstall an extension (let's say a module), module_installer service triggers \Drupal\Core\Extension\ModuleInstaller::updateKernel which leads to container rebuild via \Drupal\Core\DrupalKernelInterface::updateModules call. In that scenario container.namespaces the container parameter is replaced by a new one, it also invalidates the previous instance of container.namespaces service (which is an ArrayObject), and all instantiated plugin managers at this point are using an old namespace information. They must request container.namespaces again when this is happens to get a fresh list of namespaces.

Steps to reproduce

  1. Get namespaces from container: $namespaces = $this->container->get('container.namespaces');
  2. Install/remove some module.
  3. The $namespaces will hold an old namespaces.

So, basically, this affects every plugin manager that relies on namespaces and such information.

Proposed resolution

The good thing that container.namespaces parameter is an ArrayObject, not a raw array. This means, we can update this particular instance, and all plugin manager will have updated namespaces on container rebuild.

So, we need to update this instance instead of replacing it.

Remaining tasks

Decide how to fix it.

πŸ› Bug report
Status

Needs work

Version

10.1 ✨

Component
BaseΒ  β†’

Last updated about 11 hours ago

Created by

πŸ‡·πŸ‡ΊRussia niklan Russia, Perm

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @niklan
  • πŸ‡·πŸ‡ΊRussia niklan Russia, Perm

    This simple test shows that the parameter is replaced with a new value instead of updating the existing instance.

  • πŸ‡ΊπŸ‡¦Ukraine chesn0k Ukraine, Odessa

    +1

  • πŸ‡·πŸ‡ΊRussia niklan Russia, Perm

    Maybe problem even not in a hard updated values. The hook config_translation_config_translation_info() from steps to reproduce gets EntityTypeManager from an updated container.

    So the problem most likely in \Drupal\Core\DrupalKernel::updateModules. This method takes care of adding new namespaces, but it not removes the ones for uninstalled modules. This also leads to this problem, even if container.namespaces will be updated via an existing instance.

    Basically, when you uninstall the module, updated container still pass namespace of this module to other services. πŸ€”

  • Status changed to Needs review over 1 year ago
  • πŸ‡·πŸ‡ΊRussia niklan Russia, Perm

    Added workaround: if container.namespces a service is already instantiated, we update this instance of an ArrayObject. In that case, all previously instantiated services will also hold an updated list of namespaces.

  • πŸ‡·πŸ‡ΊRussia niklan Russia, Perm

    Added important note for steps to reproduce. The config entity should also define the edit-form link template. Because without it, the hook will skip that entity and bug won't be reproduced.

  • πŸ‡·πŸ‡ΊRussia niklan Russia, Perm

    Hah, there is even a todo sounds like the problem I faced in \Drupal\config_translation\ConfigMapperManager::getDiscovery

          // @todo If the list of installed modules and themes is changed, new
          //   definitions are not picked up immediately and obsolete definitions
          //   are not removed, because the list of search directories is only
          //   compiled once in this constructor. The current code only works due to
          //   coincidence: The request that installs (for instance, a new theme)
          //   does not instantiate this plugin manager at the beginning of the
          //   request; when routes are being rebuilt at the end of the request,
          //   this service only happens to get instantiated with the updated list
          //   of installed themes.
    

    So it's sounds like a different issue. I did testes with my fix with a problem project, and it doesn't help, than it's not about incorrect namespaces.

    But still, it leads me to this topic. Should we update an instantiated array object with namespaces instead of pushing a new one? Because it still a problem for cases when this list is received and then container is rebuilt. All code who uses container.namespaces service will be affected by this problem. The tests actually shows it.

  • Status changed to Needs work over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    The failures in #5 main patch seem legit.

  • πŸ‡·πŸ‡ΊRussia niklan Russia, Perm

    I'm actually needing an advice here. The test for #5 requires test to be a Kernel one, but the DrupalKernel in HEAD is Unit. Switching it to Kernel causes failures in #5

    1) Drupal\Tests\Core\DrupalKernel\DrupalKernelTest::testFindSitePath
    Failed asserting that two strings are equal.
    --- Expected
    +++ Actual
    @@ @@
    -'sites/example'
    +'sites/simpletest/47698822'
    

    I'm actually not like the idea to switch DrupalKernelTest to Kernel just for a single check, but I'm not sure how to handle it in other way? So should I update DrupalKernelTest::testFindSitePath accordingly or better create another test file for Kernel DrupalKernel testing?

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

    Would post to #contribute for help.

    This fix seemed to cause this failures so there may be backwards compatibility concern now.

Production build 0.71.5 2024