Problem/Motivation

Add a custom block to your site. View a page with it. Then look at your {cache_render} table. You'll see an entry for the block (entity_view:block:BLOCK_ID:...) that does not include roles in that cache ID, even though that's a context specified in EntityViewBuilder::getBuildDefaults() as something that should apply to 'block_content' entities. The problem is that BlockContent specifies render_cache = FALSE in order to not cache the content entity, since the block itself is cached, but EntityViewBuilder::getBuildDefaults() only adds the 'contexts' if the entity itself is render cacheable, which is wrong, because contexts need to bubble up to parent elements, so should be added regardless, just like tags.

Proposed resolution

  1. Fix EntityViewBuilder to add 'contexts' outside of the isRenderCacheable() if statement, just like 'tags'.

Remaining tasks

Commit.

User interface changes

API changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

effulgentsia’s picture

Per the issue summary, I'm not sure whether to postpone this on #2099137: Entity/field access and node grants not taken into account with core cache contexts or whether to implement getRequiredCacheContexts() as returning the same contexts currently hard-coded in EntityViewBuilder::getBuildDefaults(). Thoughts?

jibran’s picture

Component: block.module » block_content.module

BlockContentBlock belongs to block_content module.

effulgentsia’s picture

Issue tags: +Triaged D8 critical

Confirmed with the branch maintainers that this is indeed critical.

larowlan’s picture

is this in fact active? or postponed on #2099137: Entity/field access and node grants not taken into account with core cache contexts
if it is active, I should be able to look at it on the weekend

effulgentsia’s picture

I think it makes sense to proceed with the "or" part of #1, since that can be done quickly (and a test added), and then improve the implementation of that method as part of #2099137: Entity/field access and node grants not taken into account with core cache contexts.

Perhaps a bonus that could be done here though is instead of duplicating the contexts that are hard-coded in getBuildDefaults(), move that out into a method somewhere that both EntityViewBuilder and BlockContentBlock have access to. Unless we're opposed to introducing a public method that might go away once the other issue is done.

effulgentsia’s picture

Hm, how about just adding a public EntityViewBuilder::getCacheContexts() method with the same signature as getBuildDefaults() has? I think we'll be able to then keep that API working in #2099137: Entity/field access and node grants not taken into account with core cache contexts, just improve its implementation over there. I might be wrong about that, but that's my best guess at this time.

larowlan’s picture

Issue summary: View changes
FileSize
60.01 KB

Are we sure this is still an issue?
Following steps to reproduce in IS I did the following:

Created a block content block
Placed it in sidebar
Viewed the homepage
Selected distinct cid from cache_render

Here's what I get

The entity_view:block:block_id (fooey in this case) has both timezone and role in it.

Screenshot:

larowlan’s picture

Status: Active » Needs review
FileSize
3.28 KB

Doh, I had my patch applied - which is attached.

So just needs a test here.

Status: Needs review » Needs work

The last submitted patch, 8: block-content-block-cache-2396333.fix_.patch, failed testing.

larowlan’s picture

Status: Needs work » Needs review
FileSize
1.32 KB
4.51 KB

Here's a failing test

The last submitted patch, 10: block-content-block-cache-2396333.fail_.patch, failed testing.

jibran’s picture

I think this covers #5. We should update proposed resolution in IS with current tasks.

larowlan’s picture

Issue summary: View changes

Updated IS

Wim Leers’s picture

Status: Needs review » Needs work

