Problem/Motivation
Views rows are render cached individually, but this is quite inefficient. Unlike entity view mode caching, the rows are specific to an individual view configuration, and we already render cache the entire view output at a higher level. It's possible that the view is invalidated via list cache tags and most of the individual rows aren't, but in practice the hit rate is still going to be pretty low.
When there's a miss on the view render cache but a hit on the rows, other caches that make the row rendering faster are likely to be warm too, like entity caches. On a cold cache request, it's a lot of cache sets, especially once we take into account cache redirects.
This is what the cache items look like:
60 => "views:fields:recipe_collections:block:fdb27fba4d5f968ea52227f521bd1414d24eab3c70a5cf7043a52b3309c26fc8:[languages:language_interface]=en:[languages:language_url]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
61 => "views:fields:recipe_collections:block:71f37900c45567604b647479931173987ea1eba0cb060878670a8fa628126799:[languages:language_interface]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
62 => "views:fields:recipe_collections:block:71f37900c45567604b647479931173987ea1eba0cb060878670a8fa628126799:[languages:language_interface]=en:[languages:language_url]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
63 => "views:fields:recipe_collections:block:c96be8074bfbccf8cc28c8759bdd19b3cc9c82438ebaaeb686f0d5cf6646ca13:[languages:language_interface]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
64 => "views:fields:recipe_collections:block:c96be8074bfbccf8cc28c8759bdd19b3cc9c82438ebaaeb686f0d5cf6646ca13:[languages:language_interface]=en:[languages:language_url]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
65 => "views:fields:recipe_collections:block:ed61bf5340f38fdaa5e9446dae63599474d5f91c7f742be8db07d9a2a4e8191f:[languages:language_interface]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
66 => "views:fields:recipe_collections:block:ed61bf5340f38fdaa5e9446dae63599474d5f91c7f742be8db07d9a2a4e8191f:[languages:language_interface]=en:[languages:language_url]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
67 => "views:fields:recipe_collections:block:f02695c8a7ff3b3eda5904ff52b6013770aef87aed7801be1ed80fb6c132ab20:[languages:language_interface]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
68 => "views:fields:recipe_collections:block:f02695c8a7ff3b3eda5904ff52b6013770aef87aed7801be1ed80fb6c132ab20:[languages:language_interface]=en:[languages:language_url]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
69 => "views:fields:recipe_collections:block:38c57f665e9959898504933e9a41749afa38611fd9594cb24d58cb789ecb466c:[languages:language_interface]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
70 => "views:fields:recipe_collections:block:38c57f665e9959898504933e9a41749afa38611fd9594cb24d58cb789ecb466c:[languages:language_interface]=en:[languages:language_url]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
71 => "views:fields:recipe_collections:block:09a8013364aaba62b9f2652288456e1c393151a0f3be323daa5730e9b3accc75:[languages:language_interface]=en:[theme]=umami:[user.permissions]=80dc35c311ab586f38e4b3f5e9ddc4e5ae9d290d51308881d6855312548b4cc5"
Steps to reproduce
Proposed resolution
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #17 | Screenshot from 2026-01-30 21-35-19.png | 80.19 KB | catch |
| #17 | Screenshot from 2026-01-30 21-35-23.png | 81.83 KB | catch |
| #17 | Screenshot from 2026-01-30 21-28-54.png | 370.46 KB | catch |
| #17 | Screenshot from 2026-01-30 21-28-49.png | 364.52 KB | catch |
| #17 | Screenshot from 2026-01-30 21-24-52.png | 514.71 KB | catch |
Issue fork drupal-3564937
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3564937-remove-duplicative-caching
changes, plain diff MR !14178
Comments
Comment #3
catchThis will break views caching tests.
Also if we go ahead here we can probably deprecate CachePluginBase::getRowCacheTags()
Comment #4
catchComment #5
catchDid some profiling:
Umami front page logged in as user/1
With and without the MR, I warmed the caches, then deleted only the views block render cache entry for the recipe collections view, which uses fields:
Then hit the page again and profiled it. This isolates the page differences to the row caching change as much as possible.
On my local while profiling, the row caching is saving around 30ms out of 60ms from that method (see screenshots), so it's having an effect, but it's not much - e.g. it probably takes as much as that to save the cache items on cache misses.
Comment #6
catchBackground reading:
#1867518: Leverage entityDisplay to provide fast rendering for fields
#2381277: Make Views use render caching and remove Views' own "output caching"
#2450897: Cache Field views row output
What I think happened is that #1867518: Leverage entityDisplay to provide fast rendering for fields mostly fixed the performance issue that #2450897: Cache Field views row output was also solving, but they were also interdependent with each other and we didn't fully re-evaluate the need for the caching issue once the field rendering issue landed.
The issue summary from the original issue still has:
And we never actually resolved those.
I think we possibly could manage to add in 'render cache multiple get' here now since we added that for placeholders, but that doesn't address the extra overhead on cache misses and the overall cache storage needed for the extra items, so can just as well remove a caching layer.
Comment #7
smustgrave commentedI will not be able to mark this as cache is my worst but any concern with removing keys from #cache? Could anyone of been using that?
Comment #8
catch@smustgrave #cache['keys'] is what determines whether a render array gets cached in its own right in the render cache or not, it's only used internally by the renderer, and in this specific case I'm not sure that render array even shows up somewhere you'd be able to alter it.
Comment #9
smustgrave commentedBeen 2 weeks and don't want to leave this one hanging, so I'll go on a limb.
Comment #11
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #12
catchRebased.
Comment #13
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #14
catchRebased.
Comment #15
catchRebased.
Comment #16
alexpottI think we need to consider views which are not using entity displays here a bit more carefully for example what is the impact on the admin content view? Maybe we should make the row cache dependent on view style? If it is using entity display's then this shouldnt be used but if it is using fields then it should be?
Comment #17
catchI warmed caches on admin/content with Umami, then clicked on the first item in the list, edited and saved it, then checked xhprof for the next admin/content load.
That should be more or less the worst case for this change since all but one row will still be in cache.
main views row rendering comes in at around 360ms, the MR here comes in at around 780ms - so rendering all but one items on admin/content the cache saves about 400ms.
However, on a cold cache (after visiting admin/help first), this is saving over 100,000 function calls and approx 300ms (with xhprof overhead etc.) The difference on my local was 2.27 seconds vs 2.0 seconds for the entire request. Can't look at only row rendering in this case, because the overhead is in the render system itself with render cache gets/sets to have to compare total function calls/time really.
Once we get into 10% of 20% of the rows not being in cache (e.g. if other site editors are adding/editing content on the site in between the page getting viewed), then the extra cache sets for those rows will probably cancel out the cache hits for the rows that haven't changed.
So for me it's a worthwhile trade-off to make the worst case better, keeping the best case unchanged, and then the middle cases will be somewhere between a fair bit worse, neutral, and a bit better (e.g. a handful of rows in cache but most rows not). Apart from the raw performance, we're also able to save space in the cache_render bin.
Comment #18
catchAnother thing here is that a lot of requests to admin/content or admin/people will immediately filter the results - when the results are filtered the likelihood of a cache hit on the rows in the filtered list is very low indeed. So with the cache even if we save time on the first request to admin/content, the second request to admin content with filters can be slower.
Comment #19
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #20
catchComment #21
catchHad a quick look at what actually takes the time rendering the views rows, and a lot of it is individually looking up path aliases which would be solved by #3518668: Use Fibers for rendering views rows, and a lot of the other time is in access/permissions checks which should be improved by #3539161: Static cache access policy checking.
Very hard to separate out exactly how much is contributed by these and in turn how much would be saved especially since this and the other views issue conflicts, but definitely identifiable savings to be made without additional persistent caching.
Comment #22
alexpottThe performance tests have changes again.
@catch thanks for the analysis on #17. I think given the plans to improve views performance in other ways going ahead here feels like a good idea.
Comment #23
catchRebased.
Comment #24
alexpottCommitted and pushed 3245d571a9f to main and 27d5b0662e3 to 11.x. Thanks!
Comment #29
quietone commentedUpdated CR and published.