Problem/Motivation

See #2430341: Comment pager-dependent cache key should be a cache context for a sister issue and #2429287: [meta] Finalize the cache contexts API & DX/usage, enable a leap forward in performance for the overarching meta.

For prior discussion, please read everything in #2433599: Ensure every (non-views) pager automatically associates a matching cache context!

Blocks #2381277: Make Views use render caching and remove Views' own "output caching"

Proposed resolution

Views using pagers should specify a cache context.

Remaining tasks

Contributor tasks needed
Task Novice task? Contributor instructions Complete?
Review patch to ensure that it fixes the issue, stays within scope, is properly documented, and follows coding standards Instructions

User interface changes

No.

API changes

Extra optional argument preserves previous behavior, so no BC break.
- public function setCurrentPage($page) {}
+ public function setCurrentPage($page, $keep_cacheability = FALSE) {}

in setCurrentPage() setItemsPerPage() setOffset()

Comments

wim leers’s picture

Status: Active » Closed (duplicate)
fabianx’s picture

Status: Closed (duplicate) » Active

Re-opening to take the views parts into account.

Views has a setPager method and we need to deal with that somehow in regards to cache context.

We'll find a solution :).

wim leers’s picture

Issue summary: View changes
wim leers’s picture

Title: Views using pagers should specify a cache context » [PP-1] Views using pagers should specify a cache context
Status: Active » Postponed
Related issues: +#2433599: Ensure every (non-views) pager automatically associates a matching cache context

Let's postpone this on #2433599: Ensure every (non-views) pager automatically associates a matching cache context.

We can discuss stuff here already, but patches are postponed on that issue.

wim leers’s picture

Title: [PP-1] Views using pagers should specify a cache context » Views using pagers should specify a cache context
Status: Postponed » Active
dawehner’s picture

StatusFileSize
new1.09 KB

Is this all we need?

wim leers’s picture

Is that patch trying to say "use the regular pager cache context, but enforce the Views pager's 'current page'"?

dawehner’s picture

Is that patch trying to say "use the regular pager cache context, but enforce the Views pager's 'current page'"?

Yeah, would that make sense?

wim leers’s picture

That makes sense conceptually, yes. But we need the "Views' pager 'current page'" to be derived from some request context. i.e. the pager cache context is derived from the URL's query arguments. We need something similar for "Views' pager 'current page'". If we don't have that, then there is no way to calculate the current page without executing the entire view…
So, what is the shortest path to calculating the "Views' pager 'current page'"?

dawehner’s picture

Let's give another example.
Random code in your template preprocess:

$view = Views::getView($name);
$view->setItemsPerPage(12);
$view->setCurrentPage(2);
$vars['foo'] = $view->preview();

There is a world beyond pages :)

fabianx’s picture

In that case the template code needs to specify manually what it wants this to be varied on in my opinion.

Does $view->preview() show a pager, btw.?

dawehner’s picture

Yes it does.

dawehner’s picture

In that case the template code needs to specify manually what it wants this to be varied on in my opinion.

Well, I still think that if we could do something about it, we should do, because otherwise there will be just tons of bugs by people all over the place.

fabianx’s picture

I agree and it is not forgotten.

So what should we do?

Lets work on some test cases?

Or we could use the 'hard, but correct method' and set max-age==0 when you use setPage() as we cannot determine if you do:

if ($this->moonService->getRandom() == '2') {
  $this->view->setCurrentPage(20);
} else {
  $this->view->setCurrentPage(10);
}

but if you know what it varies on you would need to specify something like this for example:

if ($this->moonService->getPhase() == 'full') {
  $this->view->setCurrentPage(20);
} else {
  $this->view->setCurrentPage(10);
}
$this->view->setMaxAge(Cache::PERMANENT);
$this->view->addCacheContexts(['moon']);

What do you think?

andypost’s picture

I'd like to point that view can render nodes with comments but comment formatter provides own "dumb" setting for pager.
Was done in #2155387: Multiple comment fields per entity are supported, but break horribly when they need pagers and #2430341: Comment pager-dependent cache key should be a cache context

So you can easily get in situation (probably in tests) where the same cache context would be exposed from comment field and view itself.

dawehner’s picture

Status: Active » Needs review
StatusFileSize
new2.29 KB

Let's see ...

The idea is: once you have those methods, you opt out of caching, but if you want, you can opt in and provide your corresponding cache contexts.

