Problem/Motivation

Discovered by #2429617: Make D8 2x as fast: Dynamic Page Cache: context-dependent page caching (for *all* users!), postponed on #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed.
This is blocking critical #2429617: Make D8 2x as fast: Dynamic Page Cache: context-dependent page caching (for *all* users!)

Quoting Wim Leers in #2493021: Remove unused & useless services from HtmlRenderer:

that is already happening in BlockAccessControlHandler:

      // This should not be hardcoded to an uncacheable access check result, but
      // in order to fix that, we need condition plugins to return cache contexts,
      // otherwise it will be impossible to determine by which cache contexts the
      // result should be varied.
      // @todo Change this to use $access->cacheUntilEntityChanges($entity) once
      //   https://www.drupal.org/node/2375695 is resolved.
      return $access->setCacheMaxAge(0);

Excellent!

But, then \Drupal\block\BlockRepository::getVisibleBlocksPerRegion() causes a lot of sadness:


    foreach ($this->blockStorage->loadByProperties(array('theme' => $this->getTheme())) as $block_id => $block) {
      /** @var \Drupal\block\BlockInterface $block */
      // Set the contexts on the block before checking access.
      if ($block->setContexts($contexts)->access('view')) {
        $full[$block->getRegion()][$block_id] = $block;
      }
    }

