Not possible to use render placeholdering with Layout Builder blocks

Created on 19 October 2021, over 2 years ago
Updated 9 February 2023, over 1 year ago

Problem/Motivation

The content of some block plugins may vary wildly and cannot be cached efficiently (e.g. the "user" cache context). Drupal has wonderful support for such situations by telling the renderer to defer rendering of that content by inserting a temporary placeholder that is rendered later. This allows the rest of the page to be cached in Dynamic Page Cache. More on that process here β†’ , but the gist of it is to return a render array like this:

return [
  '#lazy_builder' => [SomeClass::someMethod, [$someArguments]],
  '#create_placeholder' => TRUE,
  '#cache' => [
    'contexts' => [
      'user',
    ],
  ],
];

Normally the presence of the "user" cache context would bubble up and the Dynamic Page Cache module would refuse to cache it because it's not worth it. The page will vary by every user. But because we have the #lazy_builder and #create_placeholder, the renderer will automatically create a placeholder for this render array instead of generating the content right away.

But Layout Builder does something that invalidates this approach.

In BlockComponentRenderArray, it extracts the cache metadata from the block plugin and later adds it to a different render array due to this line:

        // We don't output the block render data if there are no render elements
        // found, but we want to capture the cache metadata from the block
        // regardless.
        $event->addCacheableDependency(CacheableMetadata::createFromRenderArray($content));

Extracting the 'user' cache context and placing it in some other render array invalidates the whole placeholdering attempt, because that other render array doesn't have the #lazy_builder or #create_placeholder associated with it, so the render system doesn't create a placeholder. This process is needlessly duplicating the 'user' cache context in the render tree, forcing Dynamic Page Cache to not cache the page at all.

That line only makes sense to execute if the block plugin returned no content, because we still want to bubble that cache metadata.

Steps to reproduce

  1. Create a custom block plugin that simply returns a render array like above. Maybe have it output the current user's username or something in the lazy_builder callback.
  2. Place the block on a page using Layout Builder. You can enable Layout Builder on a per-entity basis for the default page content type.
  3. Observe that the response headers show UNCACHEABLE for the X-Dynamic-Page-Cache

You can also observe that adding the same block plugin to a theme's region using the normal Block UI works as expected. The X-Dynamic-Page-Cache header will not show UNCACHEABLE and the page will be cached correctly.

Proposed resolution

Modify the code in the BlockComponentRenderArray subscriber so that it only copies the cache metadata from the block's render array if it's determined that the block is empty (and those the render array is abandoned). A render array is not considered empty if it has a lazy_builder on it.

Note that this cache logic was introduced in #3088077: Layout builder does not correctly bubble up cache metadata for empty blocks β†’ (partly my doing hah). Originally it was set up to only extract the cache data if the block was empty, but this was changed after comment #14. At the time I don't think we understood the implications of that change.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

πŸ› Bug report
Status

Needs work

Version

10.1 ✨

Component
Layout builderΒ  β†’

Last updated 1 minute ago

Created by

πŸ‡ΊπŸ‡ΈUnited States bkosborne New Jersey, USA

Live updates comments and jobs are added and updated live.
  • Needs tests

    The change is currently missing an automated test that fails when run with the original code, and succeeds when the bug has been fixed.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request β†’ as a guide.

    For this issue to move forward it will need a test to show the issue please.

Production build 0.69.0 2024