I think there is a performance problem when views are displayed on every site page and when there is a lot of content (node, term, etc) modification.

The problem appears on the following circumstances :

  1. There is (for example) 3 nodes updated every minute
  2. There is a view which list nodes on the homepage
  3. There is a views which list nodes on a sidebar on every page
  4. The page_cache module is enabled

Views will add a "node_list" tag on every views (cf https://git.drupalcode.org/project/drupal/blob/8.7.x/core/modules/views/...).

Pages with view (so every page here) will have the "node_list" tag because tags are bubbled on the entire page.
So every minute all page cache will be invalidated 3 times (after each node update) and it will be like there is no cache at all.

This problem occurs even if the nodes updated are not displayed in these views because the tag "[entity_type]_list" is too generic.

The issue https://www.drupal.org/project/drupal/issues/2145751 introduce a new cache tag per bundle we can use in Views to avoid to invalidate the whole cache site on each node save.

CommentFileSizeAuthor
#66 3055371-67.patch3.03 KBjoseph.olstad
#66 interdiff-3055371-65_to_67.txt723 bytesjoseph.olstad
#65 3055371-65.patch3.01 KBjoseph.olstad
#63 3055371-nr-bot.txt144 bytesneeds-review-queue-bot
#56 interdiff-55_56.txt585 bytesOscaner
#56 3055371-56.patch3 KBOscaner
#55 interdiff-34_55.txt4.72 KBOscaner
#55 3055371-55.patch3.03 KBOscaner
#53 interdiff_52_53.txt476 bytesanmolgoyal74
#53 views-bundle_cache_tag-3055371-53.patch4.45 KBanmolgoyal74
#52 views-bundle_cache_tag-3055371-52.patch4.5 KBjonnyeom
#52 interdiff-49_52.txt922 bytesjonnyeom
#49 3055371-49.patch3.69 KBadinac
#47 3055371-47.patch3.57 KBadinac
#42 3055371-42.patch3.58 KBpnagornyak
#34 3055371-34.patch2.8 KBandypost
#34 interdiff.txt776 bytesandypost
#32 3055371-32.patch2.77 KBandypost
#32 interdiff.txt1.62 KBandypost
#31 3055371-31.patch2.52 KBandypost
#31 interdiff.txt1.92 KBandypost
#29 3055371-28.patch617 bytesandypost
#28 3055371-27.patch615 bytesandypost
#19 use-bundle-specific-cache-tag-3055371-19-D8.patch3.74 KBNixou
#16 drupal-use-bundle-specific-cache-tag-3055371-16-D8.patch1.73 KBNixou
#2 drupal-remove-generic-entity-cache-tag-3055371-1-D8.patch1.01 KBNixou
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Nixou created an issue. See original summary.

Nixou’s picture

Nixou’s picture

Status: Active » Needs review

Status: Needs review » Needs work
idebr’s picture

Perhaps you can simply disable caching in your views configuration?

Nixou’s picture

Yes it was what I think about initially but if you disable the caching of the view, you are using the "None" plugin : https://git.drupalcode.org/project/drupal/blob/8.7.x/core/modules/views/....

This plugin extends the CachePluginBase.
If you disable the caching of the view, the "[entity_type]_list" tag remains on the view because this tag is added in CachePluginBase.

So this problem occurs with or without cache on the view.

dawehner’s picture

I wouldn't really call this a bug, it's mostly more like an intended feature of the Drupal cache system.
Some comments about this: https://www.drupal.org/project/views_custom_cache_tag tries to provide a solution for this.
There is also a core issue, see https://www.drupal.org/project/drupal/issues/2145751 . Maybe I would suggest to mark this issue as "closed (worked as intended)"

Nixou’s picture

Using Views Custom Cache Tags to workaround this problem is not ideal as you need to specify a custom tag to override the default "node_list".
Otherwise this tag remains on the view.

So then you need to write custom code to invalidate this custom tag.

https://www.drupal.org/project/drupal/issues/2145751 seems relative to this problem but I'm not sure it will solve the problem in a generic way.
I'll try the patch provided.

I think this issue could be close if the solution provided in 2145751 is or become generic enough.

I mean that if 2145751 will just replace "node_list" by "node_list:page" in some cases in the "default tags" provided by CachePluginBase, this will not solve the whole problem as I may not want to clear all views listing pages when I update a page (especially if i disable the cache of the view).

Berdir’s picture

Yes, this overlaps with several other issues. #2145751: Introduce ENTITY_TYPE_list:BUNDLE cache tag and add it to single bundle listing will allow to use views_custom_cache_tag without custom code but it will still require additional modules and manual configuration.

#2498857: Support min-age in render caching is another approach, that adds min-age to render caching.

As @dawehner said, the current behavior is for many cases not optimal but it is correct in ensuring that the correct information is shown all the time. I think strategies to change that, as in forcing longer caches or skipping invalidations, should be done in contrib. There could be "time only" cache plugin for example. Note that it still needs to respect the cache tags specific entities that are shown, or you might be unable to unpublish or delete content, whic is important.

Nixou’s picture

I'm not sure about the fact that the current behavior is correct.

I can't see the logic to have a view that define a tag even if the cache is disabled for this view.
If a component doesn't use cache, it may not use cache tag either.

#2145751 is about to precise the cache tag when there is a need to have one.
#2498857 seems to be about controlling cache by time (when there is a need to have cache).
And this issue can be about to not enforce a cache tag for all core views cache plugin, especially the "None" cache plugin.

We can remove the enforced tag in CachePluginBase so the "None" cache plugin will not inherit it and then add this tag on other cache plugin.
Or we can change the "None" cache plugin to override cache tags and return an empty array.
Or maybe create another cache plugin "None - Without cache tag".

But the core can't just keep this big performance leak without providing choice / solution.

Berdir’s picture

> If a component doesn't use cache, it may not use cache tag either.

Yes it does, because there's still the page cache, surrounding render caches and possibly other things, none just means the view isn't cached on its own.

> But the core can't just keep this big performance leak without providing choice / solution.

That's where I disagree, this is not a leak, this is how it is designed to work. Unlike drupal 7, where content changes invalidated every content cache, D8 still has the nodes and other blocks render cached, just the parts that have to are invalidated. It's not optimal, but there are ways to improve that.

> Or maybe create another cache plugin "None - Without cache tag".

I don't think none without cache tag is a good idea, but time-based without the list cache tag could be an option yes, but I don't think that needs to be in drupal core.

venkatadapa’s picture

node_list cache tag is definitely a bottleneck for performance issues if we have more content types and content editors changing content more frequently and we are displaying views block with nodes in many pages. Actually we use views with lot of filters to create the page/block display, But the more generic node_list tag invalidates caches on all pages where this views block displayed even though non-relate content updates.

One solution as of now is using this module https://www.drupal.org/project/views_custom_cache_tag and it allow you to enter view specific cache tags something like entity:bundle:display-mode / mysite:node:category: and then write custom code to invalidate this particular cache in entity presave hooks.

Right now there is no out of the box solution available for this issue, Some related issues are already raised and discussion going on for example https://www.drupal.org/project/drupal/issues/2145751 talking about adding bundle name also to the node_list tag like node_list:page.

Nixou’s picture

I understand views needs to take care of other cache system and especially the page cache system.

When the view use the "None" plugin : if a content is updated the cache needs to be refresh immediately including the page cache.
So it's logic to have a "node_list" tag.

This tag is currently too generic but this will be improved by https://www.drupal.org/project/drupal/issues/2145751 by having a more specific tag (by bundle).
We need to works on this issue ASAP.

It's the same thing when the view's cache is "Tag based".

On the other side, I don't understand for now why the "node_list" tag remains when the view's cache is "time based".
In this case the behavior is :

  1. The view's cache is not yet expired
  2. A content is updated : the view's cache is not updated but the page cache is invalidated for nothing (due to the "node_list" tag)

I think it would be more logic to have the node_list tag for "None" and "Tag based" (improved by 2145751).
And to have no tag for "Time based".

What do you think ?

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.9 was released on November 6 and is the final full bugfix release for the Drupal 8.7.x series. Drupal 8.7.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.8.0 on December 4, 2019. (Drupal 8.8.0-beta1 is available for testing.)

Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Nixou’s picture

Nixou’s picture

I saw that there had been progress on the issue https://www.drupal.org/project/drupal/issues/2145751.

There is now a patch that is working and that provides bundle specific cache tag : https://www.drupal.org/files/issues/2019-11-14/2145751-122.patch.

This patch make sure that tags "[entity_type]_list:[bundle]" will be invalidated when an entity of a particular bundle is created / updated / deleted.

Since the issue 2145751 is not intented to update views behavior, I created the patch here with the following behavior :

  1. If the view use a filter by bundle, the cache tags provided for the view are "bundle based" depending the bundle choose in the filter configuration
  2. If the view do not use a filter by bundle, the cache tag provided for the view is the default one "[entity_type]"_list

The patch is created against 8.9.x and require the patch 2145751-122.patch (linked above) to work.

Berdir’s picture

I think it would be more logic to have the node_list tag for "None" and "Tag based" (improved by 2145751).
And to have no tag for "Time based".

What do you think ?

Yes, the time based caching that exists at the moment is pretty useless, I do agree with that. Still, changing the behavior of an existing plugin is problematic as it will change for sites.

One option would be to add another cache plugin and have descriptions/explanations for the behavior difference.

There are also issues to support min-age caching, so it would return it from the cache for at least X seconds. But that's challenging and just having a time-based version that's not invalidated by new entities might be best solution.

The problem is that there will be confusing cases, imagine a view with time-based caching and a pager. It shows node 1-5 on the first page and 6-10 on the second. Now, node 3 gets unpublished or deleted. You'd expect the first page to now show 1,2,4,5,6 and the second page 7-11, because that moves up by one. With the list cache tag, that happens, but without that, only the first page that actually has node 3 gets invalidated. So a user will see item 6 on both pages. Not a huge deal, but still, it will definitely result in confusion and bug reports :) So it will be important to explain the advantages/drawbacks of both options (the only advantage of the current option is that it will definitely rebuild after X seconds, in case changes are not something that can be invalidated (e.g. "upcoming events starting in the future"))

Status: Needs review » Needs work
Nixou’s picture

Status: Needs work » Needs review
FileSize
3.74 KB

New patch with :

- Deletion of the test "saving a node not in this view invalidates the cache too" since its what we don't want anymore
- Adaptation of resetCache() in EntityViewBuilder.php so the new "bundle specific tag" is cleared as are the generic ones

Nixou’s picture

Title: Most page cache are invalidated when updating content » Use new cache tag ENTITY_TYPE_list:BUNDLE in Views
Issue summary: View changes

Status: Needs review » Needs work

The last submitted patch, 19: use-bundle-specific-cache-tag-3055371-19-D8.patch, failed testing. View results

Nixou’s picture

The patch passed test on local environments since I also applied the one in the issue 2145751.

So I guess we need now to wait until 2145751 is committed before to continue here.

Lendude’s picture

Status: Needs work » Postponed
geek-merlin’s picture

Let's not forget @berdir's #17:
> Still, changing the behavior of an existing plugin is problematic as it will change for sites.
> One option would be to add another cache plugin and have descriptions/explanations for the behavior difference.

Or we add a setting on the bundle filter plugin.

In any case,
* the code to add the bundle cache tags might better live in \Drupal\views\Plugin\views\filter\Bundle::getCacheTags (to not violate Encapsulation)
* we'd still need to remove the list cache tag in CachePluginBase:getCacheTags depending on the filter tags
* we'll have to account for the case of ORed filters. If you OR two bundle filters, the resulting tags are different as when you AND them. When you OR a bundle filter with a is-published filter, the result is list tag.

Berdir’s picture

FWIW, my comment was about changing the behavior for the time cache plugin, this is a bit different and might be OK, but we'll have to figure that out once the blocker is committed (which btw now should be ready and needs reviews to be able to set it to RTBC).

catch’s picture

Status: Postponed » Active
Wim Leers’s picture

Title: Use new cache tag ENTITY_TYPE_list:BUNDLE in Views » Use new cache tag ENTITY_TYPE_list:BUNDLE in Views to improve cache hit rate
Priority: Normal » Major
Status: Active » Needs work
Issue tags: +D8 cacheability

Marking needs work because there already is a patch, in #19!

andypost’s picture

At least bundle filter could add tags

andypost’s picture

FileSize
617 bytes

valid patch

catch’s picture

So the bundle filter is the safest way to add the cache tags, but is there a way for views to then not add the entity_list cache tag when those are added? Otherwise we don't get the actual cache invalidation benefits.

andypost’s picture

I think kind of it could work, basically here could be more then one bundle filter so they add override for "old" list tags

andypost’s picture

FileSize
1.62 KB
2.77 KB

Cache tag names fixed, also changed comments a bit

The last submitted patch, 31: 3055371-31.patch, failed testing. View results

andypost’s picture

FileSize
776 bytes
2.8 KB

filters are null by default( could use follow-up to set default value to empty array

The last submitted patch, 32: 3055371-32.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 34: 3055371-34.patch, failed testing. View results

Berdir’s picture

Issue tags: +Needs tests
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -234,14 +235,35 @@ public function generateResultsKey() {
+    $filters = $this->view->filter ?: [];
+    foreach ($filters as $filter) {
+      if ($filter instanceof Bundle && $filter->value) {
+        // Bundle filters could provide optimized cache tags when limit query to
+        // specific list.
+        $entity_type_id = $filter->getEntityType();
+        $filter_tags[$entity_type_id] = array_map(function ($bundle) use ($entity_type_id) {

we'll also need to check the operator, as it could be an exclude condition.

I'm wondering if there are any scenarios where this could cause a problem other than that, not sure yet.

geek-merlin’s picture

> we'll also need to check the operator, as it could be an exclude condition.
> I'm wondering if there are any scenarios where this could cause a problem other than that, not sure yet.

Good point. What i stated in #24 implies that any correct implementation must look on all negations, conditions, and condition groups.
And pass at least these tests (assuming we only have page and article bundles):

  • bundle-in(page) => node_list:page
  • NOT-bundle-in(page) => node_list:article
  • bundle-in(page) AND is-published => node_list:page
  • bundle-in(page) OR is-published => node_list
  • bundle-in(page) AND bundle-in(page,article) => node_list:page
  • bundle-in(page) OR bundle-in(page,article) => node_list:page,node_list:article
  • bundle-in(page) OR NOT-bundle-in(page) => node_list
  • (bundle-in(page) AND is-published) OR (NOT-bundle-in(page) AND is-published) => node_list
PQ’s picture

One other edge case could be views with relationships to the same entity type, where a bundle filter exists on either the queried entity or the related entity and does not exist on the other. This might mean that the view should still have the ENTITY_TYPE_list tag since changes to entities of different bundles could yield different results for the view, but I think if I understand the patch correctly, this wouldn't happen.

It looks like since $this->view->getQuery()->getEntityTableInfo() contains results for each relationship (keys generated with $entity_tables[$relationship_id . '__' . $relationship->tableAlias]), if we set the keys of $filter_tags to match we could check the filter against each relationship instead of against each entity type.

Berdir’s picture

Yes, it can get complicated. For BC and edge-cases, we could add trying-to-use-bundle-cache-tag as a new setting, default to enabled for new views while existing ones would default to false?

PQ’s picture

Hi Berdir,

I was thinking the same, but perhaps it might be worth initially committing the change with it defaulting to disabled in all circumstances and change to defaulting to enabled for new views in a follow up once it's been soak-tested in the real world for a while.

pnagornyak’s picture

Hi there.
I agree that this bundle list cache option should not be enabled by default at this point, so I created new views cache plugin that fallback to default cache tag plugin if there is no bundle filter in views. It works with IN and NOT IN operators as well as with grouped filters, please check it.
I also tried to find out how to correctly create a test for this new plugin, but I have not found anything how to easily build a view with filter on the fly.

geek-merlin’s picture

@berdir #40:

(#38)
> [This] implies that any correct implementation must look on all negations, conditions, and condition groups.

I doubt that anyone trying to code that understood that this effectively means duplicating the query builder. Or extend the api to have caching passed up from filters. Which both is complex, and the first an imho too big maintenance burden for the effect.

Instead i'd advocate let sitebuilder add cache tags, and crafting a good help text. (effectively moving your fine views_custom_cache_tag module into core)

pnagornyak’s picture

@geek-merlin I see your point, but what I`ve done it is completely safe as it is just fallbacks to default cache tag plugin and the new cache plugin will cover like 90% of views I guess (I have not seen some complex views with many grouped filters yet). So my point is that kind of solution should be available for end users either in Drupal core or in contrib module.

@berdir what do you think about moving patch #42 from this issue to your views_custom_cache_tag module?

Berdir’s picture

I'd be open to adding it to my project if we can't get it into core easily, probably as another cache plugin or a setting in the existing one?

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

adinac’s picture

There is an issue with the path for the new file in the patch from #42, there shouldn't be "web" in it. E.g.:
"b/core/modules/views/src/Plugin/views/cache/EntityListBundleTag.php" instead of "b/web/core/modules/views/src/Plugin/views/cache/EntityListBundleTag.php"

anmolgoyal74’s picture

Status: Needs work » Needs review
adinac’s picture

There were times when I received the following warning:

Warning: Invalid argument supplied for foreach() in Drupal\views\Plugin\views\cache\EntityListBundleTag->isSupported() (line 87 of core/modules/views/src/Plugin/views/cache/EntityListBundleTag.php).

So I added some checks on $this->view->filter.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

tanubansal’s picture

Added #49, Warning message issue has been resolved on 9.1

jonnyeom’s picture

Im really liking this idea,

It works wonders on most displays, but not for the ViewsBlock display,
It looks like its because $this->view->buildRenderable() is called ahead of View::preRenderViewElement(), the default cache tags like node_list are being added to $output['#view']->element['#cache'].

In fact, this means anything that uses the \Drupal\views\ViewExecutable::buildRenderable()method, like the block display or attachment display, will not be able to use the more specific cache tags.

I think there is two ways to go about this,

option 1

We can improve the caching process so that we only set the cache metadata after the View is rendered and the related cache tags are "calculated". I think we could avoid applying Cache Metadata in \Drupal\views\Plugin\views\display\DisplayPluginBase::buildRenderable().

option 2

Perhaps, we can unset the cache data in the ViewsBlock.php.
I don't like this one since that requires any plugins that use buildRenderable() to make a similar change to utilize the better cache.

Any thoughts on the 2 options?

Attached is a patch that does option 2 for now.

anmolgoyal74’s picture

Removed unused use statement.

Status: Needs review » Needs work

The last submitted patch, 53: views-bundle_cache_tag-3055371-53.patch, failed testing. View results

Oscaner’s picture

This patch based on #34, and did some improvement.

Oscaner’s picture

FileSize
3 KB
585 bytes

Remove useless ensureMyTable().

abhisekmazumdar’s picture

Status: Needs work » Needs review

Moving this to needs review as there is a patch that needs to be reviewed.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

alexpott’s picture

Reading the latest patch on this issue I don't see how this is going to replace the node_list cache tag that the view already has. It's going to add the additional bundle tags in but the generic node_list is not being removed. I've just created an issue to discuss how the published filter should affect the cache tags - #3214362: Add and use a node_list:published cache tag. I think these issues should work together to discuss how filters / where's can alter the list cache tags from entity queries / views / json lists.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
FileSize
144 bytes

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

joseph.olstad’s picture

patch 56 had some fuzz, new reroll has no fuzz.

joseph.olstad’s picture

joseph.olstad’s picture

The tests written for the views_custom_cache_tag module could inspire / be borrowed to help more easily create automated tests for a new patch, this would accelerate the creation of automated test coverage and hopefully move this issue forward more quickly.

Given the performance gain opportunity here and the wide proliferation of the views module and views in general , I think this is a worthy core effort that merits the effort.

Any volunteers? https://drupal.org/project/views_custom_cache_tag

joseph.olstad’s picture

I like the small scope of the above patch, if we can keep it simple , add the necessary test and no more, we can move this forward in a timely manner.