Problem/Motivation
Sparked from Fabianx's comment at #2378789-8: Views output cache is broken plus an IRC discussion with damiankloip, Fabianx and myself.
That issue had to make Views' output cache aware of:
$element['#attached']$element['#post_render_cache']$element['#cache']['tags']
Ideally, that wouldn't have been necessary; ideally Views would just have to call a method on the Renderer service to get the cacheable representation of a rendered render array. This issue is about adding that.
Proposed resolution
Add a Renderer::getCacheableRenderArray() method, to encapsulate which data is needed for caching a render array.
Remaining tasks
None.
User interface changes
None.
API changes
- Added
Renderer::getCacheableRenderArray() - Removed
drupal_render_cache_get() - Removed
drupal_render_cache_set() - Removed
drupal_render_cid_create()
Beta phase evaluation
| Issue category | Bug because: #2379741-2: Add Renderer::getCacheableRenderArray() to encapsulate which data is needed for caching a render array and have views use it. |
|---|---|
| Issue priority | Major because it blocks #2381797: Add render_cache debug output, which is major. |
| Disruption | Almost zero disruption: if there are any callers of drupal_render_cache_(get|set)() or drupal_render_cid_create(), it's going to be a handful at most. Disruption is hence very, very low, and does not outweigh the long-term benefits of this change. (Which includes e.g. adding a "max age" property to the #cache key of render arrays in a later D8 release, with zero API breakage or logic breakage thanks to this change.) |
| Comment | File | Size | Author |
|---|---|---|---|
| #37 | 2379741-37.patch | 34.85 KB | wim leers |
Comments
Comment #1
wim leersHere's a patch that adds this.
Note that I have one concern: Views output cache would ideally not exist; it should just use render caching like anything else. Which then leaves the question: what is a valid use case for this?
Fabianx wrote this in IRC, but it's not entirely clear what that means. It'd be great if you could explain that in some more detail, Fabian :)
Comment #2
fabianx commentedSo #pre_render, #cache is just a pattern, but it might not work for you, so you want custom caching.
The bigger impact however is if views hardcodes this:
I might want to add downstream-ttl as a render array #cache property and I need it propagated upstream.
Therefore I can now exchange the renderer service, but if views has this hardcoded, I can't have this work with views caching.
And that is why this is a bug.
Comment #3
fabianx commentedPostponing on #2378789: Views output cache is broken briefly ...
Comment #4
fabianx commentedComment #5
fabianx commentedCode needs work to port views over to the new API.
Comment #6
wim leersVoilà.
Comment #7
damiankloip commentedWe are losing something here, we need to make sure this is ok. With this patch we will no longer be merging with any other items in $view->element.
Comment #8
damiankloip commentedI removed the 'output' key on the storage property. If we are not using other keys, I don't think we need to use that either now. Let's just simplify as much as we can?
We should also probably inject the renderer, and not use drupal_render directly anymore.
Just question in #7 I think.
Comment #10
damiankloip commentedWhoops.
Comment #11
dawehnerShould we inject the cache services? From an abstract point of view it would be cool to be able to render something without a cache dependency, so maybe make it optional?
At least this request() could be replaced with the stack again.
Tis is really nice code now!
Comment #12
wim leers#7: I found that
$view->elementbusiness highly confusing; I don't entirely understand how that works or is intended to work. Could you change that to match how you think it should work? :) Thanks!#8 + #10: looking good :) Just one docblock update that didn't match the parameter order; fixed that.
#11:
Comment #13
fabianx commentedThe patch looks already really good!
Comment #14
wim leersI'm not yet happy with the method names (
saveToCache()andgetFromCache()). Suggestions?I'm also not yet convinced
getCacheableData()is the best method name, because you don't just get back "data", but you get back an actual valid render array that is the most minimal representation of the given rendered render array.Thoughts?
These hunk doesn't belong here.
Comment #15
wim leersReroll after #2382503: Not possible to render self-contained render array while a render stack is active landed. Removed the faulty hunks mentioned in #14 (which btw have already been committed, hence no interdiff for those removals).
Comment #16
wim leersThese are protected methods anyway, so it actually doesn't matter at all. It's not important, and is sufficiently clear.
More accurate names would be
getCacheableRepresentation(&$markup, $elements)orgetCacheableRenderArray(&$markup, $elements)orgetMinimalRepresentation(&$markup, $elements).I think that
getCacheableRenderArray(&$markup, $elements)would be the best choice, and better thangetCacheableData(&$markup, $elements), which is in the current patch. Do others agree?Comment #17
fabianx commentedgetCacheableRenderArray is great!
cacheSet / cacheGet is probably sufficient for the protected methods.
Comment #18
wim leersDone.
Comment #19
wim leersI'd implemented the first part of #17, not the second part (). Did that now.
Comment #21
wim leersinterdiff == patch => facepalm
Comment #22
wim leersThis blocks #2381797: Add render_cache debug output, which is major. Hence bumping this too.
Comment #23
fabianx commentedI would _love_ to RTBC this, but the question remains if there can be something already in $this->view->element[metadata-key] that is not cached.
In D7 RenderCache I circumvented this in two ways:
a) Either adding the whole element as x_render_cache_storage (and removing any #markup other children by using again the getCacheableRenderArray function).
b) Rendering whatever was in there early on the stack.
Needs feedback by Views maintainer I assume to see if there can be something in $this->view->element that was not cached before.
Comment #24
wim leers… in which case that'd have to be merged, which is what HEAD does.
I tried to answer this in multiple ways:
->elementis being modified. None of them are called on a Views output cache hit, hence there cannot be anything to merge.#pre_rendercallback. This initializes theViewExecutable $viewobject, then callsexecuteDisplay()on it, which then shortcuts to the output cache. The whole point of the output cache is to avoid executing the rendering logic for all the plugins, so it's only natural that there's no opportunity for them to alter$view->element.So AFAICT, the theoretical question you raised (which is a good question) is not a problem.
Comment #25
wim leersYesterday I noticed that
HtmlRenderer::prepare()could also benefit from this :)And while working on that, I managed to clean this patch up further. The only thing that's been removed is the comment about ESI, where it's stated that cache backends can replace the actual content with ESI includes. That's been a lie for a very long time, because the cache backend receives the array by value, not by reference, as is dictated by
CacheBackendInterface. In D7,cache_set()also worked by value, but in D7, it'd have been possible to replacecache.incand change the signature, in which case the documented ESI approach would've worked indeed. This was added in 2009, and came with zero test coverage. ESI would probably not be implemented like this anyway nowadays.Comment #26
fabianx commentedI don't think this was ever used by anything, so i think this is fine
Thanks for finding further usages of this and thanks for checking that the "merge" before was no merge, because there was never any content.
RTBC :)
Comment #27
dawehnerJust in case you want to, you could document that this result can be safely serialized()/unseralized(), not sure whether this is implicit already with "cached"?
<3
Comment #28
wim leersDoc changes only, hence keeping at RTBC.
Comment #30
alexpottComment #31
wim leersdrupal_render_cache_(get|set)(). It's an API that I'd consider almost part of the internal Drupal API, very few people are using this, if any, but since it was a procedural API, it's indeed technically an API change.drupal_render_cid_create()for consistency, as requested. (This had zero calls to it, except for the Renderer service and tests (ab)using it.) Note that this pretty much doubled the patch size, but it's still a simple enough patch IMO.drupal_render_cache_generate_placeholder()is then the last remnant of what logically belongs on the Renderer service, but that's for a follow-up issue. That we might want to keep around for BC. But, again, that's a discussion for a separate issue.Comment #32
dawehnerI know this existed before, but can we document why we don't want to deal with it in case we use https? It feels like quite an detail which is not always necessarily right.
I can haz the cache factory injected?
it would be nice to newline this.
Meh, this conflicts with #2378815: DisplayPluginBase::elementPreRender() and View::preRenderViewElement() are called even when the Views output cache is being used, causing notice
Do we think it is okay to no longer reference the view element entries?
Comment #33
wim leersI think it's okay, because the modifying by reference only needs to happen in the "pre render" stage of rendering a view. The cited code is already in the "render" stage. It isViewExecutable::render()that callsCachePluginBase::cacheGet(), and all logic in that function after the invocation ofcacheGet()directly uses$this->display_handler->output, so it's safe.On further consideration; I don't think it's okay, because it's possible a Views "post render" callback modifies
$view->element['#attached']instead of$view->display_handler->output, because that used to be possible. So, restoring the references.Comment #34
wim leersDiscussed point #32.1 with dawehner and catch in IRC, this is the conclusion.
Comment #37
wim leersFixed test failures (oops!) + 80 cols violations.
Comment #38
dawehnerAlright, looks sane now.
Comment #39
alexpottCommitted 62c31b5 and pushed to 8.0.x. Thanks!
Thanks for adding the beta evaluation to the issue summary.