i.e. access results' cacheability metadata for blocks never makes it into the render arrays of blocks. I did some git history debugging; this was just never tested; this max-age = 0 thing originates in the original "access result cacheability metadata" issue, which just updated all code, and didn't add test coverage for every particular use of it (#2287071: Add cacheability metadata to access checks). Because that was out of scope.

Proposed resolution

Fix it.

Remaining tasks

TBD

User interface changes

None.

API changes

TBD

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers’s picture

Fabianx’s picture

FileSize
2.69 KB

Here is a patch that would work, but that I am not happy with overall ...

I think better would be if #access would be set in the BlockViewBuilder() and Renderer supported an object for #access.

Fabianx’s picture

FileSize
2.69 KB
Fabianx’s picture

Status: Active » Needs review

Lets see what testbot says ...

Wim Leers’s picture

I think better would be if #access would be set in the BlockViewBuilder() and Renderer supported an object for #access.

+1

@dawehner was working on making #access support that. Can't find the issue ATM.

Wim Leers’s picture

Status: Needs review » Needs work
+++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
@@ -133,6 +134,15 @@ public function build() {
+        if ($access->isForbidden()) {

Should be !$access->isAllowed().

Wim Leers’s picture

Fabianx’s picture

Status: Needs work » Needs review
FileSize
2.69 KB
tim.plunkett’s picture

+++ b/core/modules/block/src/BlockRepository.php
+++ b/core/modules/block/src/BlockRepository.php
@@ -77,9 +77,8 @@ public function getVisibleBlocksPerRegion(array $contexts) {

getVisibleBlocksPerRegion() is now an inaccurate name, since we return all blocks per region and the caller is responsible for checking access.

Wim Leers’s picture

#9: I had the exact same remark. :)

17:38:19 WimLeers: it makes the "getVisibleBlocksPerRegion" name nonsensical
17:38:43 Fabianx-screen: Well, visibile in terms of displayed in that theme.
17:38:48 WimLeers: fair
tim.plunkett’s picture

This effectively reverts #2367579: Move retrieval of visible blocks to a standalone service, which @chx opened for what I can only assume is Mongo:

HEAD runs a config entity query on block entities and then it filters the results. All this is hardwired into the FullPageVariant. More efficient storages would need to replace FullPageVariant

Wim Leers’s picture

I don't see how, because access checking will always invoke the access checkers, regardless of where block config entities are stored?

Status: Needs review » Needs work

The last submitted patch, 8: block_access_results-2495171-8.patch, failed testing.

Status: Needs work » Needs review
Wim Leers’s picture

Testbot failed: FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] Setup environment: failed to clear checkout directory.. Hence retesting.

Status: Needs review » Needs work

The last submitted patch, 8: block_access_results-2495171-8.patch, failed testing.

chx’s picture

The BlockRepository stuff was meant to significantly reduce the number of blocks that are even possible. A theoretical implementation could read the user from CurrentUserContext, get the roles for that user and run it against visibility.user_role and then remove that visibility rule from the result to avoid a slow PHP check. Of course I have no idea how that would work in actual code because I never actually got around to implement it nor do I know how it fits in the crazy caching world of Drupal 8.

tim.plunkett’s picture

So we can probably continue on with this, just renaming the method and being clear that it does NOT run access checking, and then page_manager/panels will have to update.

chx’s picture

Seems then the only speedup possible will be querying per theme. It's way less useful but I will work with what I can (in theory. if someone picks up mongodb)

Berdir’s picture

@chx: That already (and still with this patch) happens, just inside the patch context. It's a loadByProperties(), but that's just a shortcut for an entity query. An entity query that uses a config entity query index now, so it's really fast (one key value lookup to get the config entity ID's).

YesCT’s picture

Issue summary: View changes
Issue tags: +blocker

The last submitted patch, 3: block_access_results-2495171-3.patch, failed testing.

Status: Needs work » Needs review

The last submitted patch, 3: block_access_results-2495171-3.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 8: block_access_results-2495171-8.patch, failed testing.

lauriii’s picture

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

This should at least fix the fatal error on the tests

Status: Needs review » Needs work

The last submitted patch, 27: block_access_results-2495171-27.patch, failed testing.

Wim Leers’s picture

The remaining failures are likely at least partially broken tests: tests that are assuming a block is render cached with a certain cache ID, but that CID is now different because they now take cache contexts from access results into account.

Berdir’s picture

Assigned: Unassigned » Berdir

Looking at the test fails.

Berdir’s picture

Assigned: Berdir » Unassigned

Uhm. I don't think we can do this without #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed because what it actually means is that block render caching is dead without having fixed the other issue first?

Berdir’s picture

I've verified that at least the BlockCacheTest is green with the patch from #2375695-24: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed. So pretty sure we need that before we can continue here.

Fabianx’s picture

Status: Needs work » Postponed

Postponing on #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed, which I had to make critical.

Berdir is right that issue is a hard blocker for this one.

Fabianx’s picture

Issue summary: View changes
Fabianx’s picture

Title: Block access results' cacheability metadata is not applied to the render arrays » [PP-1] Block access results' cacheability metadata is not applied to the render arrays
Wim Leers’s picture

Title: [PP-1] Block access results' cacheability metadata is not applied to the render arrays » Block access results' cacheability metadata is not applied to the render arrays
Status: Postponed » Needs work

I don't understand why this is blocked on #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed. Note that the title of this issue explicitly mentions Block access results, not Block visibility conditions or Block cacheability in general.

You're absolutely right that #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed is necessary for blocks to be cacheable when using visibility conditions, and that many blocks will effectively not be cacheable if that is missing. But that's a pure performance/cacheability problem. This issue here also presents a security problem, because blocks not intended for one user may be served to another user, because the access results' cacheability metadata never makes it onto the render array.

Back to NW to either still finish this patch independently, or to get clarity on why it is indeed blocked by #2375695.

Berdir’s picture

No, it is not only about visibility conditions.

*every* block is set to max-age 0 even if it doesn't have visibility conditions. As soon as we respect that, then blocks are never cached, because max-age prevents that.

See the mentioned BlockCacheTest test. That is very simple, it just tests if a block is cached at all or not. And with this change, the block is no longer cached.

There is no security issue without smartcache because we don't cache the access results.

Wim Leers’s picture

Title: Block access results' cacheability metadata is not applied to the render arrays » [PP-1] Block access results' cacheability metadata is not applied to the render arrays
Status: Needs work » Postponed

Riiiight. Blocking this again. Thanks for the clarification!

Berdir’s picture

Status: Postponed » Needs review
FileSize
14.48 KB

Combined patch, want to see how many test fails are left then.

Status: Needs review » Needs work

The last submitted patch, 39: block_access_results-conditions-2495171-39.patch, failed testing.

Berdir’s picture

Fixing those test fails to better understand what is needed for test coverage. Quite some changes necessary in unit tests because the place where access checks happen moved. Also again annoyed at static methods calling service methods :)

We could possibly extend them for more unit test coverage but this kind of proves already that we are calling those methods.

PageCacheIntegrationTest shows again that we should filter out the single block cache tags when we have the list cache tag around as well. Would save a ton of cache tags that we need to check in the page cache.

Also fixed an annoying and confusing bug in AssertPageCacheContextsAndTagsTrait, the Unwanted/Missing labels are inverted ;)