plach’s picture

The last patch makes sense to me, but in the case of regular page displays I guess we could keep caching enabled and specify the regular pager contexts.

wim leers’s picture

StatusFileSize
new2.78 KB

So, in reviewing this, I noticed there is one non-test call to ::setCurrentPage(): in CachePluginBase, where data is being retrieved from the results cache. While that should have caused any view using the results cache to have a max-age of zero, it did not. The reason for that turned out to be that \Drupal\views\ViewExecutable::attachDisplays() clones itself, and the clone's element property is then used, thus causing the max-age zero on the original (uncloned) view executable to be ignored.

In other words: this particular thing is only resulting in the correct end result (non-zero max age) despite not using the $keep_cacheability = TRUE ability thanks to a bug deeper in Views…

Thanks to @plach for the help while debugging.

@plach suggested syncing the element properties across clones.

plach’s picture

So we need to ensure CachePluginBase does not make things uncacheable when retrieving results.

Status: Needs review » Needs work

The last submitted patch, 18: 2433591-18.patch, failed testing.

plach’s picture

Status: Needs work » Needs review
StatusFileSize
new3.82 KB

Spoke with Daniel: #18 is the wrong fix. This addresses #19, I'll test this a bit more to see what I can do to fix #18.

No interdiff as I touched almost every line.

Status: Needs review » Needs work

The last submitted patch, 21: views-cache_exposed_sort-2487099-21.patch, failed testing.

plach’s picture

Status: Needs work » Needs review
StatusFileSize
new3.53 KB

OMG, I actually posted the interdiff instead...

plach’s picture

StatusFileSize
new2.06 KB
new5.59 KB

This makes pagers actually work with cached full views. Still investigating Wim's issue.

Status: Needs review » Needs work

The last submitted patch, 24: views-cache_pagers-2433591-24.patch, failed testing.

plach’s picture

Status: Needs work » Needs review
StatusFileSize
new1.53 KB
new7.35 KB

Fixed failures

Status: Needs review » Needs work

The last submitted patch, 26: views-cache_pagers-2433591-26.patch, failed testing.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 26: views-cache_pagers-2433591-26.patch, failed testing.

dawehner’s picture

