Commit 9fab1ad5 authored by catch's avatar catch

Issue #2471232 by borisson_, JeroenT, sorressean, rbmboogie, ruha,...

Issue #2471232 by borisson_, JeroenT, sorressean, rbmboogie, ruha, mariancalinro: Optimize Cache::merge*(), by only accepting 2 instead of N arguments
parent 0f9ebb2a
......@@ -24,19 +24,16 @@ class Cache {
/**
* Merges arrays of cache contexts and removes duplicates.
*
* @param string[] …
* Arrays of cache contexts to merge.
* @param array $a
* Cache contexts array to merge.
* @param array $b
* Cache contexts array to merge.
*
* @return string[]
* The merged array of cache contexts.
*/
public static function mergeContexts() {
$cache_context_arrays = func_get_args();
$cache_contexts = [];
foreach ($cache_context_arrays as $contexts) {
$cache_contexts = array_merge($cache_contexts, $contexts);
}
$cache_contexts = array_unique($cache_contexts);
public static function mergeContexts(array $a = [], array $b = []) {
$cache_contexts = array_unique(array_merge($a, $b));
\Drupal::service('cache_contexts_manager')->validateTokens($cache_contexts);
sort($cache_contexts);
return $cache_contexts;
......@@ -53,19 +50,16 @@ public static function mergeContexts() {
* allows items to be invalidated based on all tags attached to the content
* they're constituted from.
*
* @param string[] …
* Arrays of cache tags to merge.
* @param array $a
* Cache tags array to merge.
* @param array $b
* Cache tags array to merge.
*
* @return string[]
* The merged array of cache tags.
*/
public static function mergeTags() {
$cache_tag_arrays = func_get_args();
$cache_tags = [];
foreach ($cache_tag_arrays as $tags) {
$cache_tags = array_merge($cache_tags, $tags);
}
$cache_tags = array_unique($cache_tags);
public static function mergeTags(array $a = [], array $b = []) {
$cache_tags = array_unique(array_merge($a, $b));
static::validateTags($cache_tags);
sort($cache_tags);
return $cache_tags;
......@@ -76,29 +70,25 @@ public static function mergeTags() {
*
* Ensures infinite max-age (Cache::PERMANENT) is taken into account.
*
* @param int …
* Max-age values.
* @param int $a
* Max age value to merge.
* @param int $b
* Max age value to merge.
*
* @return int
* The minimum max-age value.
*/
public static function mergeMaxAges() {
$max_ages = func_get_args();
// Filter out all max-age values set to cache permanently.
if (in_array(Cache::PERMANENT, $max_ages)) {
$max_ages = array_filter($max_ages, function ($max_age) {
return $max_age !== Cache::PERMANENT;
});
// If nothing is left, then all max-age values were set to cache
// permanently, and then that is the result.
if (empty($max_ages)) {
return Cache::PERMANENT;
public static function mergeMaxAges($a = Cache::PERMANENT, $b = Cache::PERMANENT) {
// If one of the values is Cache::PERMANENT, return the other value.
if ($a === Cache::PERMANENT){
return $b;
}
if ($b === Cache::PERMANENT){
return $a;
}
return min($max_ages);
// If none or the values are Cache::PERMANENT, return the minimum value.
return min($a, $b);
}
/**
......
......@@ -365,7 +365,8 @@ public function resetCache(array $entities = NULL) {
if (isset($entities)) {
$tags = [];
foreach ($entities as $entity) {
$tags = Cache::mergeTags($tags, $entity->getCacheTags(), $entity->getEntityType()->getListCacheTags());
$tags = Cache::mergeTags($tags, $entity->getCacheTags());
$tags = Cache::mergeTags($tags, $entity->getEntityType()->getListCacheTags());
}
Cache::invalidateTags($tags);
}
......
......@@ -48,6 +48,9 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
$derivative_id = $plugin->getDerivativeId();
$configuration = $plugin->getConfiguration();
$cache_tags = Cache::mergeTags($this->getCacheTags(), $entity->getCacheTags());
$cache_tags = Cache::mergeTags($cache_tags, $plugin->getCacheTags());
// Create the render array for the block as a whole.
// @see template_preprocess_block().
$build[$entity_id] = array(
......@@ -68,11 +71,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
'#cache' => [
'keys' => ['entity_view', 'block', $entity->id()],
'contexts' => $plugin->getCacheContexts(),
'tags' => Cache::mergeTags(
$this->getCacheTags(), // Block view builder cache tag.
$entity->getCacheTags(), // Block entity cache tag.
$plugin->getCacheTags() // Block plugin cache tags.
),
'tags' => $cache_tags,
'max-age' => $plugin->getCacheMaxAge(),
],
'#pre_render' => [
......
......@@ -91,12 +91,14 @@ public function testBlock() {
// @see \Drupal\block\BlockViewBuilder::viewMultiple()
$expected_block_cache_keys = ['entity_view', 'block', $block->id()];
$expected_block_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
$expected_block_cache_tags = Cache::mergeTags(['block_view', 'rendered'], $block->getCacheTags(), $block->getPlugin()->getCacheTags());
$expected_block_cache_tags = Cache::mergeTags(['block_view', 'rendered'], $block->getCacheTags());
$expected_block_cache_tags = Cache::mergeTags($expected_block_cache_tags, $block->getPlugin()->getCacheTags());
// Expected contexts and tags for the BlockContent entity.
// @see \Drupal\Core\Entity\EntityViewBuilder::getBuildDefaults().
$expected_entity_cache_contexts = ['theme'];
$expected_entity_cache_tags = Cache::mergeTags(['block_content_view'], $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntity($this->entity));
$expected_entity_cache_tags = Cache::mergeTags(['block_content_view'], $this->entity->getCacheTags());
$expected_entity_cache_tags = Cache::mergeTags($expected_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
// Verify that what was render cached matches the above expectations.
$cid = $this->createCacheId($expected_block_cache_keys, $expected_block_cache_contexts);
......
......@@ -200,11 +200,8 @@ public function testEntityFormatter() {
';
$renderer->renderRoot($build[0]);
$this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() . $expected_rendered_name_field_1 . $expected_rendered_body_field_1, sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
$expected_cache_tags = Cache::mergeTags(
\Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(),
$this->referencedEntity->getCacheTags(),
FilterFormat::load('full_html')->getCacheTags()
);
$expected_cache_tags = Cache::mergeTags(\Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(), $this->referencedEntity->getCacheTags());
$expected_cache_tags = Cache::mergeTags($expected_cache_tags, FilterFormat::load('full_html')->getCacheTags());
$this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
// Test the second field item.
......
......@@ -266,10 +266,12 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) {
$do_assert_views_caches,
Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags)
);
$expected_tags = Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags);
$expected_tags = Cache::mergeTags($expected_tags, ['rendered', 'config:user.role.anonymous']);
$this->assertPageCacheContextsAndTags(
Url::fromRoute('view.frontpage.page_1'),
$cache_contexts,
Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags, ['rendered', 'config:user.role.anonymous'])
$expected_tags
);
// Create some nodes on the frontpage view. Add more than 10 nodes in order
......@@ -312,10 +314,8 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) {
'node:15',
];
$cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts)->getCacheTags();
$first_page_output_cache_tags = Cache::mergeTags(
$first_page_result_cache_tags,
$cache_context_tags,
[
$first_page_output_cache_tags = Cache::mergeTags($first_page_result_cache_tags, $cache_context_tags);
$first_page_output_cache_tags = Cache::mergeTags($first_page_output_cache_tags, [
'config:filter.format.plain_text',
'node_view',
'user_view',
......
......@@ -339,16 +339,14 @@ public function testReferencedEntity() {
$entity_cache_contexts = $default_cache_contexts;
// Cache tags present on every rendered page.
$page_cache_tags = Cache::mergeTags(
['rendered'],
// 'user.permissions' is a required cache context, and responses that vary
// by this cache context when requested by anonymous users automatically
// also get this cache tag, to ensure correct invalidation.
['config:user.role.anonymous'],
$page_cache_tags = Cache::mergeTags(['rendered'], ['config:user.role.anonymous']);
// If the block module is used, the Block page display variant is used,
// which adds the block config entity type's list cache tags.
\Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list']: []
);
$page_cache_tags = Cache::mergeTags($page_cache_tags, \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list']: []);
$page_cache_tags_referencing_entity = in_array('user.permissions', $this->getAccessCacheContextsForEntity($this->referencingEntity)) ? ['config:user.role.anonymous'] : [];
$view_cache_tag = array();
......@@ -361,40 +359,34 @@ public function testReferencedEntity() {
$cache_context_tags = $context_metadata->getCacheTags();
// Generate the cache tags for the (non) referencing entities.
$referencing_entity_cache_tags = Cache::mergeTags(
$this->referencingEntity->getCacheTags(),
\Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(),
$referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
// Includes the main entity's cache tags, since this entity references it.
$this->entity->getCacheTags(),
$this->getAdditionalCacheTagsForEntity($this->entity),
$view_cache_tag,
$cache_context_tags,
['rendered']
);
$non_referencing_entity_cache_tags = Cache::mergeTags(
$this->nonReferencingEntity->getCacheTags(),
\Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(),
['rendered']
);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->entity->getCacheTags());
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $view_cache_tag);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $cache_context_tags);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']);
$non_referencing_entity_cache_tags = Cache::mergeTags($this->nonReferencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
$non_referencing_entity_cache_tags = Cache::mergeTags($non_referencing_entity_cache_tags, ['rendered']);
// Generate the cache tags for all two possible entity listing paths.
// 1. list cache tag only (listing query has no match)
// 2. list cache tag plus entity cache tag (listing query has a match)
$empty_entity_listing_cache_tags = Cache::mergeTags(
$this->entity->getEntityType()->getListCacheTags(),
$page_cache_tags
);
$nonempty_entity_listing_cache_tags = Cache::mergeTags(
$this->entity->getEntityType()->getListCacheTags(),
$this->entity->getCacheTags(),
$this->getAdditionalCacheTagsForEntityListing($this->entity),
$page_cache_tags
);
$empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags);
$nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags());
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $this->getAdditionalCacheTagsForEntityListing($this->entity));
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags);
$this->pass("Test referencing entity.", 'Debug');
$this->verifyPageCache($referencing_entity_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags, $page_cache_tags_referencing_entity));
$expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags);
$expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity);
$this->verifyPageCache($referencing_entity_url, 'HIT', $expected_tags);
// Also verify the existence of an entity render cache entry.
$cache_keys = ['entity_view', 'entity_test', $this->referencingEntity->id(), 'full'];
$cid = $this->createCacheId($cache_keys, $entity_cache_contexts);
......@@ -402,7 +394,8 @@ public function testReferencedEntity() {
$additional_cache_contexts = $this->getAdditionalCacheContextsForEntity($this->referencingEntity);
$redirected_cid = NULL;
if (count($access_cache_contexts) || count($additional_cache_contexts)) {
$cache_contexts = Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts, $access_cache_contexts);
$cache_contexts = Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts);
$cache_contexts = Cache::mergeContexts($cache_contexts, $access_cache_contexts);
$redirected_cid = $this->createCacheId($cache_keys, $cache_contexts);
$context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $context_metadata->getCacheTags());
......@@ -422,9 +415,11 @@ public function testReferencedEntity() {
$this->pass("Test listing of referencing entities.", 'Debug');
// Prime the page cache for the listing of referencing entities.
$this->verifyPageCache($listing_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$this->verifyPageCache($listing_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags, $page_cache_tags_referencing_entity));
// Verify a cache hit, but also the presence of the correct cache tags.
$expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags);
$expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity);
$this->verifyPageCache($listing_url, 'HIT', $expected_tags);
$this->pass("Test empty listing.", 'Debug');
// Prime the page cache for the empty listing.
......@@ -643,15 +638,16 @@ public function testReferencedEntity() {
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
// Verify cache hits.
$referencing_entity_cache_tags = Cache::mergeTags(
$this->referencingEntity->getCacheTags(),
\Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(),
['rendered']
);
$referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']);
$nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing());
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags);
$this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags));
$this->verifyPageCache($listing_url, 'HIT', $page_cache_tags);
$this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing(), $page_cache_tags));
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
}
/**
......
......@@ -56,7 +56,9 @@ public function testEntityUri() {
if (count($additional_cache_contexts)) {
$redirected_cid = $this->createCacheId($cache_keys, Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts));
}
$expected_cache_tags = Cache::mergeTags($cache_tag, $view_cache_tag, $this->getAdditionalCacheTagsForEntity($this->entity), array($render_cache_tag));
$expected_cache_tags = Cache::mergeTags($cache_tag, $view_cache_tag);
$expected_cache_tags = Cache::mergeTags($expected_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
$expected_cache_tags = Cache::mergeTags($expected_cache_tags, array($render_cache_tag));
$this->verifyRenderCache($cid, $expected_cache_tags, $redirected_cid);
}
......
......@@ -79,7 +79,9 @@ function testTrackerAll() {
// Assert cache contexts, specifically the pager and node access contexts.
$this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user.node_grants:view', 'user.permissions']);
// Assert cache tags for the visible node and node list cache tag.
$this->assertCacheTags(Cache::mergeTags($published->getCacheTags(), $published->getOwner()->getCacheTags(), ['node_list', 'rendered']));
$expected_tags = Cache::mergeTags($published->getCacheTags(), $published->getOwner()->getCacheTags());
$expected_tags = Cache::mergeTags($expected_tags, ['node_list', 'rendered']);
$this->assertCacheTags($expected_tags);
// Delete a node and ensure it no longer appears on the tracker.
$published->delete();
......@@ -144,14 +146,13 @@ function testTrackerUser() {
$this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
// Assert cache tags for the visible nodes (including owners) and node list
// cache tag.
$tags = Cache::mergeTags(
$my_published->getCacheTags(),
$my_published->getOwner()->getCacheTags(),
$other_published_my_comment->getCacheTags(),
$other_published_my_comment->getOwner()->getCacheTags(),
['node_list', 'rendered']
);
$this->assertCacheTags($tags);
$expected_tags = Cache::mergeTags($my_published->getCacheTags(), $my_published->getOwner()->getCacheTags());
$expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getCacheTags());
$expected_tags = Cache::mergeTags($expected_tags, $other_published_my_comment->getOwner()->getCacheTags());
$expected_tags = Cache::mergeTags($expected_tags, ['node_list', 'rendered']);
$this->assertCacheTags($expected_tags);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args.pagers:0', 'user', 'user.node_grants:view']);
$this->assertLink($my_published->label());
$this->assertNoLink($unpublished->label());
......
......@@ -124,7 +124,11 @@ protected function assertCacheTagsForFieldBasedView($do_assert_views_caches) {
$this->pass('Test pager');
$this->pass('Page 1');
\Drupal::request()->query->set('page', 0);
$tags_page_1 = Cache::mergeTags($base_tags, $entities[1]->getCacheTags(), $entities[2]->getCacheTags(), $entities[3]->getCacheTags(), $entities[4]->getCacheTags(), $entities[5]->getCacheTags());
$tags_page_1 = Cache::mergeTags($base_tags, $entities[1]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[2]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[3]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[4]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[5]->getCacheTags());
$this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1);
$this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_1, $do_assert_views_caches);
$view->destroy();
......@@ -254,7 +258,8 @@ protected function assertCacheTagsForEntityBasedView($do_assert_views_caches) {
$entity->save();
$result_tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags());
$render_tags_with_entity = Cache::mergeTags($base_render_tags, $entities[0]->getCacheTags(), ['entity_test_view']);
$render_tags_with_entity = Cache::mergeTags($base_render_tags, $entities[0]->getCacheTags());
$render_tags_with_entity = Cache::mergeTags($render_tags_with_entity, ['entity_test_view']);
$this->assertViewsCacheTags($view, $result_tags_with_entity, $do_assert_views_caches, $render_tags_with_entity);
$this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_with_entity, $do_assert_views_caches);
......@@ -265,9 +270,13 @@ protected function assertCacheTagsForEntityBasedView($do_assert_views_caches) {
$entity->save();
}
$new_entities_cache_tags = Cache::mergeTags($entities[1]->getCacheTags(), $entities[2]->getCacheTags(), $entities[3]->getCacheTags(), $entities[4]->getCacheTags(), $entities[5]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($entities[1]->getCacheTags(), $entities[2]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[3]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[4]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[5]->getCacheTags());
$result_tags_page_1 = Cache::mergeTags($base_tags, $new_entities_cache_tags);
$render_tags_page_1 = Cache::mergeTags($base_render_tags, $new_entities_cache_tags, ['entity_test_view']);
$render_tags_page_1 = Cache::mergeTags($base_render_tags, $new_entities_cache_tags);
$render_tags_page_1 = Cache::mergeTags($render_tags_page_1, ['entity_test_view']);
$this->assertViewsCacheTags($view, $result_tags_page_1, $do_assert_views_caches, $render_tags_page_1);
$this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_page_1, $do_assert_views_caches);
}
......
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