Quick review.

  1. +++ b/core/modules/block_content/src/BlockContentViewBuilder.php
    @@ -60,4 +62,33 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityView
    +   * @return array
    
    +++ b/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php
    @@ -37,6 +37,13 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
    +   * @var array.
    
    @@ -153,4 +160,30 @@ public function build() {
    +   * @return array
    

    string[]

  2. +++ b/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php
    @@ -37,6 +37,13 @@ class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInter
    +   * Cache contexts. Array with 'tags' and 'keys' keys.
    

    If it's an array with those keys, it's not just cache contexts. I think this comment is wrong?

  3. +++ b/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php
    @@ -153,4 +160,30 @@ public function build() {
    +    return parent::getRequiredCacheContexts() + $this->cacheContext();
    

    Must be array_merge(), since keys are numeric.

  4. +++ b/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php
    @@ -153,4 +160,30 @@ public function build() {
    +   * Generate the cache context for the block content entity.
    ...
    +  protected function cacheContext() {
    ...
    +    return $this->blockCacheContext;
    

    Must all be plural.

larowlan’s picture

Status: Needs work » Needs review
Issue tags: -Needs issue summary update
FileSize
2.04 KB
4.51 KB

Thanks @WimLeers, all great catches, you can see I changed my mind over time, but didn't go back to rectify.

#14

Status: Needs review » Needs work

The last submitted patch, 15: block-content-block-cache-2396333.15.patch, failed testing.

larowlan’s picture

Status: Needs work » Needs review
FileSize
628 bytes
4.51 KB

doh

Status: Needs review » Needs work

The last submitted patch, 17: block-content-block-cache-2396333.17.patch, failed testing.

Wim Leers’s picture

  1. +++ b/core/modules/block_content/src/BlockContentViewBuilder.php
    @@ -60,4 +62,33 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityView
    +  public function getCacheContexts(EntityInterface $entity, $view_mode, $langcode) {
    

    Should have an @see to \Drupal\Core\Entity\EntityViewBuilder::getBuildDefaults().

    Better yet, I'd argue that the default cache contexts embedded in that method should actually be moved into a \Drupal\Core\Entity\EntityViewBuilder::getCacheContexts() method, so that each entity type's builder can easily override it (that's not an API change!), so that BlockContentBlock can then call that.

    Then there is no duplication of logic (and duplication of the list of cache contexts to use), which always comes with that unavoidable brittleness.

    You can then still create a public getBlockCacheContexts(), which would just return whatever the (inherited) protected getCacheContexts() returns, to allow BlockContentBlock to use it.

  2. +++ b/core/modules/block_content/src/Tests/BlockContentFieldTest.php
    @@ -103,6 +109,19 @@ public function testBlockFields() {
    +    $cache_records = db_select('cache_render', 'cr')
    +      ->fields('cr')
    +      ->condition('cid', db_like('entity_view:block:' . $instance['id']) . '%', 'LIKE')
    +      ->execute();
    +    $found_records = FALSE;
    +
    +    foreach ($cache_records as $record) {
    +      $this->assertTrue(strpos($record->cid, 'r.authenticated') !== FALSE, 'Found cache key for role');
    +      $this->assertTrue(strpos($record->cid, date_default_timezone_get()) !== FALSE, 'Found cache key for timezone');
    +      $found_records = TRUE;
    +    }
    +    $this->assertTrue($found_records, 'Found cache records');
    

    All of this can be simplified: it can be turned into a check that checks whether a render cache item with the expected cache ID exists. See \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::verifyRenderCache() and its callers for examples.

larowlan’s picture

fixes warnings, not started on #19 yet

larowlan’s picture

Status: Needs work » Needs review
larowlan’s picture

19.1 I think that is what #2099137: Entity/field access and node grants not taken into account with core cache contexts is for - @effulgentsia said we could move ahead here provided the solution was compatible with the approach there. I can tackle it here instead - please advise?

19.2 - done

Status: Needs review » Needs work

The last submitted patch, 22: block-content-block-cache-2396333.22.patch, failed testing.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 22: block-content-block-cache-2396333.22.patch, failed testing.

larowlan’s picture

Status: Needs work » Needs review
FileSize
1.21 KB
4.79 KB

passes locally, so figure trying something different

larowlan’s picture

@WimLeers or @effulgentsia can you advise if I should be tackling 19.1 here or in the related issue

Status: Needs review » Needs work

The last submitted patch, 26: block-content-block-cache-2396333.26.patch, failed testing.

larowlan’s picture

Status: Needs work » Needs review
FileSize
1.27 KB
4.8 KB

larowlan--

Status: Needs review » Needs work

The last submitted patch, 29: block-content-block-cache-2396333.28.patch, failed testing.

larowlan’s picture

right, so I'm out of ideas - this passes locally

webchick’s picture

Issue tags: +Performance

Sorry, just doing some house-cleaning. Since D8 cacheability is a subset of Performance, adding the more common tag as well.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
856 bytes
4.81 KB

@larowlan this passes locally for you because you're in Australia/Sydney, but I'm in America/Los_Angeles, and the testbots probably are too.

larowlan’s picture

Thanks Tim
/me feels like a nuffy for not picking that up

webchick’s picture

Urban Dictionary: nuffy
www.urbandictionary.com/define.php?term=nuffy
If there was a bright center of the universe where intelligence, grace and success dwelled, Nuffy would be the individual that would be farthest from.

Thanks for the vocabulary lesson. :D

Wim Leers’s picture

Title: BlockContentBlock ignores cache contexts required by the block_content entity » [PP-1] BlockContentBlock ignores cache contexts required by the block_content entity
Status: Needs review » Postponed

We're now solving this generically at #2429257: Bubble cache contexts.

Let's revisit this once that has landed, just to make sure.

Wim Leers’s picture

Title: [PP-1] BlockContentBlock ignores cache contexts required by the block_content entity » BlockContentBlock ignores cache contexts required by the block_content entity
Status: Postponed » Needs work
Parent issue: » #2429287: [meta] Finalize the cache contexts API & DX/usage, enable a leap forward in performance
Related issues: +#2429287: [meta] Finalize the cache contexts API & DX/usage, enable a leap forward in performance
Wim Leers’s picture

Wanted to do a straight reroll of the patch, but just remove all the hunks that fixed the problem, to see if #2429257: Bubble cache contexts on its own fixed this.

But #2428633: Remove unnecessary BlockContentFieldTest removed BlockContentFieldTest. Not sure what the new best place for a test is.

I believe larowlan wants to take a shot at this.

Berdir’s picture

I'd just bring the test back, possibly as a simplified and renamed version, we don't need the configurable field stuff and so on anymore I think.

larowlan’s picture

Assigned: Unassigned » larowlan

yep that's my plan, hoping to work on this in transit to DrupalSouth (in 7 hrs or so)

effulgentsia’s picture

Manually tested HEAD, and found it still fails, but differently than the issue summary says:

Add a custom block to your site. View a page with it. Then look at your {cache_render} table. You'll see an entry for the block (entity_view:block:BLOCK_ID:...) that does not include roles or timezone in that cache key, and an entry for the block_content (entity_view:block_content:BLOCK_CONTENT_ID:...) that does.

That latter entry no longer exists due to #2284917: In-place editing of custom blocks broken *again* (because attributes and contextual links of custom blocks are lost).

But, EntityViewBuilder::getBuildDefaults() only adds contexts if isRenderCacheable() is true, which seems wrong, since even if it's not cacheable on its own, we still need the contexts to bubble up. Leaving this issue critical for that reason.

Additionally, even if this issue can be fixed by making the bubbling work, I think this is a case where we should still do something similar to what #33 does and get that info into the block without relying on bubbling, because:

  1. Performance: per #2429257-54: Bubble cache contexts, it's a cheap way to optimize away the need for a 2nd cache get.
  2. When configuring the block, the "Cache settings" UI shows unchecked checkboxes for contexts that the block itself doesn't identify as required, so it's best for UX if we can make it know about the ones that are. (Side note: this UI might need an overhaul anyway, since what are we actually trying to achieve with it now that we have bubbling?)

However, neither of the above points justify a "critical" priority, so if we want to limit the scope of this issue to just solving the part that is and punt the rest to a non-critical follow-up, that's fine too.

effulgentsia’s picture

Side note: this UI might need an overhaul anyway, since what are we actually trying to achieve with it now that we have bubbling?

Apparently, we already have an issue for this: #2428805: Remove the ability to configure a block's cache contexts

Wim Leers’s picture

I don't think you wanted to link to #2284917: In-place editing of custom blocks broken *again* (because attributes and contextual links of custom blocks are lost)?

But, EntityViewBuilder::getBuildDefaults() only adds contexts if isRenderCacheable() is true, which seems wrong, since even if it's not cacheable on its own, we still need the contexts to bubble up.

I noticed that too.

Wim Leers’s picture

To clarify, that's because we split up #cache[keys] into #cache[keys] + #cache[contexts], but then failed to move #cache[contexts] to the place where #cache[tags] is set, which runs unconditionally. We simply split it up and kept it in the same branch of code.
That's how this happened.

effulgentsia’s picture

I don't think you wanted to link to #2284917: In-place editing of custom blocks broken *again* (because attributes and contextual links of custom blocks are lost)?

I did. That's the issue that added render_cache = FALSE to BlockContent.

Wim Leers’s picture

Ah, that makes sense :)

larowlan’s picture

Status: Needs work » Needs review
FileSize
5.93 KB

so started on test on plane, but getting "cache_context.timezone" is not a valid cache context ID." exception

Status: Needs review » Needs work

The last submitted patch, 47: block-content-block-cache-2396333.29.patch, failed testing.

dawehner’s picture

+++ b/core/modules/block_content/src/BlockContentViewBuilder.php
@@ -60,4 +62,33 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityView
+      'cache_context.timezone',

it should be just 'timezone'. Ran into the same problem earlier.

larowlan’s picture

Right but that's in HEAD?

larowlan’s picture

Status: Needs work » Needs review
FileSize
874 bytes
5.9 KB

Status: Needs review » Needs work

The last submitted patch, 51: block-content-block-cache-2396333.30.patch, failed testing.

effulgentsia’s picture

Status: Needs work » Needs review
FileSize
2.76 KB

Different testing approach. Here's just a test-only to prove it fails on HEAD.

effulgentsia’s picture

+++ b/core/modules/block_content/src/Plugin/Block/BlockContentBlock.php
@@ -153,4 +160,30 @@ public function build() {
+      if ($block = $this->entityManager->loadEntityByUuid('block_content', $uuid)) {
+        $context = $this->entityManager->getViewBuilder($block->getEntityTypeId())->getCacheContexts($block, $this->configuration['view_mode'], $block->language()->getId());
+      }

In #41, I argued that we should do this approach based on a belief that it would be more performant and a concern about the block cache settings UI. However, the latter will be removed in #2428805: Remove the ability to configure a block's cache contexts and looking at the above code, that hardly seems more performant than a cache get from a #cache_redirect. Therefore, here's this alternate fix, as alluded to in #41.

The last submitted patch, 53: block-content-block-cache-2396333-53-test-only.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 54: block-content-block-cache-2396333-54.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
7 KB
3.94 KB
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -169,6 +169,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
+        'contexts' => ['theme', 'user.roles'],

Let's put these on separate lines.

Fixed that, and fixed the test failures.

Fabianx’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for the extensive test coverage.

The problem indeed was the conditional vs. non-conditional execution of the code path.

This is RTBC.

Fabianx’s picture

It would be great to get an IS update so that it matches the patch.

effulgentsia’s picture

Assigned: larowlan » Unassigned
Issue summary: View changes
Issue tags: -Needs issue summary update

Updated summary.

Fabianx’s picture

Thanks, effulgentsia! IS looks great now!

catch’s picture

Status: Reviewed & tested by the community » Fixed

Great how cache context bubbling makes an issue like this straightforward.

Committed/pushed to 8.0.x, thanks!

  • catch committed 5ad41ab on 8.0.x
    Issue #2396333 by larowlan, effulgentsia, Wim Leers, tim.plunkett:...
larowlan’s picture

Thanks all

Status: Fixed » Closed (fixed)

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