From 16964d02db93b094f51a680475dbbc5f17f31aa8 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Sat, 30 Mar 2024 17:55:55 +0000 Subject: [PATCH] Issue #2575105 by Berdir, catch, longwave, quietone, kristiaanvandeneynde, andypost, alexpott, mathilde_dumond, pradhumanjain2311, amateescu, Wim Leers, xjm: Use cache collector for state --- .../scaffold/files/default.settings.php | 10 ++ core/core.services.yml | 6 +- .../Compiler/DevelopmentSettingsPass.php | 6 +- .../Drupal/Core/Extension/ExtensionList.php | 15 +-- .../Drupal/Core/Routing/RoutePreloader.php | 29 +---- core/lib/Drupal/Core/State/State.php | 113 +++++++++++------- .../Core/Test/RefreshVariablesTrait.php | 2 +- .../tests/language_test/language_test.module | 2 +- .../LanguageNegotiationContentEntityTest.php | 12 +- .../LanguageNegotiationInfoTest.php | 2 +- .../src/Controller/ResourceController.php | 16 +-- core/modules/system/system.install | 11 ++ ...nTelemetryAuthenticatedPerformanceTest.php | 10 +- .../StandardPerformanceTest.php | 72 +++-------- .../Core/Routing/MatcherDumperTest.php | 6 +- .../Core/Routing/RouteProviderTest.php | 6 +- core/tests/Drupal/Tests/Core/CronTest.php | 6 +- .../Core/Extension/ThemeExtensionListTest.php | 7 +- .../Core/Render/RendererBubblingTest.php | 5 +- .../Tests/Core/Routing/RoutePreloaderTest.php | 18 +-- sites/default/default.settings.php | 10 ++ 21 files changed, 182 insertions(+), 182 deletions(-) diff --git a/core/assets/scaffold/files/default.settings.php b/core/assets/scaffold/files/default.settings.php index 9c876acbfff5..71da3e6f821c 100644 --- a/core/assets/scaffold/files/default.settings.php +++ b/core/assets/scaffold/files/default.settings.php @@ -807,6 +807,16 @@ */ $settings['entity_update_backup'] = TRUE; +/** + * State caching. + * + * State caching uses the cache collector pattern to cache all requested keys + * from the state API in a single cache entry, which can greatly reduce the + * amount of database queries. However, some sites may use state with a + * lot of dynamic keys which could result in a very large cache. + */ +$settings['state_cache'] = TRUE; + /** * Node migration type. * diff --git a/core/core.services.yml b/core/core.services.yml index 4e6b575dcc3c..1f9637165443 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -552,7 +552,9 @@ services: Drupal\Core\Site\Settings: '@settings' state: class: Drupal\Core\State\State - arguments: ['@keyvalue'] + arguments: ['@keyvalue', '@cache.bootstrap', '@lock'] + tags: + - { name: needs_destruction } Drupal\Core\State\StateInterface: '@state' queue: class: Drupal\Core\Queue\QueueFactory @@ -1052,7 +1054,7 @@ services: arguments: ['@router.route_provider', '@router.builder'] router.route_preloader: class: Drupal\Core\Routing\RoutePreloader - arguments: ['@router.route_provider', '@state', '@cache.bootstrap'] + arguments: ['@router.route_provider', '@state'] url_generator.non_bubbling: class: Drupal\Core\Routing\UrlGenerator arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '%filter_protocols%'] diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/DevelopmentSettingsPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/DevelopmentSettingsPass.php index 7e5778afb0d7..0d385e2c3628 100644 --- a/core/lib/Drupal/Core/DependencyInjection/Compiler/DevelopmentSettingsPass.php +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/DevelopmentSettingsPass.php @@ -15,8 +15,10 @@ class DevelopmentSettingsPass implements CompilerPassInterface { * {@inheritdoc} */ public function process(ContainerBuilder $container): void { - /** @var \Drupal\Core\State\StateInterface $state */ - $state = $container->get('state'); + // This does access the state key value store directly to avoid edge-cases + // with lazy ghost objects during early bootstrap. + /** @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state */ + $state = $container->get('keyvalue')->get('state'); $twig_debug = $state->get('twig_debug', FALSE); $twig_cache_disable = $state->get('twig_cache_disable', FALSE); if ($twig_debug || $twig_cache_disable) { diff --git a/core/lib/Drupal/Core/Extension/ExtensionList.php b/core/lib/Drupal/Core/Extension/ExtensionList.php index 26dce53016e5..b0af9c5e1727 100644 --- a/core/lib/Drupal/Core/Extension/ExtensionList.php +++ b/core/lib/Drupal/Core/Extension/ExtensionList.php @@ -177,7 +177,6 @@ public function reset() { // early installer. } - $this->cache->delete($this->getPathNamesCacheId()); // @todo In the long run it would be great to add the reset, but the early // installer fails due to that. https://www.drupal.org/node/2719315 could // help to resolve with that. @@ -416,18 +415,14 @@ protected function recalculateInfo() { public function getPathNames() { if ($this->pathNames === NULL) { $cache_id = $this->getPathNamesCacheId(); - if ($cache = $this->cache->get($cache_id)) { - $path_names = $cache->data; - } - // We use $file_names below. - elseif (!$path_names = $this->state->get($cache_id)) { - $path_names = $this->recalculatePathNames(); + $this->pathNames = $this->state->get($cache_id); + + if ($this->pathNames === NULL) { + $this->pathNames = $this->recalculatePathNames(); // Store filenames to allow static::getPathname() to retrieve them // without having to rebuild or scan the filesystem. - $this->state->set($cache_id, $path_names); - $this->cache->set($cache_id, $path_names); + $this->state->set($cache_id, $this->pathNames); } - $this->pathNames = $path_names; } return $this->pathNames; } diff --git a/core/lib/Drupal/Core/Routing/RoutePreloader.php b/core/lib/Drupal/Core/Routing/RoutePreloader.php index 673cebfd9f55..9748980965e4 100644 --- a/core/lib/Drupal/Core/Routing/RoutePreloader.php +++ b/core/lib/Drupal/Core/Routing/RoutePreloader.php @@ -2,8 +2,6 @@ namespace Drupal\Core\Routing; -use Drupal\Core\Cache\Cache; -use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\State\StateInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\KernelEvent; @@ -40,13 +38,6 @@ class RoutePreloader implements EventSubscriberInterface { */ protected $nonAdminRoutesOnRebuild = []; - /** - * The cache backend used to skip the state loading. - * - * @var \Drupal\Core\Cache\CacheBackendInterface - */ - protected $cache; - /** * Constructs a new RoutePreloader. * @@ -54,13 +45,13 @@ class RoutePreloader implements EventSubscriberInterface { * The route provider. * @param \Drupal\Core\State\StateInterface $state * The state key value store. - * @param \Drupal\Core\Cache\CacheBackendInterface $cache - * The cache backend. */ - public function __construct(RouteProviderInterface $route_provider, StateInterface $state, CacheBackendInterface $cache) { + public function __construct(RouteProviderInterface $route_provider, StateInterface $state) { $this->routeProvider = $route_provider; $this->state = $state; - $this->cache = $cache; + if (func_num_args() > 2) { + @trigger_error(sprintf('Passing a cache bin to %s is deprecated in drupal:10.3.0 and will be removed before drupal:11.0.0. Caching is now managed by the state service. See https://www.drupal.org/node/3177901', __METHOD__), E_USER_DEPRECATED); + } } /** @@ -73,17 +64,7 @@ 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') { - // 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']); - } - + $routes = $this->state->get('routing.non_admin_routes', []); if ($routes) { // Preload all the non-admin routes at once. $this->routeProvider->preLoadRoutes($routes); diff --git a/core/lib/Drupal/Core/State/State.php b/core/lib/Drupal/Core/State/State.php index 19c520d61fc5..e2d0c2c79966 100644 --- a/core/lib/Drupal/Core/State/State.php +++ b/core/lib/Drupal/Core/State/State.php @@ -3,12 +3,16 @@ namespace Drupal\Core\State; use Drupal\Core\Asset\AssetQueryString; +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Cache\CacheCollector; use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; +use Drupal\Core\Lock\LockBackendInterface; +use Drupal\Core\Site\Settings; /** * Provides the state system using a key value store. */ -class State implements StateInterface { +class State extends CacheCollector implements StateInterface { /** * Information about all deprecated state, keyed by legacy state key. @@ -33,21 +37,33 @@ class State implements StateInterface { */ protected $keyValueStore; - /** - * Static state cache. - * - * @var array - */ - protected $cache = []; - /** * Constructs a State object. * * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory * The key value store to use. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache + * The cache backend. + * @param \Drupal\Core\Lock\LockBackendInterface $lock + * The lock backend. */ - public function __construct(KeyValueFactoryInterface $key_value_factory) { + public function __construct(KeyValueFactoryInterface $key_value_factory, CacheBackendInterface $cache = NULL, LockBackendInterface $lock = NULL) { + if (!$cache) { + @trigger_error('Calling ' . __METHOD__ . '() without the $cache argument is deprecated in drupal:10.3.0 and is required in drupal:11.0.0. See https://www.drupal.org/node/3177901', E_USER_DEPRECATED); + $cache = \Drupal::cache('discovery'); + } + if (!$lock) { + @trigger_error('Calling ' . __METHOD__ . '() without the $lock argument is deprecated in drupal:10.3.0 and is required in drupal:11.0.0. See https://www.drupal.org/node/3177901', E_USER_DEPRECATED); + $lock = \Drupal::service('lock'); + } + parent::__construct('state', $cache, $lock); $this->keyValueStore = $key_value_factory->get('state'); + + // For backward compatibility, allow to opt-out of state caching, if cache + // is not explicitly enabled, flag the cache as already loaded. + if (Settings::get('state_cache') !== TRUE) { + $this->cacheLoaded = TRUE; + } } /** @@ -61,8 +77,17 @@ public function get($key, $default = NULL) { @trigger_error(self::$deprecatedState[$key]['message'], E_USER_DEPRECATED); $key = self::$deprecatedState[$key]['replacement']; } - $values = $this->getMultiple([$key]); - return $values[$key] ?? $default; + return parent::get($key) ?? $default; + } + + /** + * {@inheritdoc} + */ + protected function resolveCacheMiss($key) { + $value = $this->keyValueStore->get($key); + $this->storage[$key] = $value; + $this->persist($key); + return $value; } /** @@ -70,31 +95,8 @@ public function get($key, $default = NULL) { */ public function getMultiple(array $keys) { $values = []; - $load = []; foreach ($keys as $key) { - // Check if we have a value in the cache. - if (isset($this->cache[$key])) { - $values[$key] = $this->cache[$key]; - } - // Load the value if we don't have an explicit NULL value. - elseif (!array_key_exists($key, $this->cache)) { - $load[] = $key; - } - } - - if ($load) { - $loaded_values = $this->keyValueStore->getMultiple($load); - foreach ($load as $key) { - // If we find a value, even one that is NULL, add it to the cache and - // return it. - if (\array_key_exists($key, $loaded_values)) { - $values[$key] = $loaded_values[$key]; - $this->cache[$key] = $loaded_values[$key]; - } - else { - $this->cache[$key] = NULL; - } - } + $values[$key] = $this->get($key); } return $values; @@ -109,42 +111,69 @@ public function set($key, $value) { @trigger_error(self::$deprecatedState[$key]['message'], E_USER_DEPRECATED); $key = self::$deprecatedState[$key]['replacement']; } - $this->cache[$key] = $value; $this->keyValueStore->set($key, $value); + parent::set($key, $value); + $this->persist($key); } /** * {@inheritdoc} */ public function setMultiple(array $data) { + $this->keyValueStore->setMultiple($data); foreach ($data as $key => $value) { - $this->cache[$key] = $value; + parent::set($key, $value); + $this->persist($key); } - $this->keyValueStore->setMultiple($data); } /** * {@inheritdoc} */ public function delete($key) { - $this->deleteMultiple([$key]); + $this->keyValueStore->delete($key); + parent::delete($key); } /** * {@inheritdoc} */ public function deleteMultiple(array $keys) { + $this->keyValueStore->deleteMultiple($keys); foreach ($keys as $key) { - unset($this->cache[$key]); + parent::delete($key); } - $this->keyValueStore->deleteMultiple($keys); } /** * {@inheritdoc} */ public function resetCache() { - $this->cache = []; + $this->clear(); + } + + /** + * {@inheritdoc} + */ + protected function updateCache($lock = TRUE) { + // For backward compatibility, allow to opt-out of state caching, if cache + // is not explicitly enabled, there is no need to update it. + if (Settings::get('state_cache') !== TRUE) { + return; + } + parent::updateCache($lock); + } + + /** + * {@inheritdoc} + */ + protected function invalidateCache() { + // For backward compatibility, allow to opt-out of state caching, if cache + // is not explicitly enabled, there is no need to invalidate it. + if (Settings::get('state_cache') !== TRUE) { + return; + } + parent::invalidateCache(); } } diff --git a/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php b/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php index 0e00b111345a..d43b4fb9c86a 100644 --- a/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php +++ b/core/lib/Drupal/Core/Test/RefreshVariablesTrait.php @@ -37,7 +37,7 @@ protected function refreshVariables() { } \Drupal::service('config.factory')->reset(); - \Drupal::service('state')->resetCache(); + \Drupal::service('state')->reset(); } } diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module index f05c1bc30d73..dc5e63a4e619 100644 --- a/core/modules/language/tests/language_test/language_test.module +++ b/core/modules/language/tests/language_test/language_test.module @@ -72,7 +72,7 @@ function language_test_store_language_negotiation() { foreach (\Drupal::languageManager()->getDefinedLanguageTypes() as $type) { $last[$type] = \Drupal::languageManager()->getCurrentLanguage($type)->getId(); } - \Drupal::state()->set('language_test.language_negotiation_last', $last); + \Drupal::keyValue('language_test')->set('language_negotiation_last', $last); } /** diff --git a/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php b/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php index 13288b2e3c15..65aeb557c592 100644 --- a/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php +++ b/core/modules/language/tests/src/Functional/LanguageNegotiationContentEntityTest.php @@ -72,7 +72,7 @@ protected function setUp(): void { public function testDefaultConfiguration() { $translation = $this->entity; $this->drupalGet($translation->toUrl()); - $last = $this->container->get('state')->get('language_test.language_negotiation_last'); + $last = \Drupal::keyValue('language_test')->get('language_negotiation_last'); $last_content_language = $last[LanguageInterface::TYPE_CONTENT]; $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE]; $this->assertSame($last_content_language, $last_interface_language); @@ -80,7 +80,7 @@ public function testDefaultConfiguration() { $translation = $this->entity->getTranslation('es'); $this->drupalGet($translation->toUrl()); - $last = $this->container->get('state')->get('language_test.language_negotiation_last'); + $last = \Drupal::keyValue('language_test')->get('language_negotiation_last'); $last_content_language = $last[LanguageInterface::TYPE_CONTENT]; $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE]; $this->assertSame($last_content_language, $last_interface_language); @@ -88,7 +88,7 @@ public function testDefaultConfiguration() { $translation = $this->entity->getTranslation('fr'); $this->drupalGet($translation->toUrl()); - $last = $this->container->get('state')->get('language_test.language_negotiation_last'); + $last = \Drupal::keyValue('language_test')->get('language_negotiation_last'); $last_content_language = $last[LanguageInterface::TYPE_CONTENT]; $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE]; $this->assertSame($last_content_language, $last_interface_language); @@ -140,7 +140,7 @@ public function testEnabledLanguageContentNegotiator() { $translation = $this->entity; $this->drupalGet($translation->toUrl()); - $last = $this->container->get('state')->get('language_test.language_negotiation_last'); + $last = \Drupal::keyValue('language_test')->get('language_negotiation_last'); $last_content_language = $last[LanguageInterface::TYPE_CONTENT]; $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE]; // Check that interface language and content language are the same as the @@ -151,7 +151,7 @@ public function testEnabledLanguageContentNegotiator() { $translation = $this->entity->getTranslation('es'); $this->drupalGet($translation->toUrl()); - $last = $this->container->get('state')->get('language_test.language_negotiation_last'); + $last = \Drupal::keyValue('language_test')->get('language_negotiation_last'); $last_content_language = $last[LanguageInterface::TYPE_CONTENT]; $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE]; $this->assertSame($last_interface_language, $default_site_langcode, 'Interface language did not change from the default site language.'); @@ -159,7 +159,7 @@ public function testEnabledLanguageContentNegotiator() { $translation = $this->entity->getTranslation('fr'); $this->drupalGet($translation->toUrl()); - $last = $this->container->get('state')->get('language_test.language_negotiation_last'); + $last = \Drupal::keyValue('language_test')->get('language_negotiation_last'); $last_content_language = $last[LanguageInterface::TYPE_CONTENT]; $last_interface_language = $last[LanguageInterface::TYPE_INTERFACE]; $this->assertSame($last_interface_language, $default_site_langcode, 'Interface language did not change from the default site language.'); diff --git a/core/modules/language/tests/src/Functional/LanguageNegotiationInfoTest.php b/core/modules/language/tests/src/Functional/LanguageNegotiationInfoTest.php index 555add6adcf5..2cde3eafb259 100644 --- a/core/modules/language/tests/src/Functional/LanguageNegotiationInfoTest.php +++ b/core/modules/language/tests/src/Functional/LanguageNegotiationInfoTest.php @@ -136,7 +136,7 @@ public function testInfoAlterations() { // Check language negotiation results. $this->drupalGet(''); - $last = $this->container->get('state')->get('language_test.language_negotiation_last'); + $last = \Drupal::keyValue('language_test')->get('language_negotiation_last'); foreach ($this->languageManager()->getDefinedLanguageTypes() as $type) { $langcode = $last[$type]; $value = $type == LanguageInterface::TYPE_CONTENT || str_contains($type, 'test') ? 'it' : 'en'; diff --git a/core/modules/media/tests/modules/media_test_oembed/src/Controller/ResourceController.php b/core/modules/media/tests/modules/media_test_oembed/src/Controller/ResourceController.php index 6ea9a1e5354b..fd871b38fcab 100644 --- a/core/modules/media/tests/modules/media_test_oembed/src/Controller/ResourceController.php +++ b/core/modules/media/tests/modules/media_test_oembed/src/Controller/ResourceController.php @@ -23,15 +23,15 @@ class ResourceController { public function get(Request $request) { $asset_url = $request->query->get('url'); - $resources = \Drupal::state()->get(static::class, []); + $resource = \Drupal::keyValue('media_test_oembed')->get($asset_url); - if ($resources[$asset_url] === 404) { + if ($resource === 404) { $response = new Response('Not Found', 404); } else { - $content = file_get_contents($resources[$asset_url]); + $content = file_get_contents($resource); $response = new Response($content); - $response->headers->set('Content-Type', 'application/' . pathinfo($resources[$asset_url], PATHINFO_EXTENSION)); + $response->headers->set('Content-Type', 'application/' . pathinfo($resource, PATHINFO_EXTENSION)); } return $response; @@ -58,9 +58,7 @@ public function getThumbnailWithNoExtension() { * The path of the oEmbed resource representing the asset. */ public static function setResourceUrl($asset_url, $resource_path) { - $resources = \Drupal::state()->get(static::class, []); - $resources[$asset_url] = $resource_path; - \Drupal::state()->set(static::class, $resources); + \Drupal::keyValue('media_test_oembed')->set($asset_url, $resource_path); } /** @@ -70,9 +68,7 @@ public static function setResourceUrl($asset_url, $resource_path) { * The asset URL. */ public static function setResource404($asset_url) { - $resources = \Drupal::state()->get(static::class, []); - $resources[$asset_url] = 404; - \Drupal::state()->set(static::class, $resources); + \Drupal::keyValue('media_test_oembed')->set($asset_url, 404); } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index e4d6cd07ba83..1cea9ec49176 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1565,6 +1565,17 @@ function (callable $hook, string $module) use (&$module_list, $update_registry, } } + // Add warning if state caching is not explicitly set. + if ($phase === 'runtime') { + if (Settings::get('state_cache') === NULL) { + $requirements['state_cache_not_set'] = [ + 'title' => t('State cache flag not set'), + 'value' => t("State cache flag \$settings['state_cache'] is not set. It is recommended to be set to TRUE in settings.php unless there are too many state keys. Drupal 11 will default to having state cache enabled."), + 'severity' => REQUIREMENT_WARNING, + ]; + } + } + return $requirements; } diff --git a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php index 55252faff758..6f0d81ad0e6e 100644 --- a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php +++ b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php @@ -41,17 +41,11 @@ public function testFrontPageAuthenticatedWarmCache(): void { 'SELECT * FROM "users_field_data" "u" WHERE "u"."uid" = "10" AND "u"."default_langcode" = 1', 'SELECT "roles_target_id" FROM "user__roles" WHERE "entity_id" = "10"', 'SELECT "config"."name" AS "name" FROM "config" "config" WHERE ("collection" = "") AND ("name" LIKE "language.entity.%" ESCAPE ' . "'\\\\'" . ') ORDER BY "collection" ASC, "name" ASC', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.private_key" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "twig_extension_hash_prefix" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "asset.css_js_query_string" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', ]; $recorded_queries = $performance_data->getQueries(); $this->assertSame($expected_queries, $recorded_queries); - $this->assertSame(10, $performance_data->getQueryCount()); - $this->assertSame(44, $performance_data->getCacheGetCount()); + $this->assertSame(4, $performance_data->getQueryCount()); + $this->assertSame(45, $performance_data->getCacheGetCount()); $this->assertSame(0, $performance_data->getCacheSetCount()); $this->assertSame(0, $performance_data->getCacheDeleteCount()); $this->assertSame(0, $performance_data->getCacheTagChecksumCount()); diff --git a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php index c07f367006b8..25bc8f94141f 100644 --- a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php +++ b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php @@ -58,9 +58,6 @@ public function testAnonymous() { $expected_queries = [ 'SELECT "base_table"."id" AS "id", "base_table"."path" AS "path", "base_table"."alias" AS "alias", "base_table"."langcode" AS "langcode" FROM "path_alias" "base_table" WHERE ("base_table"."status" = 1) AND ("base_table"."alias" LIKE "/node" ESCAPE ' . "'\\\\'" . ') AND ("base_table"."langcode" IN ("en", "und")) ORDER BY "base_table"."langcode" ASC, "base_table"."id" DESC', 'SELECT "name", "route", "fit" FROM "router" WHERE "pattern_outline" IN ( "/node" ) AND "number_parts" >= 1', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.private_key" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "views.view_route_names" ) AND "collection" = "state"', 'SELECT COUNT(*) AS "expression" FROM (SELECT 1 AS "expression" FROM "node_field_data" "node_field_data" WHERE ("node_field_data"."promote" = 1) AND ("node_field_data"."status" = 1)) "subquery"', 'SELECT "node_field_data"."sticky" AS "node_field_data_sticky", "node_field_data"."created" AS "node_field_data_created", "node_field_data"."nid" AS "nid" FROM "node_field_data" "node_field_data" WHERE ("node_field_data"."promote" = 1) AND ("node_field_data"."status" = 1) ORDER BY "node_field_data_sticky" DESC, "node_field_data_created" DESC LIMIT 10 OFFSET 0', 'SELECT "revision"."vid" AS "vid", "revision"."langcode" AS "langcode", "revision"."revision_uid" AS "revision_uid", "revision"."revision_timestamp" AS "revision_timestamp", "revision"."revision_log" AS "revision_log", "revision"."revision_default" AS "revision_default", "base"."nid" AS "nid", "base"."type" AS "type", "base"."uuid" AS "uuid", CASE "base"."vid" WHEN "revision"."vid" THEN 1 ELSE 0 END AS "isDefaultRevision" FROM "node" "base" INNER JOIN "node_revision" "revision" ON "revision"."vid" = "base"."vid" WHERE "base"."nid" IN (1)', @@ -70,16 +67,12 @@ public function testAnonymous() { 'SELECT "t".* FROM "node__field_image" "t" WHERE ("entity_id" IN (1)) AND ("deleted" = 0) AND ("langcode" IN ("en", "und", "zxx")) ORDER BY "delta" ASC', 'SELECT "t".* FROM "node__field_tags" "t" WHERE ("entity_id" IN (1)) AND ("deleted" = 0) AND ("langcode" IN ("en", "und", "zxx")) ORDER BY "delta" ASC', 'SELECT "ces".* FROM "comment_entity_statistics" "ces" WHERE ("ces"."entity_id" IN (1)) AND ("ces"."entity_type" = "node")', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "twig_extension_hash_prefix" ) AND "collection" = "state"', 'SELECT "config"."name" AS "name" FROM "config" "config" WHERE ("collection" = "") AND ("name" LIKE "comment.type.%" ESCAPE ' . "'\\\\'" . ') ORDER BY "collection" ASC, "name" ASC', 'SELECT "config"."name" AS "name" FROM "config" "config" WHERE ("collection" = "") AND ("name" LIKE "node.type.%" ESCAPE ' . "'\\\\'" . ') ORDER BY "collection" ASC, "name" ASC', 'SELECT 1 AS "expression" FROM "path_alias" "base_table" WHERE ("base_table"."status" = 1) AND ("base_table"."path" LIKE "/node%" ESCAPE ' . "'\\\\'" . ') LIMIT 1 OFFSET 0', 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "theme:stark" ) AND "collection" = "config.entity.key_store.block"', 'SELECT "menu_tree"."menu_name" AS "menu_name", "menu_tree"."route_name" AS "route_name", "menu_tree"."route_parameters" AS "route_parameters", "menu_tree"."url" AS "url", "menu_tree"."title" AS "title", "menu_tree"."description" AS "description", "menu_tree"."parent" AS "parent", "menu_tree"."weight" AS "weight", "menu_tree"."options" AS "options", "menu_tree"."expanded" AS "expanded", "menu_tree"."enabled" AS "enabled", "menu_tree"."provider" AS "provider", "menu_tree"."metadata" AS "metadata", "menu_tree"."class" AS "class", "menu_tree"."form_class" AS "form_class", "menu_tree"."id" AS "id" FROM "menu_tree" "menu_tree" WHERE ("route_name" = "view.frontpage.page_1") AND ("route_param_key" = "view_id=frontpage&display_id=page_1") AND ("menu_name" = "main") ORDER BY "depth" ASC, "weight" ASC, "id" ASC', 'SELECT "menu_tree"."menu_name" AS "menu_name", "menu_tree"."route_name" AS "route_name", "menu_tree"."route_parameters" AS "route_parameters", "menu_tree"."url" AS "url", "menu_tree"."title" AS "title", "menu_tree"."description" AS "description", "menu_tree"."parent" AS "parent", "menu_tree"."weight" AS "weight", "menu_tree"."options" AS "options", "menu_tree"."expanded" AS "expanded", "menu_tree"."enabled" AS "enabled", "menu_tree"."provider" AS "provider", "menu_tree"."metadata" AS "metadata", "menu_tree"."class" AS "class", "menu_tree"."form_class" AS "form_class", "menu_tree"."id" AS "id" FROM "menu_tree" "menu_tree" WHERE ("route_name" = "view.frontpage.page_1") AND ("route_param_key" = "view_id=frontpage&display_id=page_1") AND ("menu_name" = "account") ORDER BY "depth" ASC, "weight" ASC, "id" ASC', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "asset.css_js_query_string" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', 'INSERT INTO "semaphore" ("name", "value", "expire") VALUES ("theme_registry:runtime:stark:Drupal\Core\Utility\ThemeRegistry", "LOCK_ID", "EXPIRE")', 'DELETE FROM "semaphore" WHERE ("name" = "theme_registry:runtime:stark:Drupal\Core\Utility\ThemeRegistry") AND ("value" = "LOCK_ID")', 'INSERT INTO "semaphore" ("name", "value", "expire") VALUES ("library_info:stark:Drupal\Core\Cache\CacheCollector", "LOCK_ID", "EXPIRE")', @@ -87,20 +80,16 @@ public function testAnonymous() { 'INSERT INTO "semaphore" ("name", "value", "expire") VALUES ("path_alias_whitelist:Drupal\Core\Cache\CacheCollector", "LOCK_ID", "EXPIRE")', 'DELETE FROM "semaphore" WHERE ("name" = "path_alias_whitelist:Drupal\Core\Cache\CacheCollector") AND ("value" = "LOCK_ID")', 'SELECT "base_table"."id" AS "id", "base_table"."path" AS "path", "base_table"."alias" AS "alias", "base_table"."langcode" AS "langcode" FROM "path_alias" "base_table" WHERE ("base_table"."status" = 1) AND ("base_table"."alias" LIKE "CSS_FILE" ESCAPE ' . "'\\\\'" . ') AND ("base_table"."langcode" IN ("en", "und")) ORDER BY "base_table"."langcode" ASC, "base_table"."id" DESC', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "routing.menu_masks.router" ) AND "collection" = "state"', 'SELECT "name", "route", "fit" FROM "router" WHERE "pattern_outline" IN ( "/sites/simpletest/TEST_ID/files/css/CSS_FILE", "/sites/simpletest/TEST_ID/files/css/%", "/sites/simpletest/TEST_ID/files/%/CSS_FILE", "/sites/simpletest/%/files/%/CSS_FILE", "/sites/simpletest/%/%/%/CSS_FILE", "/sites/%/TEST_ID/%/css/%", "/sites/simpletest/TEST_ID/files/css", "/sites/simpletest/TEST_ID/files/%", "/sites/simpletest/TEST_ID/%/css", "/sites/simpletest/TEST_ID/%/%", "/sites/simpletest/TEST_ID/files/css/%"0, "/sites/simpletest/TEST_ID/files/css/%"1, "/sites/simpletest/TEST_ID/files/css/%"2, "/sites/simpletest/TEST_ID/files/css/%"3, "/sites/simpletest/TEST_ID/files/css/%"4, "/sites/simpletest/TEST_ID/files/css/%"5, "/sites/simpletest/TEST_ID/files/css/%"6, "/sites/simpletest/TEST_ID/files/css/%"7, "/sites/simpletest/TEST_ID/files/css/%"8, "/sites/simpletest/TEST_ID/files/css/%"9, "/sites/simpletest/TEST_ID/files/%/CSS_FILE"0, "/sites/simpletest/TEST_ID/files/%/CSS_FILE"1, "/sites/simpletest/TEST_ID/files/%/CSS_FILE"2, "/sites/simpletest/TEST_ID/files/%/CSS_FILE"3 ) AND "number_parts" >= 6', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', ]; $recorded_queries = $performance_data->getQueries(); $this->assertSame($expected_queries, $recorded_queries); - $this->assertSame(36, $performance_data->getQueryCount()); + $this->assertSame(25, $performance_data->getQueryCount()); $this->assertSame(136, $performance_data->getCacheGetCount()); $this->assertSame(47, $performance_data->getCacheSetCount()); $this->assertSame(0, $performance_data->getCacheDeleteCount()); - $this->assertCountBetween(39, 42, $performance_data->getCacheTagChecksumCount()); - $this->assertCountBetween(45, 48, $performance_data->getCacheTagIsValidCount()); + $this->assertCountBetween(38, 41, $performance_data->getCacheTagChecksumCount()); + $this->assertCountBetween(43, 46, $performance_data->getCacheTagIsValidCount()); $this->assertSame(0, $performance_data->getCacheTagInvalidationCount()); // Test node page. @@ -112,27 +101,21 @@ public function testAnonymous() { $expected_queries = [ 'SELECT "base_table"."id" AS "id", "base_table"."path" AS "path", "base_table"."alias" AS "alias", "base_table"."langcode" AS "langcode" FROM "path_alias" "base_table" WHERE ("base_table"."status" = 1) AND ("base_table"."alias" LIKE "/node/1" ESCAPE ' . "'\\\\'" . ') AND ("base_table"."langcode" IN ("en", "und")) ORDER BY "base_table"."langcode" ASC, "base_table"."id" DESC', 'SELECT "name", "route", "fit" FROM "router" WHERE "pattern_outline" IN ( "/node/1", "/node/%", "/node" ) AND "number_parts" >= 2', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.private_key" ) AND "collection" = "state"', 'SELECT "name", "data" FROM "config" WHERE "collection" = "" AND "name" IN ( "core.entity_view_display.node.article.full" )', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "twig_extension_hash_prefix" ) AND "collection" = "state"', 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "theme:stark" ) AND "collection" = "config.entity.key_store.block"', 'SELECT "menu_tree"."menu_name" AS "menu_name", "menu_tree"."route_name" AS "route_name", "menu_tree"."route_parameters" AS "route_parameters", "menu_tree"."url" AS "url", "menu_tree"."title" AS "title", "menu_tree"."description" AS "description", "menu_tree"."parent" AS "parent", "menu_tree"."weight" AS "weight", "menu_tree"."options" AS "options", "menu_tree"."expanded" AS "expanded", "menu_tree"."enabled" AS "enabled", "menu_tree"."provider" AS "provider", "menu_tree"."metadata" AS "metadata", "menu_tree"."class" AS "class", "menu_tree"."form_class" AS "form_class", "menu_tree"."id" AS "id" FROM "menu_tree" "menu_tree" WHERE ("route_name" = "entity.node.canonical") AND ("route_param_key" = "node=1") AND ("menu_name" = "main") ORDER BY "depth" ASC, "weight" ASC, "id" ASC', 'SELECT "menu_tree"."menu_name" AS "menu_name", "menu_tree"."route_name" AS "route_name", "menu_tree"."route_parameters" AS "route_parameters", "menu_tree"."url" AS "url", "menu_tree"."title" AS "title", "menu_tree"."description" AS "description", "menu_tree"."parent" AS "parent", "menu_tree"."weight" AS "weight", "menu_tree"."options" AS "options", "menu_tree"."expanded" AS "expanded", "menu_tree"."enabled" AS "enabled", "menu_tree"."provider" AS "provider", "menu_tree"."metadata" AS "metadata", "menu_tree"."class" AS "class", "menu_tree"."form_class" AS "form_class", "menu_tree"."id" AS "id" FROM "menu_tree" "menu_tree" WHERE ("route_name" = "entity.node.canonical") AND ("route_param_key" = "node=1") AND ("menu_name" = "account") ORDER BY "depth" ASC, "weight" ASC, "id" ASC', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "asset.css_js_query_string" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', 'INSERT INTO "semaphore" ("name", "value", "expire") VALUES ("theme_registry:runtime:stark:Drupal\Core\Utility\ThemeRegistry", "LOCK_ID", "EXPIRE")', 'DELETE FROM "semaphore" WHERE ("name" = "theme_registry:runtime:stark:Drupal\Core\Utility\ThemeRegistry") AND ("value" = "LOCK_ID")', ]; $recorded_queries = $performance_data->getQueries(); $this->assertSame($expected_queries, $recorded_queries); - $this->assertSame(14, $performance_data->getQueryCount()); + $this->assertSame(8, $performance_data->getQueryCount()); $this->assertSame(94, $performance_data->getCacheGetCount()); $this->assertSame(16, $performance_data->getCacheSetCount()); $this->assertSame(0, $performance_data->getCacheDeleteCount()); $this->assertCountBetween(23, 24, $performance_data->getCacheTagChecksumCount()); - $this->assertCountBetween(40, 41, $performance_data->getCacheTagIsValidCount()); + $this->assertCountBetween(39, 40, $performance_data->getCacheTagIsValidCount()); $this->assertSame(0, $performance_data->getCacheTagInvalidationCount()); // Test user profile page. @@ -150,26 +133,20 @@ public function testAnonymous() { 'SELECT "data".* FROM "users_field_data" "data" WHERE "data"."uid" IN (2) ORDER BY "data"."uid" ASC', 'SELECT "t".* FROM "user__roles" "t" WHERE ("entity_id" IN (2)) AND ("deleted" = 0) AND ("langcode" IN ("en", "und", "zxx")) ORDER BY "delta" ASC', 'SELECT "t".* FROM "user__user_picture" "t" WHERE ("entity_id" IN (2)) AND ("deleted" = 0) AND ("langcode" IN ("en", "und", "zxx")) ORDER BY "delta" ASC', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.private_key" ) AND "collection" = "state"', 'SELECT "name", "data" FROM "config" WHERE "collection" = "" AND "name" IN ( "core.entity_view_display.user.user.full" )', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "twig_extension_hash_prefix" ) AND "collection" = "state"', 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "theme:stark" ) AND "collection" = "config.entity.key_store.block"', 'SELECT "menu_tree"."menu_name" AS "menu_name", "menu_tree"."route_name" AS "route_name", "menu_tree"."route_parameters" AS "route_parameters", "menu_tree"."url" AS "url", "menu_tree"."title" AS "title", "menu_tree"."description" AS "description", "menu_tree"."parent" AS "parent", "menu_tree"."weight" AS "weight", "menu_tree"."options" AS "options", "menu_tree"."expanded" AS "expanded", "menu_tree"."enabled" AS "enabled", "menu_tree"."provider" AS "provider", "menu_tree"."metadata" AS "metadata", "menu_tree"."class" AS "class", "menu_tree"."form_class" AS "form_class", "menu_tree"."id" AS "id" FROM "menu_tree" "menu_tree" WHERE ("route_name" = "entity.user.canonical") AND ("route_param_key" = "user=2") AND ("menu_name" = "main") ORDER BY "depth" ASC, "weight" ASC, "id" ASC', 'SELECT "menu_tree"."menu_name" AS "menu_name", "menu_tree"."route_name" AS "route_name", "menu_tree"."route_parameters" AS "route_parameters", "menu_tree"."url" AS "url", "menu_tree"."title" AS "title", "menu_tree"."description" AS "description", "menu_tree"."parent" AS "parent", "menu_tree"."weight" AS "weight", "menu_tree"."options" AS "options", "menu_tree"."expanded" AS "expanded", "menu_tree"."enabled" AS "enabled", "menu_tree"."provider" AS "provider", "menu_tree"."metadata" AS "metadata", "menu_tree"."class" AS "class", "menu_tree"."form_class" AS "form_class", "menu_tree"."id" AS "id" FROM "menu_tree" "menu_tree" WHERE ("route_name" = "entity.user.canonical") AND ("route_param_key" = "user=2") AND ("menu_name" = "account") ORDER BY "depth" ASC, "weight" ASC, "id" ASC', 'SELECT "ud".* FROM "users_data" "ud" WHERE ("module" = "contact") AND ("uid" = "2") AND ("name" = "enabled")', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "asset.css_js_query_string" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', ]; $recorded_queries = $performance_data->getQueries(); $this->assertSame($expected_queries, $recorded_queries); - $this->assertSame(18, $performance_data->getQueryCount()); + $this->assertSame(12, $performance_data->getQueryCount()); $this->assertSame(80, $performance_data->getCacheGetCount()); $this->assertSame(16, $performance_data->getCacheSetCount()); $this->assertSame(0, $performance_data->getCacheDeleteCount()); - $this->assertCountBetween(23, 24, $performance_data->getCacheTagChecksumCount()); - $this->assertCountBetween(34, 35, $performance_data->getCacheTagIsValidCount()); + $this->assertCountBetween(22, 23, $performance_data->getCacheTagChecksumCount()); + $this->assertCountBetween(33, 34, $performance_data->getCacheTagIsValidCount()); $this->assertSame(0, $performance_data->getCacheTagInvalidationCount()); } @@ -196,17 +173,14 @@ public function testLogin(): void { }, 'standardLogin'); $expected_queries = [ - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', 'SELECT COUNT(*) AS "expression" FROM (SELECT 1 AS "expression" FROM "flood" "f" WHERE ("event" = "user.failed_login_ip") AND ("identifier" = "CLIENT_IP") AND ("timestamp" > "TIMESTAMP")) "subquery"', 'SELECT "base_table"."uid" AS "uid", "base_table"."uid" AS "base_table_uid" FROM "users" "base_table" INNER JOIN "users_field_data" "users_field_data" ON "users_field_data"."uid" = "base_table"."uid" WHERE ("users_field_data"."name" IN ("ACCOUNT_NAME")) AND ("users_field_data"."default_langcode" IN (1))', 'SELECT COUNT(*) AS "expression" FROM (SELECT 1 AS "expression" FROM "flood" "f" WHERE ("event" = "user.failed_login_user") AND ("identifier" = "CLIENT_IP") AND ("timestamp" > "TIMESTAMP")) "subquery"', 'INSERT INTO "watchdog" ("uid", "type", "message", "variables", "severity", "link", "location", "referer", "hostname", "timestamp") VALUES ("2", "user", "Session opened for %name.", "WATCHDOG_DATA", 6, "", "LOCATION", "REFERER", "CLIENT_IP", "TIMESTAMP")', 'UPDATE "users_field_data" SET "login"="TIMESTAMP" WHERE "uid" = "2"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', 'SELECT "session" FROM "sessions" WHERE "sid" = "SESSION_ID" LIMIT 0, 1', 'SELECT 1 AS "expression" FROM "sessions" "sessions" WHERE "sid" = "SESSION_ID"', 'INSERT INTO "sessions" ("sid", "uid", "hostname", "session", "timestamp") VALUES ("SESSION_ID", "2", "CLIENT_IP", "SESSION_DATA", "TIMESTAMP")', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', 'SELECT "session" FROM "sessions" WHERE "sid" = "SESSION_ID" LIMIT 0, 1', 'SELECT * FROM "users_field_data" "u" WHERE "u"."uid" = "2" AND "u"."default_langcode" = 1', 'SELECT "roles_target_id" FROM "user__roles" WHERE "entity_id" = "2"', @@ -214,21 +188,15 @@ public function testLogin(): void { 'SELECT "data".* FROM "users_field_data" "data" WHERE "data"."uid" IN (2) ORDER BY "data"."uid" ASC', 'SELECT "t".* FROM "user__roles" "t" WHERE ("entity_id" IN (2)) AND ("deleted" = 0) AND ("langcode" IN ("en", "und", "zxx")) ORDER BY "delta" ASC', 'SELECT "t".* FROM "user__user_picture" "t" WHERE ("entity_id" IN (2)) AND ("deleted" = 0) AND ("langcode" IN ("en", "und", "zxx")) ORDER BY "delta" ASC', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.private_key" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "twig_extension_hash_prefix" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "asset.css_js_query_string" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', ]; $recorded_queries = $performance_data->getQueries(); $this->assertSame($expected_queries, $recorded_queries); - $this->assertSame(24, $performance_data->getQueryCount()); - $this->assertSame(63, $performance_data->getCacheGetCount()); + $this->assertSame(15, $performance_data->getQueryCount()); + $this->assertSame(64, $performance_data->getCacheGetCount()); $this->assertSame(1, $performance_data->getCacheSetCount()); $this->assertSame(1, $performance_data->getCacheDeleteCount()); $this->assertSame(1, $performance_data->getCacheTagChecksumCount()); - $this->assertSame(29, $performance_data->getCacheTagIsValidCount()); + $this->assertSame(28, $performance_data->getCacheTagIsValidCount()); $this->assertSame(0, $performance_data->getCacheTagInvalidationCount()); } @@ -258,10 +226,6 @@ public function testLoginBlock(): void { }, 'standardBlockLogin'); $expected_queries = [ - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.private_key" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "views.view_route_names" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "twig_extension_hash_prefix" ) AND "collection" = "state"', 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "theme:stark" ) AND "collection" = "config.entity.key_store.block"', 'SELECT "config"."name" AS "name" FROM "config" "config" WHERE ("collection" = "") AND ("name" LIKE "search.page.%" ESCAPE ' . "'\\\\'" . ') ORDER BY "collection" ASC, "name" ASC', 'SELECT COUNT(*) AS "expression" FROM (SELECT 1 AS "expression" FROM "flood" "f" WHERE ("event" = "user.failed_login_ip") AND ("identifier" = "CLIENT_IP") AND ("timestamp" > "TIMESTAMP")) "subquery"', @@ -273,29 +237,21 @@ public function testLoginBlock(): void { 'SELECT COUNT(*) AS "expression" FROM (SELECT 1 AS "expression" FROM "flood" "f" WHERE ("event" = "user.failed_login_user") AND ("identifier" = "CLIENT_IP") AND ("timestamp" > "TIMESTAMP")) "subquery"', 'INSERT INTO "watchdog" ("uid", "type", "message", "variables", "severity", "link", "location", "referer", "hostname", "timestamp") VALUES ("2", "user", "Session opened for %name.", "WATCHDOG_DATA", 6, "", "LOCATION", "REFERER", "CLIENT_IP", "TIMESTAMP")', 'UPDATE "users_field_data" SET "login"="TIMESTAMP" WHERE "uid" = "2"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', 'SELECT "session" FROM "sessions" WHERE "sid" = "SESSION_ID" LIMIT 0, 1', 'SELECT 1 AS "expression" FROM "sessions" "sessions" WHERE "sid" = "SESSION_ID"', 'INSERT INTO "sessions" ("sid", "uid", "hostname", "session", "timestamp") VALUES ("SESSION_ID", "2", "CLIENT_IP", "SESSION_DATA", "TIMESTAMP")', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', 'SELECT "session" FROM "sessions" WHERE "sid" = "SESSION_ID" LIMIT 0, 1', 'SELECT * FROM "users_field_data" "u" WHERE "u"."uid" = "2" AND "u"."default_langcode" = 1', 'SELECT "roles_target_id" FROM "user__roles" WHERE "entity_id" = "2"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.maintenance_mode" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.private_key" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "twig_extension_hash_prefix" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "asset.css_js_query_string" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "drupal.test_wait_terminate" ) AND "collection" = "state"', - 'SELECT "name", "value" FROM "key_value" WHERE "name" IN ( "system.cron_last" ) AND "collection" = "state"', ]; $recorded_queries = $performance_data->getQueries(); $this->assertSame($expected_queries, $recorded_queries); - $this->assertSame(29, $performance_data->getQueryCount()); - $this->assertSame(106, $performance_data->getCacheGetCount()); + $this->assertSame(17, $performance_data->getQueryCount()); + $this->assertSame(107, $performance_data->getCacheGetCount()); $this->assertSame(1, $performance_data->getCacheSetCount()); $this->assertSame(1, $performance_data->getCacheDeleteCount()); $this->assertSame(1, $performance_data->getCacheTagChecksumCount()); - $this->assertSame(44, $performance_data->getCacheTagIsValidCount()); + $this->assertSame(43, $performance_data->getCacheTagIsValidCount()); $this->assertSame(0, $performance_data->getCacheTagInvalidationCount()); } diff --git a/core/tests/Drupal/KernelTests/Core/Routing/MatcherDumperTest.php b/core/tests/Drupal/KernelTests/Core/Routing/MatcherDumperTest.php index c156512c4c4e..ce400cbd216e 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/MatcherDumperTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/MatcherDumperTest.php @@ -3,10 +3,13 @@ namespace Drupal\KernelTests\Core\Routing; use ColinODell\PsrTestLogger\TestLogger; +use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Database\Database; +use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; use Drupal\Core\Routing\MatcherDumper; use Drupal\Core\Routing\RouteCompiler; +use Drupal\Core\Lock\NullLockBackend; use Drupal\Core\State\State; use Drupal\KernelTests\KernelTestBase; use Drupal\Tests\Core\Routing\RoutingFixtures; @@ -46,7 +49,8 @@ protected function setUp(): void { parent::setUp(); $this->fixtures = new RoutingFixtures(); - $this->state = new State(new KeyValueMemoryFactory()); + $time = $this->prophesize(TimeInterface::class)->reveal(); + $this->state = new State(new KeyValueMemoryFactory(), new MemoryBackend($time), new NullLockBackend()); $this->logger = new TestLogger(); } diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php index 30ea7be869b5..56824df8426f 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php @@ -7,6 +7,7 @@ use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\Database\Database; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; +use Drupal\Core\Lock\NullLockBackend; use Drupal\Core\Path\CurrentPathStack; use Drupal\Core\Routing\MatcherDumper; use Drupal\Core\Routing\RouteProvider; @@ -96,9 +97,10 @@ class RouteProviderTest extends KernelTestBase { protected function setUp(): void { parent::setUp(); $this->fixtures = new RoutingFixtures(); - $this->state = new State(new KeyValueMemoryFactory()); + $time = \Drupal::service(TimeInterface::class); + $this->state = new State(new KeyValueMemoryFactory(), new MemoryBackend($time), new NullLockBackend()); $this->currentPath = new CurrentPathStack(new RequestStack()); - $this->cache = new MemoryBackend(\Drupal::service(TimeInterface::class)); + $this->cache = new MemoryBackend($time); $this->pathProcessor = \Drupal::service('path_processor_manager'); $this->cacheTagsInvalidator = \Drupal::service('cache_tags.invalidator'); $this->installEntitySchema('path_alias'); diff --git a/core/tests/Drupal/Tests/Core/CronTest.php b/core/tests/Drupal/Tests/Core/CronTest.php index 226c3e8cdac2..d10be2b26f08 100644 --- a/core/tests/Drupal/Tests/Core/CronTest.php +++ b/core/tests/Drupal/Tests/Core/CronTest.php @@ -4,10 +4,13 @@ namespace Drupal\Tests\Core; +use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ImmutableConfig; +use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\Cron; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; +use Drupal\Core\Lock\NullLockBackend; use Drupal\Core\Queue\DelayedRequeueException; use Drupal\Core\Queue\Memory; use Drupal\Core\Queue\RequeueException; @@ -64,7 +67,8 @@ protected function setUp(): void { parent::setUp(); // Construct a state object used for testing logger assertions. - $this->state = new State(new KeyValueMemoryFactory()); + $time = $this->prophesize(TimeInterface::class)->reveal(); + $this->state = new State(new KeyValueMemoryFactory(), new MemoryBackend($time), new NullLockBackend()); // Create a mock logger to set a flag in the resulting state. $logger = $this->prophesize('Drupal\Core\Logger\LoggerChannelInterface'); diff --git a/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php b/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php index 118f884713ab..0dc1de5b8b65 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeExtensionListTest.php @@ -13,6 +13,7 @@ use Drupal\Core\Extension\ThemeEngineExtensionList; use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; +use Drupal\Core\Lock\NullLockBackend; use Drupal\Core\State\State; use Drupal\Tests\UnitTestCase; use Prophecy\Argument; @@ -65,7 +66,7 @@ public function testRebuildThemeDataWithThemeParents() { ->alter('system_info', Argument::type('array'), Argument::type(Extension::class), Argument::any()) ->shouldBeCalled(); - $state = new State(new KeyValueMemoryFactory()); + $state = new State(new KeyValueMemoryFactory(), new NullBackend('bin'), new NullLockBackend()); $config_factory = $this->getConfigFactoryStub([ 'core.extension' => [ @@ -123,7 +124,7 @@ public function testRebuildThemeDataWithThemeParents() { public function testGetBaseThemes(array $themes, $theme, array $expected) { // Mocks and stubs. $module_handler = $this->prophesize(ModuleHandlerInterface::class); - $state = new State(new KeyValueMemoryFactory()); + $state = new State(new KeyValueMemoryFactory(), new NullBackend('bin'), new NullLockBackend()); $config_factory = $this->getConfigFactoryStub([]); $theme_engine_list = $this->prophesize(ThemeEngineExtensionList::class); $theme_listing = new ThemeExtensionList($this->root, 'theme', new NullBackend('test'), new InfoParser($this->root), $module_handler->reveal(), $state, $config_factory, $theme_engine_list->reveal(), 'test'); @@ -149,7 +150,7 @@ public function testGetBaseThemes(array $themes, $theme, array $expected) { public function testDoGetBaseThemes(array $themes, $theme, array $expected): void { // Mocks and stubs. $module_handler = $this->prophesize(ModuleHandlerInterface::class); - $state = new State(new KeyValueMemoryFactory()); + $state = new State(new KeyValueMemoryFactory(), new NullBackend('bin'), new NullLockBackend()); $config_factory = $this->getConfigFactoryStub([]); $theme_engine_list = $this->prophesize(ThemeEngineExtensionList::class); $theme_listing = new ThemeExtensionList($this->root, 'theme', new NullBackend('test'), new InfoParser($this->root), $module_handler->reveal(), $state, $config_factory, $theme_engine_list->reveal(), 'test'); diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php index fc307e762fbe..cc4088d3da23 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php @@ -5,11 +5,13 @@ namespace Drupal\Tests\Core\Render; use Drupal\Component\Datetime\Time; +use Drupal\Component\Datetime\TimeInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\Cache\VariationCache; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; use Drupal\Core\Security\TrustedCallbackInterface; +use Drupal\Core\Lock\NullLockBackend; use Drupal\Core\State\State; use Drupal\Core\Cache\Cache; @@ -448,7 +450,8 @@ public function testBubblingWithPrerender($test_element) { $this->setUpMemoryCache(); // Mock the State service. - $memory_state = new State(new KeyValueMemoryFactory()); + $time = $this->prophesize(TimeInterface::class)->reveal(); + $memory_state = new State(new KeyValueMemoryFactory(), new MemoryBackend($time), new NullLockBackend()); \Drupal::getContainer()->set('state', $memory_state); // Simulate the theme system/Twig: a recursive call to Renderer::render(), diff --git a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php index 772a95679d02..796844a6cbee 100644 --- a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php @@ -38,13 +38,6 @@ class RoutePreloaderTest extends UnitTestCase { */ protected $preloader; - /** - * The mocked cache. - * - * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected $cache; - /** * {@inheritdoc} */ @@ -53,8 +46,7 @@ protected function setUp(): void { $this->routeProvider = $this->createMock('Drupal\Core\Routing\PreloadableRouteProviderInterface'); $this->state = $this->createMock('\Drupal\Core\State\StateInterface'); - $this->cache = $this->createMock('Drupal\Core\Cache\CacheBackendInterface'); - $this->preloader = new RoutePreloader($this->routeProvider, $this->state, $this->cache); + $this->preloader = new RoutePreloader($this->routeProvider, $this->state); } /** @@ -184,4 +176,12 @@ public function testOnRequestOnHtml() { $this->preloader->onRequest($event); } + /** + * @group legacy + */ + public function testConstructorDeprecation() { + $this->expectDeprecation('Passing a cache bin to Drupal\Core\Routing\RoutePreloader::__construct is deprecated in drupal:10.3.0 and will be removed before drupal:11.0.0. Caching is now managed by the state service. See https://www.drupal.org/node/3177901'); + new RoutePreloader($this->routeProvider, $this->state, $this->createMock('Drupal\Core\Cache\CacheBackendInterface')); + } + } diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 9c876acbfff5..71da3e6f821c 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -807,6 +807,16 @@ */ $settings['entity_update_backup'] = TRUE; +/** + * State caching. + * + * State caching uses the cache collector pattern to cache all requested keys + * from the state API in a single cache entry, which can greatly reduce the + * amount of database queries. However, some sites may use state with a + * lot of dynamic keys which could result in a very large cache. + */ +$settings['state_cache'] = TRUE; + /** * Node migration type. * -- GitLab