Skip to content
Snippets Groups Projects
Unverified Commit 58a9b746 authored by Sebastian Siemssen's avatar Sebastian Siemssen Committed by GitHub
Browse files

Fixing sub request buffer and cache metadata collection. (#547)

parent e0918ccd
No related branches found
No related tags found
No related merge requests found
......@@ -141,6 +141,8 @@ class BlocksByRegion extends FieldPluginBase implements ContainerFactoryPluginIn
});
return function ($value, array $args, ResolveContext $context, ResolveInfo $info) use ($resolve) {
/** @var \Drupal\graphql\GraphQL\Cache\CacheableValue $response */
$response = $resolve();
$metadata = new CacheableMetadata();
$metadata->addCacheTags(['config:block_list']);
$blocks = array_map(function (Block $block) {
......@@ -151,10 +153,10 @@ class BlocksByRegion extends FieldPluginBase implements ContainerFactoryPluginIn
else {
return $block;
}
}, $resolve());
}, $response->getValue());
foreach ($blocks as $block) {
yield new CacheableValue($block, [$metadata]);
yield new CacheableValue($block, [$metadata, $response]);
}
};
}
......
......@@ -7,6 +7,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\graphql\GraphQL\Buffers\SubRequestBuffer;
use Drupal\graphql\GraphQL\Cache\CacheableValue;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -101,9 +102,12 @@ class Breadcrumbs extends FieldPluginBase implements ContainerFactoryPluginInter
});
return function ($value, array $args, ResolveContext $context, ResolveInfo $info) use ($resolve) {
$links = $resolve();
/** @var \Drupal\graphql\GraphQL\Cache\CacheableValue $response */
$response = $resolve();
$links = $response->getValue();
foreach ($links as $link) {
yield $link;
yield new CacheableValue($link, [$response]);
}
};
}
......
......@@ -7,6 +7,7 @@ use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\graphql\GraphQL\Buffers\SubRequestBuffer;
use Drupal\graphql\GraphQL\Cache\CacheableValue;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -82,16 +83,18 @@ class LanguageSwitchLinks extends FieldPluginBase implements ContainerFactoryPlu
});
return function () use ($resolve) {
list($current, $links) = $resolve();
/** @var \Drupal\graphql\GraphQL\Cache\CacheableValue $response */
$response = $resolve();
list($current, $links) = $response->getValue();
if (!empty($links->links)) {
foreach ($links->links as $link) {
// Yield the link array and the language object of the language
// context resolved from the sub-request.
yield [
yield new CacheableValue([
'link' => $link,
'context' => $current,
];
], [$response]);
}
}
};
......
......@@ -5,6 +5,7 @@ namespace Drupal\graphql_core\Plugin\GraphQL\Fields\Routing\InternalUrl;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\graphql\GraphQL\Buffers\SubRequestBuffer;
use Drupal\graphql\GraphQL\Cache\CacheableValue;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -114,7 +115,9 @@ class InternalRequest extends FieldPluginBase implements ContainerFactoryPluginI
});
return function ($value, array $args, ResolveContext $context, ResolveInfo $info) use ($resolve) {
yield $resolve();
/** @var \Drupal\graphql\GraphQL\Cache\CacheableValue $response */
$response = $resolve();
yield new CacheableValue($response->getValue(), [$response]);
};
}
}
......
......@@ -172,8 +172,11 @@ class RouteEntity extends FieldPluginBase implements ContainerFactoryPluginInter
});
return function ($value, array $args, ResolveContext $context, ResolveInfo $info) use ($resolve, $entity) {
$language = $resolve();
$entity = $this->entityRepository->getTranslationFromContext($entity, $language);
/** @var \Drupal\graphql\GraphQL\Cache\CacheableValue $response */
$response = $resolve();
$entity = $this->entityRepository->getTranslationFromContext($entity, $response->getValue());
$entity->addCacheableDependency($response);
return $this->resolveEntity($entity, $value, $args, $info);
};
}
......
......@@ -4,6 +4,8 @@ namespace Drupal\graphql\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\graphql\GraphQL\Buffers\SubRequestResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
......@@ -20,22 +22,38 @@ class SubrequestExtractionController extends ControllerBase {
*/
protected $requestStack;
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('request_stack'),
$container->get('language_manager')
$container->get('language_manager'),
$container->get('renderer')
);
}
/**
* {@inheritdoc}
* SubrequestExtractionController constructor.
*
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* The request stack.
* @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
* The language manager service.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The rewnderer service.
*/
public function __construct(RequestStack $requestStack, LanguageManagerInterface $languageManager) {
public function __construct(RequestStack $requestStack, LanguageManagerInterface $languageManager, RendererInterface $renderer) {
$this->requestStack = $requestStack;
$this->languageManager = $languageManager;
$this->renderer = $renderer;
}
/**
......@@ -51,7 +69,18 @@ class SubrequestExtractionController extends ControllerBase {
// TODO: Remove this once https://www.drupal.org/project/drupal/issues/2940036#comment-12479912 is resolved.
$this->languageManager->reset();
return new SubRequestResponse($callback());
// Collect any potentially leaked cache metadata released by the callback.
$context = new RenderContext();
$result = $this->renderer->executeInRenderContext($context, function () use ($callback) {
return $callback();
});
$response = new SubRequestResponse($result);
if (!$context->isEmpty()) {
$response->addCacheableDependency($context->pop());
}
return $response;
}
}
......@@ -2,7 +2,9 @@
namespace Drupal\graphql\GraphQL\Buffers;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Url;
use Drupal\graphql\GraphQL\Cache\CacheableValue;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\HttpKernelInterface;
......@@ -60,20 +62,27 @@ class SubRequestBuffer extends BufferBase {
* {@inheritdoc}
*/
protected function getBufferId($item) {
/** @var \Drupal\Core\Url $url */
$url = $item['url'];
return $url->toString();
/** @var \Drupal\Core\GeneratedUrl $url */
$url = $item['url']->toString(TRUE);
return hash('sha256', json_encode([
'url' => $url->getGeneratedUrl(),
'tags' => $url->getCacheTags(),
'contexts' => $url->getCacheContexts(),
'age' => $url->getCacheMaxAge(),
]));
}
/**
* {@inheritdoc}
*/
public function resolveBufferArray(array $buffer) {
/** @var \Drupal\Core\Url $url */
$url = reset($buffer)['url'];
/** @var \Drupal\Core\GeneratedUrl $url */
$url = reset($buffer)['url']->toString(TRUE);
$currentRequest = $this->requestStack->getCurrentRequest();
$request = Request::create(
$url->getOption('routed_path') ?: $url->toString(),
$url->getGeneratedUrl(),
'GET',
$currentRequest->query->all(),
$currentRequest->cookies->all(),
......@@ -93,6 +102,9 @@ class SubRequestBuffer extends BufferBase {
/** @var \Drupal\graphql\GraphQL\Buffers\SubRequestResponse $response */
$response = $this->httpKernel->handle($request, HttpKernelInterface::SUB_REQUEST);
if ($url instanceof CacheableDependencyInterface) {
$response->addCacheableDependency($url);
}
// TODO:
// Remove the request stack manipulation once the core issue described at
......@@ -101,7 +113,9 @@ class SubRequestBuffer extends BufferBase {
$this->requestStack->pop();
}
return $response->getResult();
return array_map(function ($value) use ($response) {
return new CacheableValue($value, [$response]);
}, $response->getResult());
}
}
\ No newline at end of file
......@@ -2,9 +2,12 @@
namespace Drupal\graphql\GraphQL\Buffers;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
use Symfony\Component\HttpFoundation\Response;
class SubRequestResponse extends Response {
class SubRequestResponse extends Response implements RefinableCacheableDependencyInterface {
use RefinableCacheableDependencyTrait;
/**
* The request result.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment