Problem/Motivation

> $service = \Drupal::service('paramconverter_manager');
= Drupal\Core\ParamConverter\ParamConverterManager {#1601}

> (new ReflectionProperty($service, 'converters'))->getValue($service);
= [
    "drupal.proxy_original_service.paramconverter.views_ui" => Drupal\views_ui\ParamConverter\ViewUIConverter {#1608},
    "drupal.proxy_original_service.paramconverter.configentity_admin" => Drupal\Core\ParamConverter\AdminPathConfigEntityConverter {#1603},
    "paramconverter.entity" => Drupal\Core\ParamConverter\EntityConverter {#1606},
    "paramconverter.entity_revision" => Drupal\Core\ParamConverter\EntityRevisionParamConverter {#1607},
    "drupal.proxy_original_service.paramconverter.menu_link" => Drupal\Core\ParamConverter\MenuLinkPluginConverter {#1605},
    "drupal.proxy_original_service.node_preview" => Drupal\node\ParamConverter\NodePreviewConverter {#1609},
  ]

Services tagged paramconverter are often declared as lazy in order to try and avoid instantiating them early. But it appears the lazy flag is ignored and the actual service (not the proxy) is instantiated when ParamConverterManager is constructed - which happens on every page request.

paramconverter services should only be needed when required by a route so we should lazily load all of them instead of each service having to declare itself lazy (which appears to be broken anyway).

Steps to reproduce

See above, or also try adding a breakpoint to ViewUIConverter::__construct().

Proposed resolution

Convert ParamConverterManager to use a service locator.
Remove the lazy proxy services for paramconverter services.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3436295

Command icon 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

longwave created an issue. See original summary.

longwave’s picture

By adding logging to Container::get():

    file_put_contents('/tmp/container.log', str_repeat('>', count($this->loading)) . " $id\n", FILE_APPEND);
    $this->loading[$id] = TRUE;

    try {
      $service = $this->createService($definition, $id);
    }

we can see the chains of services that are created:

>>> route_enhancer.param_conversion
>>>> paramconverter_manager
>>>>> drupal.proxy_original_service.paramconverter.views_ui
>>>>>> entity_type.manager
>>>>>>> string_translation
>>>>>>>> string_translator.custom_strings
>>>>>>> entity.last_installed_schema.repository
>>>>>> tempstore.shared
>>>>>>> keyvalue.expirable
>>>>>> router.admin_context
>>>>>> entity.repository
>>>>>>> context.repository
>>>>> drupal.proxy_original_service.paramconverter.configentity_admin
>>>>> paramconverter.entity
>>>>> paramconverter.entity_revision
>>>>> drupal.proxy_original_service.paramconverter.menu_link
>>>>>> plugin.manager.menu.link
>>>>>>> Drupal\Core\Menu\MenuTreeStorageInterface
>>>>>>> menu_link.static.overrides
>>>>> drupal.proxy_original_service.node_preview
>>>>>> tempstore.private

If we lazily instantiate paramconverter_manager's services properly then we remove all the direct children and perhaps some of the other children from being required on almost all page loads.

longwave’s picture

Status: Active » Needs review

smustgrave made their first commit to this issue’s fork.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

Rebased to make sure everything passed with all the changes from the last month.

Unfortunately caused a large number of failures.

longwave’s picture

It appears that #[AutowireLocator] does not support ordering services by priority. This means that the services in the wrong order when iterating through all of them, and this causes bugs and the tests to fail. I traced through the compiler builder and in PriorityTaggedServiceTrait::findAndSortTaggedServices() the services are initially correctly sorted but when the final array is built the services are indexed by service name and the ordering is lost.

https://github.com/symfony/symfony/issues/42506 implies this did work at some point - at least when using !tagged_locator instead of autowiring maybe - but it doesn't appear to here.

longwave’s picture

In fact, PriorityTaggedServiceTrait::findAndSortTaggedServices() does sort services correctly, but then later on ServiceLocatorTagPass::map() calls ksort($services) which destroys the order.

longwave’s picture

Title: ParamConverterManager lazy services are broken and should use a service locator » [PP-upstream] ParamConverterManager lazy services are broken and should use a service locator
Status: Needs work » Postponed
longwave’s picture

Title: [PP-upstream] ParamConverterManager lazy services are broken and should use a service locator » ParamConverterManager lazy services are broken and should use a service locator
Status: Postponed » Needs work

Upstream bug is fixed in Symfony 7.0.9

https://github.com/symfony/symfony/pull/57581

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

longwave changed the visibility of the branch 3436295-paramconvertermanager-lazy-services to hidden.

longwave’s picture

Status: Needs work » Needs review

The existing MR was too difficult to rebase, so I started again.

godotislate’s picture

Some minor comments on the MR, fine to ignore.

Do we need an automated test that the paramconverter services are loaded lazily?

godotislate’s picture

Status: Needs review » Needs work

Looks like in a similar issue #3538294: Regression: All stack middlewares are constructed at the same time even for cached pages there were automated tests for lazy loading.

longwave’s picture

Status: Needs work » Needs review

Thanks for the review and suggestions, I applied them. Then I cleaned up the tests so we don't have addConverter() any more, and added an explicit test for lazy instantiation.

godotislate’s picture

Status: Needs review » Reviewed & tested by the community

Test changes look good.

godotislate’s picture

Status: Reviewed & tested by the community » Needs work

Actually one comment about the laziness check.

longwave’s picture

Status: Needs work » Needs review

Exceptions are only caught after they have been expected.

If I swap the order:

-    $this->expectExceptionMessage('ErrorConverter was instantiated');
     $manager->convert($defaults);
+    $this->expectExceptionMessage('ErrorConverter was instantiated');

then it fails:

$ ddev test core/tests/Drupal/Tests/Core/ParamConverter/ParamConverterManagerTest.php
Disabled xdebug
PHPUnit 11.5.44 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.5.1
Configuration: /var/www/html/drupal/core/phpunit.xml.dist

............E..                                                   15 / 15 (100%)

Time: 00:00.021, Memory: 6.00 MB

There was 1 error:

1) Drupal\Tests\Core\ParamConverter\ParamConverterManagerTest::testLazyInstantiation
Exception: ErrorConverter was instantiated

/var/www/html/drupal/core/tests/Drupal/Tests/Core/ParamConverter/ParamConverterManagerTest.php:243
/var/www/html/drupal/core/tests/Drupal/Tests/Core/ParamConverter/ParamConverterManagerTest.php:162
longwave’s picture

Having said that using exceptions for control flow isn't ideal, but perhaps it's okay in a test?

godotislate’s picture

Status: Needs review » Reviewed & tested by the community

Oh you're right, I misread the code flow in PHPUnit\Framework\TestCase::runTest(). While a try statement wraps running the test method, the exception expectations need to be set before the exception is thrown.

longwave’s picture

Status: Reviewed & tested by the community » Needs review

Improved the test as per the suggestion, we can reuse the existing converter as well to make the test shorter.

godotislate’s picture

Status: Needs review » Reviewed & tested by the community

Test changes look good.

  • catch committed 5e4a4a4e on main
    fix: #3436295 ParamConverterManager lazy services are broken and should...
catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Fixed

Committed/pushed to main and 11.x, thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • catch committed 919699c7 on 11.x
    fix: #3436295 ParamConverterManager lazy services are broken and should...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.