Problem/Motivation

It seems that, in general, renderable components and/or plugins like blocks and field items are generally limited regarding reusing instances of them on the same page multiple times, due to the behavior of the caching system. All objects holding renderable data and implementing CacheableDependencyInterface seem to be affected by this. I'm trying to describe the limitation with the following simplified example:

A given render array (element) represents an HTML tag with a unique HTML id being randomly generated.
The only requirement of the element is, that it must have a unique HTML id when it's being rendered.
The element will be returned by a block plugin, i.e. a class which implements BlockPluginInterface.
The block plugin does not depend or relate on any data / configuration of the system, i.e. no users, no nodes, no URL etc - it's just there to output the element.
The only requirement of the block plugin is, that an instance of it must be globally reusable.

An instance of the block has been created and placed via the block layout.
A node of a certain type has an entity reference field to blocks. The teaser mode of the node outputs the entity reference field.
There are now multiple nodes which reference to the same block instance. A view on a page lists these nodes in teaser view mode.
In the list, the block outputs the element multiple times, but cached. As a result, the HTML id is not unique and thus doesn't meet its corresponding requirement.

A way to meet both requirements (from element and block plugin) is to set the block plugin's max-age cache to 0. This means that the block doesn't allow any caching. Because of the render cache's "bubbling" behavior, this would imply that all parent render elements won't be cacheable. This means, when an instance of the block plugin is getting rendered inside a field item, all the field items, the whole teaser view, the whole list view, the region where this list view is getting rendered and its further parents won't be cacheable in the render cache.

For block plugins, you could use hook_block_build_alter() to remove its generated cache key for "passing through" the render array. This can be useful e.g. for blocks which

  • are cheap to render and
  • most likely have invalid cache records when their parent elements have invalid cache records

Proposed resolution

Enable objects implementing CacheableDependencyInterface to deliver some sort of cache passthrough, i.e. they just take the bubbled caching metadata from their child elements and pass them through (or say "bubble up") to their parent elements. This way, these objects are just further parts of the whole render structure, but their parent elements (in the above example the field items, teaser view mode, list views and regions etc.) may be still cacheable.

I'm not sure whether it's clear what I've written here. Feel free to ask for more details. Of course it's also possible that I miss or misunderstood an important part of the caching system here.

Comments

mxh created an issue. See original summary.

mxh’s picture

Regarding field items, the problem might become relevant if you have a field item list with multiple items which have all the same value. There also might be something like an HTML id which must be unique, regardless of the given value of the item itself.

Regarding the above example: Yes, I'm aware of the block field module, but this wouldn't solve the elementary problem of this issue report.

Berdir’s picture

Different placements aren't going to re-use the same instance of a block plugin.

What you are describing can be a problem, but you should definitely be able to rely on the fact that a block placement, another block placement and a reference in a node (I'd recommend using block field or so for that not an entity reference but that's another topic) have different instances that are *not* re-used.

If you want to have non-cacheable parts within something that is cached, then look into lazy builders: https://md-systems.github.io/drupal-8-caching/#/10/1. Blocks rendered wth the block view builder already automatically put within a lazy builder, so they can be placeholdered when they can't be cached.

mxh’s picture

Different placements aren't going to re-use the same instance of a block plugin.

Yes, but the described problem contains a requirement that instances must be reusable.

What you are describing can be a problem, but you should definitely be able to rely on the fact that a block placement, another block placement and a reference in a node (I'd recommend using block field or so for that not an entity reference but that's another topic) have different instances that are *not* re-used.

This would solve the problem in particular for blocks, but not in general.

If you want to have non-cacheable parts within something that is cached, then look into lazy builders: https://md-systems.github.io/drupal-8-caching/#/10/1. Blocks rendered wth the block view builder already automatically put within a lazy builder, so they can be placeholdered when they can't be cached.

Yes, the placeholder concept for lazy builders might solve it. Thanks for this tip, I'll try it out.

mxh’s picture

Title: Possible reuse limitation of objects implementing CacheableDependencyInterface » Possible reuse limitation of instances implementing CacheableDependencyInterface
mxh’s picture

Status: Active » Closed (works as designed)

Using lazy builders should be the way to go for this scenario.

mxh’s picture

Unfortunately I have to revoke my comment #6. Lazy builders may help, but not for any scenario. In addition, lazy builders will be called and executed at anytime, although not necessarily needed. Reusing instances like blocks shouldn't be a force for "uncacheability". Something like a cache passthrough mentioned in the proposed solution would something more useful, eg. something like Cache::PARENT. Relating this one #2888838: Optimize render caching as the reults of the discussion there might have impacts on this issue.

mxh’s picture

Status: Closed (works as designed) » Active

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Wim Leers’s picture

Issue tags: +D8 cacheability

Placeholders/lazy builders do not imply uncacheability. It's totally possible that a lazy builder tries to render something, and that something happens to already be cached in the Render Cache: a cache hit is totally possible.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mxh’s picture

Issue summary: View changes
mxh’s picture

Title: Possible reuse limitation of instances implementing CacheableDependencyInterface » Add an option to pass through caching for instances implementing CacheableDependencyInterface
Category: Support request » Feature request

When plugins implementing CacheableDependencyInterface could determine whether they should have their own cache records, this would enable developers to reduce the amount of I/O between application and cache backend. There are blocks and other items which are cheap to render, but are very likely to be invalidated very often.

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

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

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

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

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

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

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.

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.

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.

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.

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.