+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -2323,6 +2323,7 @@ public function buildRenderable(array $args = []) {
+        'keys' => ['view', $this->view->storage->id(), $this->display['id']],

We should ensure to not include that as part of the patch, right?

plach’s picture

Status: Needs work » Needs review
StatusFileSize
new1.73 KB
new5.71 KB

Meh, too many branches

dawehner’s picture

+++ b/core/modules/views/src/Tests/Handler/FieldWebTest.php
@@ -182,7 +182,7 @@ protected function xpathContent($content, $xpath, array $arguments = array()) {
+  public function atestAlterUrl() {

@@ -329,7 +329,7 @@ public function testAlterUrl() {
+  public function atestFieldClasses() {

@@ -438,7 +438,7 @@ public function testFieldClasses() {
+  public function atestTextRendering() {

Indeed

plach’s picture

StatusFileSize
new583 bytes

Correct interdiff

Status: Needs review » Needs work

The last submitted patch, 31: views-cache_pagers-2433591-31.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new7.1 KB
new1.39 KB

Added the needed expected cache contexts.

plach’s picture

Status: Needs review » Needs work

I think this should be ready to go. I investigated Wim's issue but I cannot reproduce it: when result cache is enabled (and $this->view->setCurrentPage($cache->data['current_page'], TRUE) is changed to $this->view->setCurrentPage($cache->data['current_page'])) max age is 0 and the view is not cached as expected.

plach’s picture

Status: Needs work » Needs review

Unintended status change

plach’s picture

Issue tags: +VDC
dawehner’s picture

StatusFileSize
new9.4 KB

There we go, added test coverage for it.

dawehner’s picture

StatusFileSize
new5.29 KB
new9.4 KB

Here is the test which will fail as well.

dawehner’s picture

StatusFileSize
new2.31 KB

And the interdiff

The last submitted patch, 40: 2433591-fail.patch, failed testing.

plach’s picture

+++ b/core/modules/views/src/Tests/Plugin/PagerKernelTest.php
@@ -0,0 +1,74 @@
+  protected function setUp($import_test_views = TRUE) {

Fixed missing PHP doc, aside from that looks great to me.

dawehner’s picture

Fixed missing PHP doc, aside from that looks great to me.

+1

@wim
Do you mind reviewing stuff again?

catch’s picture

Priority: Major » Critical
catch’s picture

+++ b/core/modules/views/src/ViewExecutable.php
@@ -491,10 +491,20 @@ public function setArguments(array $args) {
+   *   The current page.
+   * @param bool $keep_cacheability
+   *   (optional) Keep the cacheability, by default we mark the issue as not
+   *   cacheable. Defaults to FALSE.
    */
-  public function setCurrentPage($page) {

Should this explain what to do if you set $keep_cacheability to TRUE?

Also 'mark the issue' - should it be 'mark the view'? - this is repeated each time the doc is added.

yesct’s picture

dawehner’s picture

StatusFileSize
new9.84 KB
new2.08 KB

Also 'mark the issue' - should it be 'mark the view'? - this is repeated each time the doc is added.

Ha!

Explained a bit, why we need this parameter and why its FALSE by default, feel free to improve it.

yesct’s picture

StatusFileSize
new2.33 KB
new9.84 KB

just some grammar changes I noticed in the last interdiff.

Next I'll read the whole thing.

The last submitted patch, 48: 2433591-48.patch, failed testing.

yesct’s picture

Status: Needs review » Needs work
+++ b/core/modules/views/src/Tests/Plugin/PagerKernelTest.php
@@ -0,0 +1,77 @@
+      \Drupal::service('renderer')->renderPlain($output);
+      $this->assertIdentical(0, $output['#cache']['max-age'], 'Max age set to 0 without $keep_cacheablity.');
...
+      \Drupal::service('renderer')->renderPlain($output);
+      $this->assertIdentical(CacheBackendInterface::CACHE_PERMANENT, $output['#cache']['max-age'], 'Max age kept on -1 with $keep_cacheablity.');

+++ b/core/modules/views/src/ViewExecutable.php
@@ -491,10 +491,22 @@ public function setArguments(array $args) {
+    if (!$keep_cacheability) {
+      $this->element['#cache']['max-age'] = 0;
+    }

if it is not cacheable, we override whatever it might be. (daniel says we dont know what it is yet, and could be set in a views setting.)

In the test, we test for 0 (not cacheable) and -1 (permanent), but seems we do not have a test that tests the value from the setting actually bubbles up.

Maybe we should add a test for that?

------

Everything else looked ok to me. (read the whole patch)

Does not seem to need manual testing or anything, because the test is checking the header to see if the context for the page is added.
#2381277: Make Views use render caching and remove Views' own "output caching" will be the issue that will use that context in the header to actually use the context.

The last submitted patch, 49: 2433591-49.patch, failed testing.

yesct’s picture

Value array ( 0 => 'languages:language_interface', 1 => 'theme', 2 => 'url.query_args.pagers:0', 3 => 'url.query_args:order', 4 => 'url.query_args:sort', ) is identical to value array ( 0 => 'languages:language_interface', 1 => 'theme', 2 => 'url.query_args:order', 3 => 'url.query_args:sort', ). Other FieldWebTest.php 81 Drupal\views\Tests\Handler\FieldWebTest->testClickSorting()

fail is from #2489966: The Views table style plugin does not specify cache contexts for click sorting which added a test that went in today. I'll fix it now.

yesct’s picture

Status: Needs work » Needs review
StatusFileSize
new514 bytes
new10.34 KB

just added the new pager context to that test.
test passes locally.

yesct’s picture

Issue summary: View changes

Thinking more, the test I asked about in #51 is probably not worth delaying this critical. So I opened a normal task to add the test (and discuss if we need it). #2490852: Add a test for views pager cache age setting

yesct’s picture

Issue summary: View changes

updated the issue summary.

I'm guessing we do not need a change record for this. #2381277: Make Views use render caching and remove Views' own "output caching" probably doesn't need one either...

fabianx’s picture

Status: Needs review » Reviewed & tested by the community

RTBC, Let's get this in.

Test coverage looks good, we know that bubbling works from other tests, too.

The solution to explicitly opt-in to keep the current cacheability looks great!

yesct’s picture

StatusFileSize
new480.14 KB

@tim.plunkett and I were trying to see the page cache tag in the browser... and we haven't figured out how to see it on admin/content (yet)

drupal-cache-contexts

yesct’s picture

StatusFileSize
new591.87 KB

I can see it on a view I made.

tim.plunkett’s picture

Reading through \Drupal\Core\Cache\CacheContextsManager::optimizeTokens() @YesCT and I finally figured out why we couldn't see url.query_args.pagers:0, because url is already included and is an "ancestor", so the more specific context is not needed.

RBTC +1

dawehner’s picture

Thank you for the final push @yesct!
Its amazing how things work together at the end.

wim leers’s picture

Status: Reviewed & tested by the community » Needs work

Ugh, I wrote this before #47, but didn't notice I'd cross-posted which caused my comment not to actually be posted :/


I think this should be ready to go. I investigated Wim's issue but I cannot reproduce it: when result cache is enabled (and $this->view->setCurrentPage($cache->data['current_page'], TRUE) is changed to $this->view->setCurrentPage($cache->data['current_page'])) max age is 0 and the view is not cached as expected.

I can still reproduce this. Using the front page view, with tag-based caching enabled. With or without the cited TRUE, the front page gets an output cache item.


Due to the above, also feeling less bad for pointing out nitpicks.

  1. +++ b/core/modules/views/src/Tests/Plugin/PagerKernelTest.php
    @@ -0,0 +1,77 @@
    + * Tests pager related APIs.
    ...
    +   * Tests pager related setter methods on ViewExecutable.
    

    Nitty nit: s/pager related/pager-related/

  2. +++ b/core/modules/views/src/Tests/Plugin/PagerKernelTest.php
    @@ -0,0 +1,77 @@
    +      $this->assertIdentical(CacheBackendInterface::CACHE_PERMANENT, $output['#cache']['max-age'], 'Max age kept on -1 with $keep_cacheablity.');
    

    Nit: s/kept on -1/unchanged/

  • catch committed 87c6d77 on 8.0.x
    Issue #2433591 by dawehner, plach, YesCT, Wim Leers: Views using pagers...
catch’s picture

Status: Needs work » Fixed

Thanks for the additional docs.

Committed/pushed to 8.0.x, thanks!

wim leers’s picture

Status: Fixed » Active

Hrm, catch was probably already committing while I posted #62. Moving back to "active" to see if others can confirm the problem I found. Not sure if we want a revert here, or fix that in a follow-up.

catch’s picture

Status: Active » Needs work

Yes that was an awful cross-post.

I've gone ahead and reverted for #62, back to CNW.

  • catch committed b0e1b80 on 8.0.x
    Revert "Issue #2433591 by dawehner, plach, YesCT, Wim Leers: Views using...
dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new10.34 KB
new1.17 KB

I can still reproduce this. Using the front page view, with tag-based caching enabled. With or without the cited TRUE, the front page gets an output cache item.

So the corresponding cache contexts don't appear in the HTTP response?
I try to understand what you actually expect as part of this issue. Output caching doesn't use render caching ... but output caching relies on $view->result which itself depends on the dynamic pager data:

      $key_data['pager'] = [
        'page' => $this->view->getCurrentPage(),
        'items_per_page' => $this->view->getItemsPerPage(),
        'offset' => $this->view->getOffset(),
      ];

aka. we don't need such workarounds for output caching in its current form. Plach and I talked about getting rid of output caching though, but for sure, this is 100% out of scope of this issue.
I don't consider this as a blocker of the issue itself.

Fixed the nits ...

fabianx’s picture

Assigned: Unassigned » wim leers

Assigning to Wim to provide:

- Steps to reproduce

Aki Tendo’s picture

Looked over the code and ran all the tests locally under PHP 5.5 with no issues. RTBC?

ashrafzadeh queued 35: 2433591-35.patch for re-testing.

ashrafzadeh queued 68: 2433591-68.patch for re-testing.

The last submitted patch, 35: 2433591-35.patch, failed testing.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

My apologies, I was indeed setting render caching expectations, but this is Views' output cache, which behaves differently.

fabianx’s picture

Yes, lets create a follow-up to remove the output cache - as its broken currently anyway due to no bubbling support.

We could also do it as part of the parent: "Make views use render-caching issue" though.

wim leers’s picture

We could also do it as part of the parent: "Make views use render-caching issue" though.

That's how I always imagined we'd do it.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.0.x, thanks!

  • catch committed ae4a224 on 8.0.x
    Issue #2433591 by dawehner, plach, YesCT, Wim Leers: Views using pagers...
plach’s picture

Removing output caching will happen in #2381277: Make Views use render caching and remove Views' own "output caching", see the proposed solution.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.