Problem/Motivation
There are roughly two sets of problems with Views' caching support:
- Cache tag support
- Efficient cache hits (of cached Views)
- Cache context support
Proposed resolution
- Cache tag support: make sure it's supported fully. The main problem is that Views currently u ses a string-based rendering pipeline. Distinguish between two "types" or "layers" of cache tags in the case of Views (from #2183017-14: Views doesn't bubble up rendered entities' cache tags):
- Invalidation because the entities matching the View's query have changed. This is what #2222847: Simplify Views cache tags to just a list tag per entity type was about, and it should be sufficient.
Example: If a View lists the 5 last nodes by authors that live in Belgium, then it should also tag the cache entry with user cache tags (even if the author isn't rendered by the nodes at all).
I.e.: cache tags based on information in the query. - Invalidation because the rendered representation of the entities matching the View's query render something that the View couldn't possibly have known about
Example: If a View lists the 5 last nodes by authors that live in Belgium, and it renders the taxonomy terms associated with each node, then it should also tag the cache entry with the taxonomy term cache tags. Why? Because when a taxonomy term's label changes, that doesn't invalidate the query results, nor does it change the listing. It only means that the rendered View is now invalid.
I.e.: cache tags based on the rendered output.
- Invalidation because the entities matching the View's query have changed. This is what #2222847: Simplify Views cache tags to just a list tag per entity type was about, and it should be sufficient.
- Efficient cache hits (of cached Views): make it use "standard" render caching, by making it possible to calculate the CID very cheaply, and then do all the work to build a view in a
#pre_render, so all expensive code is only run in case of a render cache miss. Here's a high-level overview of the current problems, and what the most likely solution looks like:
The current state of Views caching
Created 5 nodes with bodies and taxonomy terms. They're listed on the frontpage view.
- By default, this View takes 65/278 (23%) of the total page generation time.
- With both query and render caching enabled in the View, it takes 46/268 (17%) of the total page generation time.
As you can see, Views caching is not very effective. At least not for simple Views. Why? Because a lot of time is spent generating the cache keys for the cache ID.
Making Views caching faster
The majority of the cost of showing a query- and render-cached View is determining the cache keys for the ID. All Views plugins are initialized at runtime to help determine the cache ID. This is where almost all of the time is spent.
(A View's results cache ID is a the hash of the query for that View. Hence we must generate the entire query. And to generate the final query, all plugins must be loaded and executed, because they may alter the query. All of this happens at runtime.)Hence a solution can be to not do this at runtime anymore, but at save time instead. We could determine the cache contexts (which things to vary by, e.g. role, URL …) at save time, and store them in the View config entity, much like the dependencies, but cache contexts would be stored per display because it could vary per display (since different Views plugins may be used in different displays). To do this, Views plugins will need to get a
getCacheContexts()method.
If we can do this, we have a nice set of consequences: we had to initialize the View (to build the query) and all plugins (to alter the query into its final form) precisely because we need them to determine the cache ID. But if the cache contexts are stored in the config entity, then we don't need to initialize the plugins anymore, hence we don't need to initialize the View anymore, henceViewPageController::handle()would be able to no longer do all the work it's currently doing… we will be able to just return a render array of the following form:array( '#cache' => array( 'keys' => ('view', '<view name>', '<display name>', '<cache contexts from the Views plugins>'), ), '#pre_render' => array( '<the method that contains what DisplayPluginBase::executeDisplay() currently contains>' ), ),When you now have Views render caching enabled, it'll be a simple matter of loading the View's config entity, looking at the saved cache context data in the desired display and generating the above render array. No more View initialization, plugin loading, plugin invocations, etc.
- Cache context support: see comment #6.
Remaining tasks
- Cache tag support
- #2222847: Simplify Views cache tags to just a list tag per entity type
- #2221433: Clean up views rendering. Move stuff from template_preprocess_views_view(), into a #pre_render callback
- #2183017: Views doesn't bubble up rendered entities' cache tags
- #2183039: Views' cache tag-based caching only sets cache tags for related entities based on Views relationships, should also do so for entity reference fields
- #2267453: Views plugins do not store additional dependencies
- #2372855: Add content & config entity dependencies to views
- #2378789: Views output cache is broken
- #2381217: Views should set cache tags on its render arrays, and bubble the output's cache tags to the cache items written to the Views output cache
- Efficient cache hits (of cached Views)
Comments
Comment #1
damiankloip commentedComment #2
dawehnerFixed the wrong information in the IS.
Comment #3
wim leers#2267453: Views plugins do not store additional dependencies landed, but was descoped, now #2372855: Add content & config entity dependencies to views is the issue we really care about.
#2183017: Views doesn't bubble up rendered entities' cache tags was fixed as part of another issue.
#2183039: Views' cache tag-based caching only sets cache tags for related entities based on Views relationships, should also do so for entity reference fields is unblocked.
In the mean time, #2378789: Views output cache is broken was handled, which was necessary to make #2183017 also have effect in case of a cached view.
Now creating new issues to cover the remaining steps to complete this meta.
Comment #4
wim leersOpened #2381217: Views should set cache tags on its render arrays, and bubble the output's cache tags to the cache items written to the Views output cache and #2381277: Make Views use render caching and remove Views' own "output caching", once those two are done, this meta can be closed!
Comment #5
wim leers#2372855: Add content & config entity dependencies to views landed, the next blocker is #2342651: Cache tags for *all* config entities; then we can do #2381217: Views should set cache tags on its render arrays, and bubble the output's cache tags to the cache items written to the Views output cache.
Comment #6
wim leers#2381217: Views should set cache tags on its render arrays, and bubble the output's cache tags to the cache items written to the Views output cache was fixed; #2183039: Views' cache tag-based caching only sets cache tags for related entities based on Views relationships, should also do so for entity reference fields has been closed.
New is #2445743: Allow views base tables and entity types to define additional cache contexts. Which points to a third problem: Views' caches are broken by design. Look at how the CID for a Views results cache item is calculated:
and it's even worse for a Views output cache item:
Note how this assumes that:
This is wrong.
But… Views also couldn't have done better in the past. It comes from D7. D8 has only had the concept of cache contexts since about a year. But now it is time to update Views' caching to use the cache contexts it knows about. Since #2267453: Views plugins do not store additional dependencies, it knows the cache contexts for the plugins/handlers for a specific view display. And with #2445743: Allow views base tables and entity types to define additional cache contexts, it will have the information it needs to know for a specific view display by which cache contexts it is varied. So we should put that information to good use — not just use it for rendering, but also for Views' own caching.
Comment #7
dawehnerAs a first step we should replace the result cache ones with the ones which got pre-calculated.
Comment #8
wim leersAll of the child issues have been fixed quite some time ago. Time to retire this issue :)