Placeholders and lazy building

Now that we've seen a bit about how the cacheability metadata can be used in more common scenarios, let's shift gears and talk about those page components that have highly dynamic data.

When we set the maximum age of our Hello World salutation to 0 seconds (don't cache), I mentioned that there are ways this can be improved in order to help performance. This involves postponing the rendering of the respective bit to the very last moment with the help of placeholders. But first, a bit of background.

Each of the cache properties we talked about can have values that make caching the render array pointless. We've already talked about the maximum age being set to 0, but you can also argue very low expiration times to have the same effect. Additionally, certain cache tags can be invalidated too frequently, again making the render arrays that depend on what they represent pointless to cache. Finally, certain cache contexts can provide many variations that significantly limit the effectiveness of the cache to the point that it may even be counterproductive (high storage costs).

Cache tags are something very specific to the application we are building, so there are no general assumptions that can be made as to which have a high invalidation rate. However, there are two cache contexts that by default are considered to have much too high cardinality to be effective: session and user. Yes, we talked about the user context earlier as a good example but in reality—by default—adding this context to a render array has pretty much the same effect as setting the max-age to 0—it will not be cached. The same goes for the session context because there can be so many sessions and users on the site, you probably won't want to have cache records for each individual one.

Since these are not rules that have to necessarily apply to all applications, Drupal configures these values as service parameters, making them changeable if needed. Inside the core.services.yml file (which lists most of the core services), we can find some parameter definitions as well, including this one:

renderer.config: 
auto_placeholder_conditions:
max-age: 0
contexts: ['session', 'user']
tags: []

As you can see, the max-age value of 0 and the previously mentioned cache contexts are included, but no tags. We can also change these values. So, for example, if in our application we know that we won't have too many users and it does, in fact, make sense to cache by user context, or we know of certain cache tags with high invalidation frequency, it makes sense to change this. There are two ways we can do it: either we use our site-wide services.yml file and copy these declarations (while making the appropriate changes) or we can use the services file of a given module in the same way. Both methods have the effect of overriding the default parameters set by Drupal core.

Now that we are clear on why certain things are not cacheable, let's see how this can be addressed using auto-placeholdering.

Auto-placeholdering is the process by which Drupal identifies the render arrays that cannot or should not be cached for the reasons we mentioned before, and replaces them with a placeholder. The latter is then replaced at the very last possible moment while allowing the rest of the page components to be cached. This is also called lazy building.

Drupal identifies the bits that need to be lazy built by the cache metadata that fits the conditions we saw before and the presence of the #lazy_builder property on the render array. The latter maps to a callback that returns its own render array, which can also contain said cache metadata. And it doesn't matter which of the render arrays contains the latter.