Commit 9453570a authored by catch's avatar catch

Issue #2508445 by dawehner, martin107: Cache the query in RouteProvider::preloadRoutes()

parent c8526e22
......@@ -699,13 +699,13 @@ services:
arguments: ['@current_route_match']
router.route_provider:
class: Drupal\Core\Routing\RouteProvider
arguments: ['@database', '@state', '@path.current', '@cache.data', '@path_processor_manager']
arguments: ['@database', '@state', '@path.current', '@cache.data', '@path_processor_manager', '@cache_tags.invalidator']
tags:
- { name: event_subscriber }
- { name: backend_overridable }
router.route_preloader:
class: Drupal\Core\Routing\RoutePreloader
arguments: ['@router.route_provider', '@state']
arguments: ['@router.route_provider', '@state', '@cache.bootstrap']
tags:
- { name: 'event_subscriber' }
router.matcher.final_matcher:
......
......@@ -7,6 +7,8 @@
namespace Drupal\Core\Routing;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
......@@ -43,6 +45,13 @@ class RoutePreloader implements EventSubscriberInterface {
*/
protected $nonAdminRoutesOnRebuild = array();
/**
* The cache backend used to skip the state loading.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* Constructs a new RoutePreloader.
*
......@@ -50,10 +59,12 @@ class RoutePreloader implements EventSubscriberInterface {
* The route provider.
* @param \Drupal\Core\State\StateInterface $state
* The state key value store.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
*/
public function __construct(RouteProviderInterface $route_provider, StateInterface $state) {
public function __construct(RouteProviderInterface $route_provider, StateInterface $state, CacheBackendInterface $cache) {
$this->routeProvider = $route_provider;
$this->state = $state;
$this->cache = $cache;
}
/**
......@@ -65,7 +76,19 @@ public function __construct(RouteProviderInterface $route_provider, StateInterfa
public function onRequest(KernelEvent $event) {
// Only preload on normal HTML pages, as they will display menu links.
if ($this->routeProvider instanceof PreloadableRouteProviderInterface && $event->getRequest()->getRequestFormat() == 'html') {
if ($routes = $this->state->get('routing.non_admin_routes', [])) {
// Ensure that the state query is cached to skip the database query, if
// possible.
$key = 'routing.non_admin_routes';
if ($cache = $this->cache->get($key)) {
$routes = $cache->data;
}
else {
$routes = $this->state->get($key, []);
$this->cache->set($key, $routes, Cache::PERMANENT, ['routes']);
}
if ($routes) {
// Preload all the non-admin routes at once.
$this->routeProvider->preLoadRoutes($routes);
}
......
......@@ -7,7 +7,9 @@
namespace Drupal\Core\Routing;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\State\StateInterface;
......@@ -75,6 +77,13 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
*/
protected $cache;
/**
* The cache tag invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
*/
protected $cacheTagInvalidator;
/**
* A path processor manager for resolving the system path.
*
......@@ -82,6 +91,11 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
*/
protected $pathProcessor;
/**
* Cache ID prefix used to load routes.
*/
const ROUTE_LOAD_CID_PREFIX = 'route_provider.route_load:';
/**
* Constructs a new PathMatcher.
*
......@@ -95,16 +109,19 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
* The cache backend.
* @param \Drupal\Core\PathProcessor\InboundPathProcessorInterface $path_processor
* The path processor.
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tag_invalidator
* The cache tag invalidator.
* @param string $table
* The table in the database to use for matching.
* (Optional) The table in the database to use for matching. Defaults to 'router'
*/
public function __construct(Connection $connection, StateInterface $state, CurrentPathStack $current_path, CacheBackendInterface $cache_backend, InboundPathProcessorInterface $path_processor, $table = 'router') {
public function __construct(Connection $connection, StateInterface $state, CurrentPathStack $current_path, CacheBackendInterface $cache_backend, InboundPathProcessorInterface $path_processor, CacheTagsInvalidatorInterface $cache_tag_invalidator, $table = 'router') {
$this->connection = $connection;
$this->state = $state;
$this->tableName = $table;
$this->currentPath = $current_path;
$this->cache = $cache_backend;
$this->cacheTagInvalidator = $cache_tag_invalidator;
$this->pathProcessor = $path_processor;
$this->tableName = $table;
}
/**
......@@ -189,8 +206,18 @@ public function preLoadRoutes($names) {
$routes_to_load = array_diff($names, array_keys($this->routes), array_keys($this->serializedRoutes));
if ($routes_to_load) {
$result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->tableName) . '} WHERE name IN ( :names[] )', array(':names[]' => $routes_to_load));
$routes = $result->fetchAllKeyed();
$cid = static::ROUTE_LOAD_CID_PREFIX . hash('sha512', serialize($routes_to_load));
if ($cache = $this->cache->get($cid)) {
$routes = $cache->data;
}
else {
$result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->tableName) . '} WHERE name IN ( :names[] )', array(':names[]' => $routes_to_load));
$routes = $result->fetchAllKeyed();
$this->cache->set($cid, $routes, Cache::PERMANENT, ['routes']);
}
$this->serializedRoutes += $routes;
}
}
......@@ -339,6 +366,7 @@ public function getAllRoutes() {
public function reset() {
$this->routes = array();
$this->serializedRoutes = array();
$this->cacheTagInvalidator->invalidateTags(['routes']);
}
/**
......
......@@ -71,6 +71,13 @@ class RouteProviderTest extends KernelTestBase {
*/
protected $pathProcessor;
/**
* The cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
*/
protected $cacheTagsInvalidator;
protected function setUp() {
parent::setUp();
$this->fixtures = new RoutingFixtures();
......@@ -78,6 +85,7 @@ protected function setUp() {
$this->currentPath = new CurrentPathStack(new RequestStack());
$this->cache = new MemoryBackend('data');
$this->pathProcessor = \Drupal::service('path_processor_manager');
$this->cacheTagsInvalidator = \Drupal::service('cache_tags.invalidator');
$this->installSchema('system', 'url_alias');
}
......@@ -107,7 +115,7 @@ protected function tearDown() {
public function testCandidateOutlines() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$parts = array('node', '5', 'edit');
......@@ -130,7 +138,7 @@ public function testCandidateOutlines() {
*/
function testExactPathMatch() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -154,7 +162,7 @@ function testExactPathMatch() {
*/
function testOutlinePathMatch() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -183,7 +191,7 @@ function testOutlinePathMatch() {
*/
function testOutlinePathMatchTrailingSlash() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -212,7 +220,7 @@ function testOutlinePathMatchTrailingSlash() {
*/
function testOutlinePathMatchDefaults() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -250,7 +258,7 @@ function testOutlinePathMatchDefaults() {
*/
function testOutlinePathMatchDefaultsCollision() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -289,7 +297,7 @@ function testOutlinePathMatchDefaultsCollision() {
*/
function testOutlinePathMatchDefaultsCollision2() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -328,7 +336,7 @@ function testOutlinePathMatchDefaultsCollision2() {
*/
public function testOutlinePathMatchZero() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -363,7 +371,7 @@ public function testOutlinePathMatchZero() {
*/
function testOutlinePathNoMatch() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -388,7 +396,7 @@ function testOutlinePathNoMatch() {
*/
public function testRouteCaching() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -450,7 +458,7 @@ public function testRouteCaching() {
*/
public function testRouteByName() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
......@@ -485,7 +493,7 @@ public function testRouteByName() {
*/
public function testGetRoutesByPatternWithLongPatterns() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
// This pattern has only 3 parts, so we will get candidates, but no routes,
......@@ -543,7 +551,7 @@ public function testGetRoutesByPatternWithLongPatterns() {
*/
public function testGetRoutesPaged() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
$dumper = new MatcherDumper($connection, $this->state, 'test_routes');
......
......@@ -41,13 +41,21 @@ class RoutePreloaderTest extends UnitTestCase {
*/
protected $preloader;
/**
* The mocked cache.
*
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cache;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->routeProvider = $this->getMock('Drupal\Core\Routing\PreloadableRouteProviderInterface');
$this->state = $this->getMock('\Drupal\Core\State\StateInterface');
$this->preloader = new RoutePreloader($this->routeProvider, $this->state);
$this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->preloader = new RoutePreloader($this->routeProvider, $this->state, $this->cache);
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment