From d1cd7481a68a09534fdb1b87597163e3f972156f Mon Sep 17 00:00:00 2001 From: Kristiaan Van den Eynde <kristiaan@factorial.io> Date: Thu, 13 Mar 2025 14:00:30 +0100 Subject: [PATCH 1/4] Delay cache get for placeholdered elements --- core/lib/Drupal/Core/Render/Renderer.php | 9 ++- .../FunctionalJavascript/PerformanceTest.php | 8 +-- ...nTelemetryAuthenticatedPerformanceTest.php | 5 +- .../OpenTelemetryNodePagePerformanceTest.php | 70 ++++++++++--------- .../StandardPerformanceTest.php | 45 ++++++------ 5 files changed, 77 insertions(+), 60 deletions(-) diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index 1383c4261b43..c295fa5cb1b9 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -276,7 +276,14 @@ protected function doRender(&$elements, $is_root_call = FALSE) { // Try to fetch the prerendered element from cache, replace any placeholders // and return the final markup. - if (isset($elements['#cache']['keys'])) { + // + // If the element was set to create a placeholder, we do not try to load it + // from the cache, as \Drupal\Core\Render\Placeholder\CachedStrategy has an + // optimization to load them all in one go, rather than individually here. + // If the current request method is not cacheable, no placeholders are + // generated, in that case, it's still better to fetch render cached + // elements individually instead of fully building them again. + if (isset($elements['#cache']['keys']) && (empty($elements['#create_placeholder']) || !$this->requestStack->getCurrentRequest()->isMethodCacheable())) { $cached_element = $this->renderCache->get($elements); if ($cached_element !== FALSE) { $elements = $cached_element; diff --git a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php index d50e419e90ae..aeb053719090 100644 --- a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php +++ b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php @@ -73,14 +73,14 @@ public function testLogin(): void { $expected = [ 'QueryCount' => 4, - 'CacheGetCount' => 60, + 'CacheGetCount' => 50, 'CacheGetCountByBin' => [ 'config' => 11, - 'data' => 5, + 'data' => 4, 'discovery' => 10, 'bootstrap' => 6, 'dynamic_page_cache' => 2, - 'render' => 25, + 'render' => 16, 'menu' => 1, ], 'CacheSetCount' => 2, @@ -89,7 +89,7 @@ public function testLogin(): void { ], 'CacheDeleteCount' => 0, 'CacheTagInvalidationCount' => 0, - 'CacheTagLookupQueryCount' => 20, + 'CacheTagLookupQueryCount' => 14, 'ScriptCount' => 3, 'ScriptBytes' => 215500, 'StylesheetCount' => 2, diff --git a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php index 5f7287335adb..8ae0be5e3a8e 100644 --- a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php +++ b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php @@ -55,14 +55,15 @@ public function testFrontPageAuthenticatedWarmCache(): void { 'config' => 22, 'bootstrap' => 5, 'discovery' => 5, - 'data' => 6, + 'data' => 5, 'dynamic_page_cache' => 2, + 'menu' => 1, 'render' => 2, ], 'CacheSetCount' => 0, 'CacheDeleteCount' => 0, 'CacheTagInvalidationCount' => 0, - 'CacheTagLookupQueryCount' => 3, + 'CacheTagLookupQueryCount' => 5, 'ScriptCount' => 2, 'ScriptBytes' => 123850, 'StylesheetCount' => 2, diff --git a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryNodePagePerformanceTest.php b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryNodePagePerformanceTest.php index ab531cff959d..c18710cd8151 100644 --- a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryNodePagePerformanceTest.php +++ b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryNodePagePerformanceTest.php @@ -287,7 +287,7 @@ protected function testNodePageWarmCache(): void { $expected = [ 'QueryCount' => 171, - 'CacheGetCount' => 255, + 'CacheGetCount' => 225, 'CacheGetCountByBin' => [ 'page' => 1, 'config' => 66, @@ -296,14 +296,14 @@ protected function testNodePageWarmCache(): void { 'data' => 13, 'entity' => 18, 'dynamic_page_cache' => 2, - 'render' => 73, + 'render' => 43, 'default' => 3, 'menu' => 2, ], 'CacheSetCount' => 41, 'CacheDeleteCount' => 0, 'CacheTagInvalidationCount' => 0, - 'CacheTagLookupQueryCount' => 27, + 'CacheTagLookupQueryCount' => 22, 'CacheTagGroupedLookups' => [ [ 'entity_types', @@ -354,13 +354,44 @@ protected function testNodePageWarmCache(): void { 'block_view', 'config:block.block.umami_search', ], - ['config:block.block.umami_account_menu', 'config:system.menu.account'], - ['config:block.block.umami_branding', 'config:system.site'], - ['config:block.block.umami_main_menu', 'config:system.menu.main'], + [ + 'config:block.block.umami_branding', + 'config:system.site', + ], ['config:block.block.umami_messages'], ['config:block.block.umami_help'], [ + 'block_content:1', + 'block_content:2', + 'config:block.block.umami_account_menu', + 'config:block.block.umami_banner_home', + 'config:block.block.umami_banner_recipes', + 'config:block.block.umami_breadcrumbs', + 'config:block.block.umami_content', + 'config:block.block.umami_disclaimer', + 'config:block.block.umami_footer', + 'config:block.block.umami_footer_promo', + 'config:block.block.umami_languageswitcher', + 'config:block.block.umami_local_tasks', + 'config:block.block.umami_main_menu', + 'config:block.block.umami_page_title', + 'config:block.block.umami_views_block__articles_aside_block_1', + 'config:block.block.umami_views_block__promoted_items_block_1', 'config:block.block.umami_views_block__recipe_collections_block', + 'config:block_list', + 'config:configurable_language_list', + 'http_response', + ], + [ + 'config:system.menu.account', + 'config:system.menu.footer', + 'block_content_view', + 'config:core.entity_view_display.block_content.footer_promo_block.default', + 'config:core.entity_view_display.media.image.medium_8_7', + 'config:image.style.medium_8_7', + 'file:37', + 'media:19', + 'config:system.menu.main', 'taxonomy_term:1', 'taxonomy_term:10', 'taxonomy_term:11', @@ -378,33 +409,6 @@ protected function testNodePageWarmCache(): void { 'taxonomy_term:9', 'taxonomy_term_list', ], - [ - 'block_content:2', - 'block_content_view', - 'config:block.block.umami_footer_promo', - 'config:core.entity_view_display.block_content.footer_promo_block.default', - 'config:core.entity_view_display.media.image.medium_8_7', - 'config:image.style.medium_8_7', - 'file:37', - 'media:19', - ], - ['config:block.block.umami_footer', 'config:system.menu.footer'], - ['config:block.block.umami_disclaimer'], - [ - 'block_content:1', - 'config:block.block.umami_banner_home', - 'config:block.block.umami_banner_recipes', - 'config:block.block.umami_breadcrumbs', - 'config:block.block.umami_content', - 'config:block.block.umami_languageswitcher', - 'config:block.block.umami_local_tasks', - 'config:block.block.umami_page_title', - 'config:block.block.umami_views_block__articles_aside_block_1', - 'config:block.block.umami_views_block__promoted_items_block_1', - 'config:block_list', - 'config:configurable_language_list', - 'http_response', - ], ['config:views.view.recipes'], ['config:workflows.workflow.editorial'], ['config:user.role.anonymous'], diff --git a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php index aa8d6dca9bb0..1adbeb26c376 100644 --- a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php +++ b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php @@ -127,7 +127,7 @@ protected function testAnonymous(): void { $this->assertSame($expected_queries, $recorded_queries); $expected = [ 'QueryCount' => 36, - 'CacheGetCount' => 132, + 'CacheGetCount' => 121, 'CacheGetCountByBin' => [ 'page' => 1, 'config' => 21, @@ -135,7 +135,7 @@ protected function testAnonymous(): void { 'discovery' => 38, 'bootstrap' => 8, 'dynamic_page_cache' => 2, - 'render' => 45, + 'render' => 34, 'default' => 5, 'entity' => 2, 'menu' => 2, @@ -171,16 +171,16 @@ protected function testAnonymous(): void { 'config:block.block.stark_search_form_narrow', 'config:search.settings', ], - ['config:block.block.stark_main_menu', 'config:system.menu.main'], ['config:block.block.stark_search_form_wide'], - ['config:block.block.stark_account_menu', 'config:system.menu.account'], ['config:block.block.stark_messages'], ['config:block.block.stark_help'], ['config:block.block.stark_powered'], ['config:block.block.stark_syndicate'], [ + 'config:block.block.stark_account_menu', 'config:block.block.stark_breadcrumbs', 'config:block.block.stark_content', + 'config:block.block.stark_main_menu', 'config:block.block.stark_page_title', 'config:block.block.stark_primary_admin_actions', 'config:block.block.stark_primary_local_tasks', @@ -188,6 +188,8 @@ protected function testAnonymous(): void { 'config:block_list', 'http_response', ], + ['config:system.menu.main'], + ['config:system.menu.account'], ['config:user.role.anonymous'], ], 'StylesheetCount' => 1, @@ -225,7 +227,7 @@ protected function testAnonymous(): void { $this->assertSame($expected_queries, $recorded_queries); $expected = [ 'QueryCount' => 10, - 'CacheGetCount' => 102, + 'CacheGetCount' => 91, 'CacheSetCount' => 16, 'CacheDeleteCount' => 0, 'CacheTagInvalidationCount' => 0, @@ -254,17 +256,16 @@ protected function testAnonymous(): void { 'config:block.block.stark_search_form_narrow', 'config:search.settings', ], - ['config:block.block.stark_main_menu', 'config:system.menu.main'], ['config:block.block.stark_search_form_wide'], - ['config:block.block.stark_account_menu', 'config:system.menu.account'], ['config:block.block.stark_messages'], ['config:block.block.stark_help'], ['config:block.block.stark_powered'], ['config:block.block.stark_syndicate'], - [ + 'config:block.block.stark_account_menu', 'config:block.block.stark_breadcrumbs', 'config:block.block.stark_content', + 'config:block.block.stark_main_menu', 'config:block.block.stark_page_title', 'config:block.block.stark_primary_admin_actions', 'config:block.block.stark_primary_local_tasks', @@ -272,6 +273,8 @@ protected function testAnonymous(): void { 'config:block_list', 'http_response', ], + ['config:system.menu.main'], + ['config:system.menu.account'], ['config:user.role.anonymous'], ], 'StylesheetCount' => 1, @@ -306,7 +309,7 @@ protected function testAnonymous(): void { $this->assertSame($expected_queries, $recorded_queries); $expected = [ 'QueryCount' => 14, - 'CacheGetCount' => 87, + 'CacheGetCount' => 76, 'CacheSetCount' => 17, 'CacheDeleteCount' => 0, 'CacheTagInvalidationCount' => 0, @@ -361,11 +364,11 @@ protected function testLogin(): void { $this->assertSame($expected_queries, $recorded_queries); $expected = [ 'QueryCount' => 17, - 'CacheGetCount' => 83, + 'CacheGetCount' => 69, 'CacheSetCount' => 1, 'CacheDeleteCount' => 1, 'CacheTagInvalidationCount' => 0, - 'CacheTagLookupQueryCount' => 17, + 'CacheTagLookupQueryCount' => 14, 'CacheTagGroupedLookups' => [ // Form submission and login. [ @@ -403,20 +406,22 @@ protected function testLogin(): void { 'config:search.settings', ], ['config:system.menu.account', 'config:system.menu.main'], - ['config:block.block.stark_main_menu'], ['config:block.block.stark_search_form_wide'], - ['config:block.block.stark_account_menu'], - ['config:block.block.stark_breadcrumbs'], - ['config:block.block.stark_primary_admin_actions'], ['config:block.block.stark_messages'], + ['config:block.block.stark_help'], + ['config:block.block.stark_powered'], + ['config:block.block.stark_syndicate'], + ['config:block.block.stark_main_menu'], + [ + 'config:block.block.stark_account_menu', + 'config:block.block.stark_breadcrumbs', + 'config:block.block.stark_primary_admin_actions', + ], [ 'config:block.block.stark_primary_local_tasks', 'config:user.role.authenticated', + 'config:block.block.stark_secondary_local_tasks' ], - ['config:block.block.stark_secondary_local_tasks'], - ['config:block.block.stark_help'], - ['config:block.block.stark_powered'], - ['config:block.block.stark_syndicate'], ], ]; $this->assertMetrics($expected, $performance_data); @@ -473,7 +478,7 @@ protected function testLoginBlock(): void { 'CacheSetCount' => 1, 'CacheDeleteCount' => 1, 'CacheTagInvalidationCount' => 0, - 'CacheTagLookupQueryCount' => 19, + 'CacheTagLookupQueryCount' => 20, ]; $this->assertMetrics($expected, $performance_data); } -- GitLab From 4c8546ff86ba2df3101a79e6f1d4d44c85ad2fee Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher <saschagros@gmail.com> Date: Sat, 29 Mar 2025 12:18:35 +0100 Subject: [PATCH 2/4] phpcs --- .../tests/src/FunctionalJavascript/StandardPerformanceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php index 1adbeb26c376..920c2f524d99 100644 --- a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php +++ b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php @@ -420,7 +420,7 @@ protected function testLogin(): void { [ 'config:block.block.stark_primary_local_tasks', 'config:user.role.authenticated', - 'config:block.block.stark_secondary_local_tasks' + 'config:block.block.stark_secondary_local_tasks', ], ], ]; -- GitLab From e06778ca00900488b922418d2131de30b2e19e51 Mon Sep 17 00:00:00 2001 From: catch <6915-catch@users.noreply.drupalcode.org> Date: Mon, 31 Mar 2025 14:10:54 +0100 Subject: [PATCH 3/4] Only skip fetching from cache when is_root_call is true. --- core/lib/Drupal/Core/Render/Renderer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index c295fa5cb1b9..c1f3a3f9450d 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -280,10 +280,12 @@ protected function doRender(&$elements, $is_root_call = FALSE) { // If the element was set to create a placeholder, we do not try to load it // from the cache, as \Drupal\Core\Render\Placeholder\CachedStrategy has an // optimization to load them all in one go, rather than individually here. + // This is only skipped when $is_root_call is false since placeholder + // strategies do not run at every level of a render array. // If the current request method is not cacheable, no placeholders are // generated, in that case, it's still better to fetch render cached // elements individually instead of fully building them again. - if (isset($elements['#cache']['keys']) && (empty($elements['#create_placeholder']) || !$this->requestStack->getCurrentRequest()->isMethodCacheable())) { + if (isset($elements['#cache']['keys']) && (empty($elements['#create_placeholder']) || !$this->requestStack->getCurrentRequest()->isMethodCacheable() || $is_root_call)) { $cached_element = $this->renderCache->get($elements); if ($cached_element !== FALSE) { $elements = $cached_element; -- GitLab From 9b89758be23b75eda894dbc15e124476208f7b10 Mon Sep 17 00:00:00 2001 From: catch <6915-catch@users.noreply.drupalcode.org> Date: Mon, 31 Mar 2025 15:05:42 +0100 Subject: [PATCH 4/4] Revert "Only skip fetching from cache when is_root_call is true." This reverts commit e06778ca00900488b922418d2131de30b2e19e51. --- core/lib/Drupal/Core/Render/Renderer.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index c1f3a3f9450d..c295fa5cb1b9 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -280,12 +280,10 @@ protected function doRender(&$elements, $is_root_call = FALSE) { // If the element was set to create a placeholder, we do not try to load it // from the cache, as \Drupal\Core\Render\Placeholder\CachedStrategy has an // optimization to load them all in one go, rather than individually here. - // This is only skipped when $is_root_call is false since placeholder - // strategies do not run at every level of a render array. // If the current request method is not cacheable, no placeholders are // generated, in that case, it's still better to fetch render cached // elements individually instead of fully building them again. - if (isset($elements['#cache']['keys']) && (empty($elements['#create_placeholder']) || !$this->requestStack->getCurrentRequest()->isMethodCacheable() || $is_root_call)) { + if (isset($elements['#cache']['keys']) && (empty($elements['#create_placeholder']) || !$this->requestStack->getCurrentRequest()->isMethodCacheable())) { $cached_element = $this->renderCache->get($elements); if ($cached_element !== FALSE) { $elements = $cached_element; -- GitLab