Problem/Motivation
The 'url' cache context for views block displays is sometimes missing when it shouldn't be because \Drupal\views\Plugin\views\display\DisplayPluginBase::buildRenderable() only bubbles the context cachablity metadata when it has been asked to create a render cache for the output.
This can lead to problems where empty view blocks get cached.
Steps to reproduce
This started out as a long adventure into why empty view blocks get cached, so here are some steps to reproduce:
Setup
(note that I exported and attached my test site's configuration in #2 — this could save you time)
- Install Drupal 8.0.x using the minimal install profile.
- Enable
field_ui,views,views_uimodules. - Go to
/admin/structure/types/add, add a newcontent type:- Name =
Test - Publishing options:
- Promoted to front page =
FALSE
- Promoted to front page =
- (leave the rest of the fields at their default value)
- Name =
- Go to
admin/structure/types/manage/test/displayand disable the body field (i.e.: set it's Format =- Hidden -). - Go to
/node/add/test, add a first new piece of content:- Title =
First test - Body =
Lorem ipsum - (leave the rest of the fields at their default value)
- Title =
- Go to
/node/add/test, add a second new piece of content:- Title =
Second test - Body =
Dolor sit - (leave the rest of the fields at their default value)
- Title =
- Go to
/admin/structure/views/add, add a new view:- View basic information:
- View name = "Tests"
- (leave the rest of the fields at their defaultvalue)
- View settings:
- Show
Contentof typeTestsorted byUnsorted
- Show
- Page settings
- (leave the rest of the fields at their default value)
- Block settings
- Create a block =
TRUE - Block title =
Tests - Block display settings:
- Display format:
Unformatted listofFields
- Display format:
- Create a block =
- (leave the rest of the fields at their default value)
- Click
Save and edit - Under Fields, remove the default
Content: Titlefield, and add aContent: Bodyfield (the default settings are fine). - Under Advanced, add a Contextual Filter. Select
Node ID, and click
Apply (all displays):- When the filter value is NOT available:
-
Default actions:
Provide default value:- Type =
Content ID from URL
- Type =
-
Default actions:
- (leave the rest of the fields at their default value)
- When the filter value is NOT available:
- Save the view.
- View basic information:
- Go to
admin/structure/block- In Footer, click
Place block. Find theTestsblock from the categoryLists (Views), and clickPlace block. Save using the default settings. - Click
Save blocks
- In Footer, click
Test 1
- Clear all caches:
drush -y cr - Go to
/node. - Go to
/node/1.- Expcected behaviour: A block is displayed in the footer with the contents of the body field ("Lorem ipsum").
- Actual behaviour: No block is displayed.
- Go to
/node/2.- Expcected behaviour: A block is displayed in the footer with the contents of the body field ("Dolor sit").
- Actual behaviour: No block is displayed.
Test 2
- Clear all caches:
drush -y cr - Go to
/node/1. Note the block is displayed in the footer with the contents of the body field ("Lorem ipsum"). - Go to
/node/2. Note the block is displayed in the footer with the contents of the body field ("Dolor sit").
Test 3
- In
settings[.local].php, disable the render cache by adding the following lines:
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml'; $settings['cache']['bins']['render'] = 'cache.backend.null';
- Clear all caches:
drush -y cr - Go to
/node. - Go to
/node/1. Note the block is displayed in the footer with the contents of the body field ("Lorem ipsum"). - Go to
/node/2. Note the block is displayed in the footer with the contents of the body field ("Dolor sit").
Test 4
- Remove the lines from
settings[.local].phpyou added in step 3, if applicable. Re-run Test 1 to confirm the bug occurs. - Apply the following patch:
diff --git a/core/modules/views/src/Plugin/Block/ViewsBlock.php b/core/modules/views/src/Plugin/Block/ViewsBlock.php index 20dbace..530d9e3 100644 --- a/core/modules/views/src/Plugin/Block/ViewsBlock.php +++ b/core/modules/views/src/Plugin/Block/ViewsBlock.php @@ -127,4 +127,15 @@ public function getMachineNameSuggestion() { return 'views_block__' . $this->view->storage->id() . '_' . $this->view->current_display; } + /** + * {@inheritdoc} + */ + public function getCacheContexts() { + $contexts = parent::getCacheContexts(); + + $contexts[] = 'url'; + + return $contexts; + } + } - Clear all caches:
drush -y cr - Go to
/node. - Go to
/node/1. Note the block is displayed in the footer with the contents of the body field ("Lorem ipsum"). - Go to
/node/2. Note the block is displayed in the footer with the contents of the body field ("Dolor sit"). Figure out what's causing empty blocks to be cached.Write a patch- Review and feedback
- RTBC.
- Commit.
Proposed resolution
Always pass the cachablity metadata; but if we're not supposed to cache the view's output, remove the cache tags, leaving the rest of the metadata to bubble.
Remaining tasks
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #34 | interdiff.txt | 484 bytes | dawehner |
| #34 | 2633308-34.patch | 10.87 KB | dawehner |
| #29 | interdiff.txt | 1 KB | dawehner |
| #29 | 2633308-29.patch | 10.86 KB | dawehner |
| #25 | 2633308-24-fail.patch | 7.98 KB | dawehner |
Comments
Comment #2
mparker17Here is an export of my test site's config, for fast debugging.
Comment #3
mparker17Tagging as related to the render cache. I'm pretty sure this worked in D7 / Views 7.x-3.x so I'm tagging as a regression also.
Comment #4
mparker17I should note the config export in the setup section of the steps to reproduce
Comment #5
mparker17Comment #6
mparker17Ok, so after a bit of debugging and chatting with @dawehner, it looks like \Drupal\views\Plugin\views\display\DisplayPluginBase::buildRenderable() only includes the context cachablity metadata when it has been asked to create a render cache for the output, which isn't correct.
Instead, we always want to pass the cachablity metadata; but if we're not supposed to cache the view's output, remove the cache tags, leaving the rest of the metadata to bubble.
Comment #7
dawehnerNo we should really have some form of test coverage, just to ensure this will never break again.
Comment #8
fabianx commentedThis is major, great catch!
Comment #9
fabianx commentedI would probably do it after the if check.
Comment #10
mparker17@Fabianx: I initially did it after the
ifcheck, butDisplayPluginBase::applyDisplayCachablityMetadata()copies over cache tags as well, meaning it puts back the cache tags after theelsepart removes them.Comment #11
fabianx commented#10: Cache tags should not be removed - that we need to discuss separately however.
If the view is cached in dynamic page cache, why would we not want to expire it via its tags?
Comment #12
mparker17Here is a patch which adds a test to compare the cache contexts with and without render caching. Dunno if this is the best way to test this, but trying to manually add a cache context (
$view->addCacheContext('views_test_cache_context')) didn't work — manually-added cache contexts showed up in the output with and without caching enabled.Feedback welcome.
Comment #13
mparker17... and apparently I attached a half-finished patch... let's try that again.
Comment #14
sagesolutions commentedHi mparker17,
I had the exact same issue which was driving me bananas. I used updated my site with views_cache_contexts-2633308-13.patch and it fixed my issue.
Thanks so much!
Mike
Comment #15
dawehnerThe patch indeed fixes the issue already. One thing I'm wondering is, should we have some kind of regression test coverage for the empty block problem, so the disappearing block?
Yes, the cache contexts are there, but it seems to be part of a detail for me, so having a test coverage which basically does exactly what is described in the issue summary would be neat and super helpful IMHO
Comment #16
alayham commentedTested the patch and resolved a complex problem I reported in #2647818: Views blocks with taxonomy arguments disappear for no reason
Comment #17
wim leersLooks sensible.
Comment #18
kclarkson commentedI am confirming that the attached patch has fixed my issue from #2650338: Contextual Views Blocks Disappearing
+1 RTBC
Comment #20
dawehnerBack to RTBC
Comment #21
gunwald commentedSeems to solve my issue from https://www.drupal.org/node/2655522 too, I am happy again.
Comment #22
alexpottIN #15 @dawehner suggested some integration tests based on the excellent steps to reproduce in the issue summary. Given the number of related issues I think this is worth it.
Comment #23
dawehnerI'm looking into writing the test coverage.
Comment #24
dawehnerComment #25
dawehnerHere is a patch which will fail
Comment #27
dawehnerBack to needs review, failures are as expected
Comment #28
wim leersThis seems unrelated? Perhaps even unwanted?
s/cachability/cacheability/
This test makes sense. Simple & clear.
I'm not entirely sure what this test is testing?
Comment #29
dawehnerThis view is the first one in core which uses
views.argument_default.nodein config.Now something fun exists in core. This is defined twice: once in node.views.schema.yml and once in user.views.schema.yml, which overrides the first one, with a wrong schema definition,
which makes it impossible to write a passing test.
Well, the bug of this issue. I hope its fine to do that :)
There we go.
Comment #30
tannerjfco commentedWe were experiencing this issue and applied the patch in #29. We have been running this in production and the issue seems to be resolved. Since it seems to fix the original issue and we have passing tests, I'm gonna go ahead and RTBC. If there's remaining work feel free to update accordingly.
Comment #31
alayham commentedPatch #29 works well.
Looking forward to remove it from my patch file.
Comment #32
wim leers#29 Thanks for the explanation!
My only concern there is that both the test name and the description are very generic. I think it'd be helpful for our future selves if it'd describe more explicitly what it's testing. But it's only a nit, so leaving at RTBC.
Comment #33
alexpottLet's get a follow up to add a specific test for this then. Nice to have it fixed though.
I don't mind about the class name but the test description could be a bit better. I left pondering what an actual site means in this context. How about we change this to
Tests render caching of blocks provided by views.Comment #34
dawehnerHere is the followup: #2660446: Test the node argument_default plugin
Comment #35
wim leersComment #36
alexpottCommitted cf2c7a4 and pushed to 8.0.x and 8.1.x. Thanks!