Status: Needs review » Needs work

The last submitted patch, 41: block_access_results-conditions-2495171-31-combined.patch, failed testing.

EclipseGc’s picture

It's disheartening to see this much effort duplication from #2377757: Expose Block Context mapping in the UI. Whatever the case, it's probably worth looking at in light of this issue. Most of this patch seemed pretty sensible to me and would further simplify what needs doing in 2377757. ++ from that perspective.

Eclipse

EclipseGc’s picture

+++ b/core/modules/block/src/BlockRepository.php
@@ -77,9 +77,8 @@ public function getVisibleBlocksPerRegion(array $contexts) {
-      if ($block->setContexts($contexts)->access('view')) {
-        $full[$block->getRegion()][$block_id] = $block;
-      }
+      $block->setContexts($contexts);
+      $full[$block->getRegion()][$block_id] = $block;

+++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
@@ -133,6 +134,15 @@ public function build() {
+        if (!$access->isAllowed()) {
+          $build[$region][$key] = [];
+          CacheableMetadata::createFromObject($access)
+            ->applyTo($build[$region][$key]);
+
+          continue;

These two things together conspire to output render array data that will result in regions rendering when there's nothing in them to display. This is functionally #953034: [meta] Themes improperly check renderable arrays when determining visibility which is why that access check before adding it to the render array is there in the first place. The problem in 953034 persists regardless because even simply adding #access => FALSE into the render arrays somewhere will cause this problem. Still those checks mitigate the problem manifesting with blocks which is the lion share of what most people are likely to see.

Must we apply the cacheable metadata to empty render array elements? If so, we're going to have to rework region rendering in some way.

Eclipse

Fabianx’s picture

Priority: Critical » Major

#45: We have to do that anyway. With placeholders the guarantees that a region is empty or not are not very strong anyway.

What if your block is *SI'd in, etc.

There is a Element::isEmpty() in the works however that could be used for now partially at least.

--

Downgrading to major and merging with the other issue for now per berdir's comment.

Berdir’s picture

@EclipseGc: I'm confused, I don't think it overlaps with that? The combined patch is this on top of #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed, if anything then that issue overlaps with yours?

And about those two lines, yes that's why the last test there is failing. I don't think we have to do add it inside an otherwise empty block element, we can also add it higher up, but so far, that didn't solve the test fail.

As discussed, will move the merged patch over to #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed

Wim Leers’s picture

#46: Element::isEmpty() has already landed.

Wim Leers’s picture

Can Element::isEmpty() not be used to solve the last test failure?

Berdir’s picture

I'm pretty sure this isn't postponed but duplicate of #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed. Everything I did here is part of the other issue. We do need a follow-up issue to try and get rid of the max-age 0 in case of missing contexts but that's a different isuse.

Fabianx’s picture

Title: [PP-1] Block access results' cacheability metadata is not applied to the render arrays » Block access results' cacheability metadata is not applied to the render arrays
Status: Needs work » Closed (duplicate)

As long as that other issue has test coverage, that should be fine.