Problem/Motivation
Inspired by @berdir in #3340999: The path-alias preloading is not used if the "Enforce clean and canonical URLs" option is enabled.
Depends on #3394423: Adopt the Revolt event loop for async task orchestration.
We currently have a path preload cache, added by me and others in #456824: drupal_lookup_path() speedup - cache system paths per page.. It records all the system paths found on a page when the cache is cold, writes them to a per-URL cache object, then when that page is requested again, if a path alias is requested, uses that cache item to preload all the path aliases.
It saves a lot of queries but is less effective in 11.x than it was in 7.x because we now have render caching and dynamic_page_cache, meaning the amount of times we're generating the same path aliases on the same page will be lower (if still possibly more than once every 24 hours). Also it takes up quite a lot of cache space.
In #3340999: The path-alias preloading is not used if the "Enforce clean and canonical URLs" option is enabled @berdir suggested looking at potentially removing it - this would be fine on sites with good render cache hit rates, but sites with lots of content changes (comment posting on the same URL, node list cache tag constantly invalidated) might still do a fair bit of extra queries.
Steps to reproduce
Proposed resolution
I think we might potentially have a way to do at least some of the multiple loading, without the caching - if so it would be best of both worlds.
When we look up an alias, just before we get to the point where we need to look it up from the database, we can check if we're inside a Fiber and if so suspend. Before doing so, we add the system path to a class property.
Alias lookups happen via Url::toString(), which is called when URLs are printed in twig templates, this means that we need twig rendering to happen inside a Fiber.
There are two issues that enable this:
#3437499: Use placeholdering for more blocks which puts more blocks into placeholders.
#3496835: Render children in fibers which renders render children in fibers.
This enables a code flow like the following:
Fiber 1 - hits a path alias lookup, adds to list, suspends.
Fiber 2 - hits a path alias lookup, adds to list, suspends.
Fiber 3 - hits a path alias lookup, adds to list, suspends.
Fiber 1 - resumes, loads all three path aliases, adds them to the static cache.
Fiber 2 - gets the alias from the static cache
Fiber 3 - gets the alias from the static cache.
This doesn't rely on anything async, it's more that we can force parallel execution of path lookups instead of serial, by backing out of one, starting another, backing out etc. then when we get back to the first one, load everything we need into the static cache and save the individual lookups.
The path alias logic is minimal - a few lines. The hard thing was finding the right place to initiate the Fibers from due to the way region/block/link rendering works.