Problem/Motivation

In practice, every single render array is varied by two cache contexts already:

  1. 'theme', because any render array that uses the theme system is tied to a theme
  2. 'languages:interface', because almost all render arrays use t()

But we don't vary every single render array by those automatically, we put the burden on EntityViewBuilder, BlockViewBuilder etc. to define those cache contexts.

Proposed resolution

Add a container parameter that contains the default cache contexts and apply this when rendering/render caching.

Remaining tasks

None.

User interface changes

None.

API changes

None.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug — this is basically adding what Drupal should already be doing and is fixing the regression of removing the functionality of drupal_render_cid_parts().
Issue priority Major because because necessary to ship with a non-WTF DX: repetition all over.
Prioritized changes The main goal of this issue is DX/performance
Disruption Zero disruption.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers’s picture

Component: request processing system » render system
Status: Postponed » Needs review
FileSize
14.22 KB

Here's an initial patch that sets 'theme'. We'll have to add 'languages:interface' once #2432837: Make cache contexts hierarchical (e.g. 'user' is more specific than 'user.roles') lands.

Status: Needs review » Needs work

The last submitted patch, 1: renderer_default_cache_contexts-2432837-1.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
14.2 KB

Chasing HEAD.

Status: Needs review » Needs work

The last submitted patch, 3: renderer_default_cache_contexts-2432837-3.patch, failed testing.

Wim Leers’s picture

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

Green.

effulgentsia’s picture

+1 to the issue title. That D8 HEAD doesn't do this is a regression from D7: https://api.drupal.org/api/drupal/includes%21common.inc/function/drupal_...

Not sure about making these two configurable though. Because in the case of 'theme', doRender() actually calls $this->theme->render(), so seems like it should force it. In the case of 'languages:interface', doRender() doesn't call t() itself, so setting that is more a convenience to allow lots of other places not to have to, but if we then proceed to make all those places not do it themselves, then that essentially is an implicit contract that the renderer will.

Having a container parameter for additional ones might make sense, but perhaps out of scope for this issue?

Wim Leers’s picture

Good point about 'theme' being always required. But I'm not sure it's worth the effort to special case 'theme' — there are a zillion other ways you can break Drupal by changing settings, configuration or container parameters. Besides, I could argue that somebody could swap out the theme manager to make Drupal unthemed (think headless Drupal), at which point omitting the "theme" cache tag would be valid.

We cannot reasonably expect every render array that uses t() (which is probably >98%) to specify the 'languages:interface' cache context, so I think an implicit contract there is acceptable. That's basically also what the D7 function you linked to does.

Therefore I think that the simplest and clearest way to do this, and to make it easily extensible, is to keep it as a container parameter. But if you feel strongly about this, I could definitely live with those two being hardcoded in \Drupal\Core\Render\Renderer.

effulgentsia’s picture

there are a zillion other ways you can break Drupal by changing settings, configuration or container parameters

Good point about container parameters. But I think D8 has come a long way in reducing the fragility of settings and configuration. So I think my only objection then is having the name "config" in the parameter name. How about renderer.required_cache_contexts?

Wim Leers’s picture

Sounds good :)

Thanks for the initial review. Now we just have to wait for the blocker to land, so that we can do 'language:interface' here too. Or do you think we should already proceed and land this?

effulgentsia’s picture

Status: Needs review » Postponed

Unless this is blocking other work, I think it can wait on the other issue.

Wim Leers’s picture

Title: [PP-1] Set default render cache contexts: 'theme' + 'languages:interface' » Set default render cache contexts: 'theme' + 'languages:interface'
Status: Postponed » Needs work
Wim Leers’s picture

Status: Needs work » Needs review
FileSize
18.38 KB

First, a rebase.

Wim Leers’s picture

Title: Set default render cache contexts: 'theme' + 'languages:interface' » Set default render cache contexts: 'theme' + 'languages:' . LanguageInterface::TYPE_INTERFACE
FileSize
24.22 KB
10.66 KB

And now with #9 done: wherever the languages cache context was added, it was actually the interface language that was intended, so 'languages:' . LanguageInterface::TYPE_INTERFACE should've been used. But until #2432837: Make cache contexts hierarchical (e.g. 'user' is more specific than 'user.roles') (which just landed), that distinction didn't exist yet.

Wim Leers’s picture

FileSize
24.54 KB
1.44 KB

I missed one bit.

Wim Leers’s picture

FileSize
24.56 KB
4.65 KB

@effulgentsia remarked what about renderer.required_cache_contexts as the parameter name?

The last submitted patch, 13: renderer_default_cache_contexts-2432837-13.patch, failed testing.

The last submitted patch, 14: renderer_default_cache_contexts-2432837-14.patch, failed testing.

Wim Leers’s picture

FileSize
25.39 KB
5 KB

Fixing the failures in #13/#14.

The last submitted patch, 15: renderer_default_cache_contexts-2432837-15.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 18: renderer_default_cache_contexts-2453059-18.patch, failed testing.

The last submitted patch, 18: renderer_default_cache_contexts-2453059-18.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
27.05 KB
8.08 KB

Status: Needs review » Needs work

The last submitted patch, 23: renderer_default_cache_contexts-2453059-23.patch, failed testing.

