Optimize render caching

Created on 23 June 2017, over 7 years ago
Updated 10 September 2024, 4 months ago

Problem/Motivation

Render caching made sense when it was introduced in Drupal 7, and even when it was expanded to be used for many more things in Drupal 8.

But since then, Dynamic Page Cache was added in #2429617: Make D8 2x as fast: Dynamic Page Cache: context-dependent page caching (for *all* users!) β†’ . It's enabled by default. The benefit is that there's a single cache hit to render most of the page, rather than many fragments to retrieve from render cache. This saves a lot of I/O. And has a lower complexity.

Also important: there have been zero noteworthy bug reports during the 19 months that Drupal 8 has been out. In that time, Dynamic Page Cache has accelerated millions of page views.

That begs the question: do we still need render cache? Dynamic Page Cache is caching at a coarser level, so avoids lots of work. Maybe we don't need the granular caching layers as much anymore. That also greatly improves understandability/maintainability/DX. And the render cache bin is the one that has hundreds of thousands of cache items on big sites: #2526150-155: Database cache bins allow unlimited growth: cache DB tables of gigabytes! β†’ , so that'd go away too.

Can we remove render caching altogether? Or do we just disable it for e.g. blocks & entities, but keep it for Views, where it's likely most impactful?

Proposed resolution

TBD

Remaining tasks

TBD

User interface changes

TBD

API changes

TBD

Data model changes

TBD

🌱 Plan
Status

Needs work

Version

11.0 πŸ”₯

Component
RenderΒ  β†’

Last updated about 4 hours ago

Created by

πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

Live updates comments and jobs are added and updated live.
  • Performance

    It affects performance. It is often combined with the Needs profiling tag.

  • needs profiling

    It may affect performance, and thus requires in-depth technical reviews and profiling.

  • Needs issue summary update

    Issue summaries save everyone time if they are kept up-to-date. See Update issue summary task instructions.

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.

  • πŸ‡§πŸ‡ͺBelgium kristiaanvandeneynde Antwerp, Belgium

    Small update:

    Now that we have VariationCache ✨ Abstract RenderCache into a separate service that is capable of cache redirects in a non-render array-specific way Fixed and made sure that nothing in core still (ab)uses the render cache to store things other than render arrays, it could be easier to remove the render cache. Having said that, I still see value in keeping it. The comments also seem to be in favor of keeping it for some parts, so perhaps we could update the IS to strike out the part that suggests outright removing the render cache?

    Having said that, I think it's hard to determine beforehand what we may or may not want to exclude. For instance the IS suggests disabling it for entities and blocks but keeping it for views. But what if I have a block in my footer that is really expensive to calculate and only ever varies by what day it is, I'd rather that block be render cached and every page request from thereon out gets the cached copy than have to recalculate said block on every page request for it to then be cached by DPC.

    I really like @Berdir's suggestion in #11 but I'd turn that upside down. What if we do not cache anything in render cache unless we specify that it's expensive to calculate. This is a lot safer when you consider outside alters: If I flag something as CACHE_NOT_WORTH_IT and then someone alters my render array and adds in something expensive, we have a problem. But if my render array is by default considered as not worth caching and then someone alters in something expensive, adding RENDER_CACHE_REQUIRED, then that would make more sense.

    Figuring out what we want cacheable or not by default is still possible that way, we'd only need to add RENDER_CACHE_REQUIRED to the things we know are often expensive.

  • πŸ‡¬πŸ‡§United Kingdom catch

    #59 sounds promising, also seems like it would fit quite well with lazy builders/autoplaceholdering where you have to explicitly define something in a way it can be placeholdered.

  • πŸ‡΅πŸ‡°Pakistan hmdnawaz

    Patch for Drupal 10.3

  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    I really like @Berdir's suggestion in #11 but I'd turn that upside down. What if we do not cache anything in render cache unless we specify that it's expensive to calculate. This is a lot safer when you consider outside alters: If I flag something as CACHE_NOT_WORTH_IT and then someone alters my render array and adds in something expensive, we have a problem. But if my render array is by default considered as not worth caching and then someone alters in something expensive, adding RENDER_CACHE_REQUIRED, then that would make more sense.

    I think render cache is by default still more valuable than not. Entity types can already easily opt out, even per bundle, and we do that for paragraphs for example, where we can safely assume that they are not reused outside of of a single node and by default, if one is invalidated by a save, then all of them are. But nodes very commonly still are absolutely useful to cache. Rendering entities is surprisingly expensive with view display config, many formatters, often multiple child entities like medias, paragraphs, layout builder and so on. It's quite easy to end up with something like a view in a block on all pages that is going to be frequently invalidate your complete dynamic page cache and then you have to rebuild all your node pages again which are most likely still cached.

    Media entities I'm on the fence, I can see those being not really worth it and enough to inherit in the parent, I've also had multiple cases with per parent access and customization, where you have to consider the parent anyway. But we don't need any new API's for that, it's just a flag we could set on the entity type. But even for media, don't forget about things the media library, where we render a lot of media entities.

    Also for blocks, IMHO the majority are still worth to cache, menu blocks can be very expensive with many links for example, it's IMHO also much easier for BC to add an opt out rather than an opt-in.

    We now have rendering time built-into the render debug output, it would be fairly easy to automatically highlight all elements that take less than a configurable/estimated/measured render cache get. We could even build that into the performance tests?

    for example, the default powered by drupal block for me reports as "RENDERING TIME: 0.003705978". The default frontpage node from the paragraphs_demo module with 7 paragraphs is "RENDERING TIME: 0.229816914". You can search for RENDERING TIME: 0.00 to find things that are propably not worth to cache:

    on olivero, for me, additionaaly:
    site branding: RENDERING TIME: 0.005095959 (interesting example, because if you do some more complex things with the logo, e.g. we often have per-language logos, or inline SVG logos, that could easily get slower)
    search form: RENDERING TIME: 0.004688025
    account menu is RENDERING TIME: 0.009571075
    messages block: RENDERING TIME: 0.002686024
    syndicate block: RENDERING TIME: 0.004869223

    so, doesn't get much faster than ~0.03 on my system. didn't compare yet how fast a render cache get is.

  • πŸ‡§πŸ‡ͺBelgium kristiaanvandeneynde Antwerp, Belgium

    The render time avenue is worth exploring, but how would that work? The debug info is only available after rendering, so we can choose not to set it when we had to render it, but we'd still be trying to get it from the cache every time it is to be built. Ideally, we don't go to the cache at all, which would be possible using a tag like CACHE_NOT_WORTH_IT or RENDER_CACHE_REQUIRED.

    So either we depend on render time and eat unnecessary cache lookups, but have the benefit of a "smart' render cache. Or we go with the aforementioned special flags/tags, but require manual intervention.

    If we do choose to go with a smart system based on render time, we'd need a way for people to opt out of it. Let's say you have a block that renders fast but pings an external API, you might wanna cache that block regardless for a given while as to not run into a rate limit. (I know there's ways around that by using a service with an internal cache, but just giving a feasible example)

    A downside of adding the render time checks to StandardPerformanceTest is that the time is machine-dependent, so we'd be introducing another test that can randomly fail on GitLab CI.

    Either way, not a bad idea. Definitely worth exploring.

  • πŸ‡¬πŸ‡§United Kingdom catch

    A downside of adding the render time checks to StandardPerformanceTest is that the time is machine-dependent, so we'd be introducing another test that can randomly fail on GitLab CI.

    Yeah I've been specifically avoiding any assertions based on times, but we could add something to the OpenTelemetry spans so that render elements show up and/or to webprofiler module.

Production build 0.71.5 2024