Using ThunderEntitySubRequestBase breaks the general GraphQL cache

Created on 16 October 2024, 3 months ago

Problem/Motivation

GraphQL never reads from the cache when using a producer that uses ThunderEntitySubRequestBase functionality. GraphQL uses a cache suffix that's created from the cache context values.

When the cache is read the suffix will have a 'route' cache context that corresponds to the graphql server route. The cache read is a miss since nothing is written yet. But let's say it creates a suffix ABC.

When the cache entry is written it will again create a suffix from the cache contexts, but because we invoked ThunderEntitySubRequestBase the cache context value for 'route' will now be the entity route if the path is associated with the entity. The suffix is now DEF, the cache is written with that suffix, but because it's different from the suffix that's read, a cache hit will never be made.

This doesn't affect the persistent queries.

Steps to reproduce

  • Set 'development' to FALSE and make sure caching is enabled in the graphql server.
  • Use a simple query like query PAGE { menu(id: \"main\", path: \"/themen\") {name}}
  • Now make a POST request to the server, set a breakpoint e.g. in ThunderLanguage since that's used in menu resolving.
  • Make the request, the breakpoint will trigger every time.

Proposed resolution

As far as I can tell this is an existing issue in Drupal 🐛 Requests are pushed onto the request stack twice, popped once Needs work .

To fix it, the paths can be checked in ThunderEntitySubRequestBase after the sub request is made and if the path doesn't match the path of the initial (main) request then we can pop a request off the request stack, which would fix the issue until it's fixed in Drupal properly.

Remaining tasks

User interface changes

API changes

Data model changes

🐛 Bug report
Status

Active

Version

7.3

Component

Code

Created by

🇸🇮Slovenia miha.wagner

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

Merge Requests

Comments & Activities

  • Issue created by @miha.wagner
  • 🇬🇧United Kingdom alexpott 🇪🇺🌍

    @miha.wagner great sleuthing. I agree this is a likely cause of a cache problem - it'd be awesome to test this somehow.

    I've tried to debug this on a vanilla thunder install. I'm running \Drupal\Tests\thunder_gqls\Functional\CacheInvalidationTest::testJsonLdCacheInvalidation and I've added the code fragment below:

      public function testCachingCalls(): void {
        $query = 'query ($path: String!) {
          jsonld(path: $path)
        }';
    
        $variables = '{"path": "/come-drupalcon-new-orleans"}';
    
        $response = $this->getJsonLdFromQuery($query, $variables);
        $this->assertEquals('Come to DrupalCon New Orleans', $response['@graph'][0]['name']);
    
        $response = $this->getJsonLdFromQuery($query, $variables);
        $this->assertEquals('Come to DrupalCon New Orleans', $response['@graph'][0]['name']);
      }
    

    I expect there to be two subrequests.... but I only get one. What am I doing wrong? Or is this something specific to menu resolving.

  • 🇸🇮Slovenia miha.wagner

    This is why I should have tested on a more plain installation. So the issue is when the 'route' cache context is added. Also probably when 'url.path' is added though I did not test this.

    Create a basic article on the site. Then this request can be used:

    {"query":"query MyQuery {\n breadcrumb(\n path: \"<path-to-the-article>\"\n ) {\n title\n url\n }\n}\n","variables":null,"operationName":"MyQuery"}

    The breadcrumbs builder do add the 'route' cache context to the response. So triggering this will always invoke the sub-request, though it should only trigger it once.

Production build 0.71.5 2024