Fabianx’s picture

  1. +++ b/core/core.services.yml
    @@ -1,6 +1,8 @@
    +  renderer.config:
    +    required_cache_contexts: ['languages:language_interface', 'theme']
    

    I think we only need this when:

    drupal_is_multilingual()

    which means more than one language is configured at a given time.

    I am not sure what the D8 equivalent is for that though.

  2. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -164,10 +174,19 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    +    // Bubbleable rendering metadata that has configurable defaults.
    +    $required_cache_contexts = $this->rendererConfig['required_cache_contexts'];
    +    if (isset($elements['#cache']['contexts'])) {
    +      $elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $required_cache_contexts);
    +    }
    +    else {
    +      $elements['#cache']['contexts'] = $required_cache_contexts;
    +    }
    

    Don't we need this only when setting cache data?

    This is a _lot_ of unnecessary de-duplication we need to do.

  3. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -164,10 +174,19 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    -    if (isset($elements['#cache'])) {
    +    if (isset($elements['#cache']['keys'])) {
    

    Good idea :)

CNW per 2.

Berdir’s picture

Does it really hurt if the context is always there? If there's only one language, it will simple always have the same value, that shouldn't hurt much?

Wim Leers’s picture

FileSize
3.8 KB

#25.1/#26:

If there's only one language, it will simple always have the same value, that shouldn't hurt much?

Exactly.


#25.2:

Don't we need this only when setting cache data?

At first, I was only setting it when caching data. But that doesn't work. It needs to happen always, because if on a page there is nothing that is render cacheable, we still want those cache contexts to be set.

What we could do, is set it on cache gets and when doing root rendering. See proposed_interdiff.txt to see what that'd look like. It trades cleanliness/maintainability for duplication/performance. Moving it into a helper method would help make it more maintainable, at the cost of a smaller performance gain.

Personally, I think we should go for maintainability in Renderer for now. We know we can still optimize the cacheability-handling of render array rendering, it's better to do all optimization work there after #2429287: [meta] Finalize the cache contexts API & DX/usage, enable a leap forward in performance.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
28.07 KB
1.1 KB

Green.

Fabianx’s picture

Yes, indeed we can only set this when:

if ($is_root_call || isset($elements['#cache']['keys'])) {
}

I think adding this if around the won't hurt or do you see a case where we would need it on another level?

That would be almost as clean as the proposed solution, but avoid an even larger percentage of Cache Context merging per page rendering.

--

Besides that this is RTBC from my side.

Fabianx’s picture

I thought for debuggability reasons we should maybe make 'rendered' now also a required cache context.

Then I changed my mind as we never want anyone to remove that one as its kinda internal ...

Wim Leers’s picture

Status: Needs review » Needs work

Excellent idea! Best of both patches :) Much simpler!

Wim Leers’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
28.49 KB
1.76 KB

Done.

Added beta evaluation.

I don't think a CR will be necessary, but I will update the docs under https://www.drupal.org/developing/api/8/render once this lands.

Status: Needs review » Needs work

The last submitted patch, 32: renderer_default_cache_contexts-2453059-32.patch, failed testing.

Fabianx’s picture

Category: Task » Bug report

Re-classifying as a bug:

This is the replacement for drupal_render_cid_parts() and hence can be considered a regression from Drupal 7.

Fabianx’s picture

Issue summary: View changes
Wim Leers’s picture

Status: Needs work » Needs review
FileSize
30.36 KB
3.6 KB

Changes:

  1. Test failures were caused by a few tests that were incorrectly simulating things. Simple fix.
  2. The changes to sites/default/default.services.yml were lost; restored them.
Fabianx’s picture

Status: Needs review » Reviewed & tested by the community
  1. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -164,10 +174,25 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    +    if ($is_root_call || isset($elements['#cache']['keys'])) {
    +      $required_cache_contexts = $this->rendererConfig['required_cache_contexts'];
    

    Nice! :)

  2. +++ b/core/modules/node/src/Tests/NodeListBuilderTest.php
    @@ -36,9 +37,9 @@ public function testCacheContexts() {
    -    $this->container->get('renderer')->render($build);
    +    $this->container->get('renderer')->renderRoot($build);
    

    I am honestly not sure, but might imagine it might be safer to use renderPlain instead?

    But we can leave it also as renderRoot for now.

This looks great now!

RTBC!

We could use some doc updates and do we need to update any of the old CRs? I am not sure.

Wim Leers’s picture

#37.2: we only need to use ::renderRoot() in those few cases because we're expecting to look at the final rendered result, which we expect to include the default render cache contexts. And "looking at the final rendered result" -> "use renderRoot()". That's all.

Documentation updated at https://www.drupal.org/developing/api/8/render/arrays/cacheability/bubbling.

I don't think CR updates are necessary: the reason we do this is precisely to allow people to not have to think about using t() nor actively think about the negotiated theme when building render arrays: thanks to this patch, they can continue to not add cache contexts for those cases.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

This issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 2847b59 and pushed to 8.0.x. Thanks!

  • alexpott committed 2847b59 on 8.0.x
    Issue #2453059 by Wim Leers: Set default render cache contexts: 'theme...

Status: Fixed » Closed (fixed)

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