Optimize route subscribers using indexed route collection

Created on 3 July 2023, 11 months ago
Updated 5 July 2023, 11 months ago

Problem/Motivation

The RouteBuildEvent allows different subscribers to alter the routes in a RouteCollection object.
Some subscribers (in particular for views) pass the route collection to a number of child objects, which can alter it (views Drupal\views\Plugin\views\display\PathPluginBase->alterRoutes()).

Subscribers that want to alter a specific route by route id are quite fast.

Subscribers that need to find routes by path need to check all routes in the route collection.
If there are many such subscribers, it can be quite slow.
It has a cost of O(n*m), with factors n and m that can both grow with the site complexity.
(n = number of routes, m = number of subscribers that need to scan all routes)
This happens only on cache rebuild, but still it is not good.

In πŸ› \Drupal\views\Plugin\views\display\PathPluginBase::alterRoutes() can become very slow on a site with lots of entities and JsonAPI Fixed there was already an attempt to optimize the route subscribers for views, but we are still at O(n*m) as before.

Steps to reproduce

Install opensocial distro, check the output from xdebug profiler on cache rebuild.
Or check on a custom project with many views displays that use a path plugin.

Proposed resolution

To push the cost to O(n * log n), we need to be able to pick a route by path _or_ by id.

So instead of a foreach($collection->all()) over all routes, it would just call $collection->getRoutesByPath($path) and only deal with these routes. The path can be a "normalized" version of the path as in RouteCompiler::getPathWithoutDefaults() and RouteCompiler::getPatternOutline().

The first id would be to add an index to RouteCollection. But this is not reliable, because the path in a route object can change during the altering, and the index won't know when it needs to be updated.

Some ideas:

  1. Route objects that ping the route collection whenever they change. This could be a wrapper of regular route objects, or a subclass.
  2. A new event type where instead of the regular RouteCollection, we pass around an IndexedRouteCollection. The route objects in that collection are immutable, and to change them we have to set the modified route object in the collection. This way the collection knows when to update the respective entries in the index.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component
RoutingΒ  β†’

Last updated 2 days ago

Created by

πŸ‡©πŸ‡ͺGermany donquixote

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

Comments & Activities

  • Issue created by @donquixote
  • πŸ‡©πŸ‡ͺGermany donquixote

    Actually we could index the subscribers instead of the routes!
    This could be done on the top level, or just within views.

    So roughly like this:
    - Each subscriber (or views page display) reports the route paths they are interested in.
    - For each route object, we compare against the reported paths.
    - For a route object and subscriber / views display with matching path, we run the alter process.
    - If the path in the route has changed, we check if the new path matches other plugins.

    Perhaps different subscribers have different route values they need to compare against. But I imagine this can be solved.

    Doing this on views level only would be less disruptive to the overall system.

Production build 0.69.0 2024