Routes are being cached with no parameters (ex: 404 routes)

Created on 30 March 2020, over 4 years ago
Updated 23 August 2023, about 1 year ago

Drupal's Routing/RouteProvider caches all incoming request's routes and parameters for future use of course. This happens after all other processes are done and after all processInbounds (most importantly from the content POV).

The case: there's a link on the current page to a place that isn't accessible from that page's context - could be language, could be permissions, domain - just a link to node that's not supposed to be there and restricted by some logic - anything will do as long as it's entity reference.

When the page renders, Drupal looks for the routes and controllers responsible for this link (most importantly it should be entity reference) - and can't find them in the current context (let's take different language context and using the link field).

What happens - it can't find the routes and controllers for this reference (in the current language since the linked node is in different language) but still caches the empty route params for this "unaccessible" path. If you hit that path it should work as expected but it don't since it's already cached as 404, so it starts throwing 404.

The inconsistent part is that if you first hit the real thing - it's going to be cached correctly and later won't break. Unfortunately that's a pretty big "IF" and not very consistent and logical.

My solution was to add a patch in the core/lib/Drupal/Core/Routing/RouteProvider.php to prevent empty routes being cached.

Here is an example of broken (empty) cached route:

a:3:{s:4:"path";s:66:"/patients/prevention/plant-products/cancer-prevention-sulforaphane";s:5:"query";a:0:{}s:6:"routes";O:41:"Symfony\Component\Routing\RouteCollection":2:{s:49:" Symfony\Component\Routing\RouteCollection routes";a:0:{}s:52:" Symfony\Component\Routing\RouteCollection resources";a:0:{}}}

Readable:

Array
(
    [path] => /patients/prevention/plant-products/cancer-prevention-sulforaphane
    [query] => Array()
    [routes] => __PHP_Incomplete_Class Object
        (
            [__PHP_Incomplete_Class_Name] => Symfony\Component\Routing\RouteCollection
            [ Symfony\Component\Routing\RouteCollection routes] => Array()
            [ Symfony\Component\Routing\RouteCollection resources] => Array()
        )
)

The questions here are:
1. how big of an impact is it to not cache empty routes?
2. why does Drupal cache them at all?
3. is there a better way/place than what I found/did to fix this issue?

πŸ› Bug report
Status

Needs work

Version

11.0 πŸ”₯

Component
RoutingΒ  β†’

Last updated 2 days ago

Created by

πŸ‡§πŸ‡¬Bulgaria kadiiski

Live updates comments and jobs are added and updated live.
  • Needs issue summary update

    Issue summaries save everyone time if they are kept up-to-date. See Update issue summary task instructions.

  • 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.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡¨πŸ‡¦Canada drclaw

    This feels like a bit of a sledgehammer fix since many of the cached "empty" routes could be belong to actual 404s (in which case you _want_ them to be cached for performance). The problem here isn't that the empty routes are being cached, but that they aren't being cached specifically enough.

    The thing is that it's usually a module that's introducing the various context that makes a route accessible in one case and not another. In that case it's really up to the module to somehow inform the routing system about those contexts. Luckily core actually does have a way to do this with CacheableRouteProviderInterface::addExtraCacheKeyPart(). This was added in #3007669: Add publishing status to path aliases β†’ and allows you to add your own contexts to the route cache key.

    An example of this is the Domain Access + Country Path module. Country path lets you toggle the active domain by path so:

    - example.com/uk -> Domain #1
    - example.com/us -> Domain #2

    In this case when you see a link to example.com/us/whatever from example.com/uk on a cold cache, the empty route gets cached for all domains because the routing system doesn't know that there's a difference based if you're seeing the link on /us vs /uk. In this case Country path or even Domain access could inform the router about this difference. And in fact, there is actually a patch doing just that for Domain Access ✨ Use core route provider with addExtraCacheKeyPart for route caching? Needs review .

    Really the hard part is usually figuring out specifically which module is introducing the contexts that cause this behaviour to happen. Ideally, the router would pull from the various render contexts that are floating around automatically, but I've only seen that eluded to in https://www.drupal.org/project/drupal/issues/2799543#comment-13344318 β†’

Production build 0.71.5 2024