Problem/Motivation
Render Caching is great, but debugging asset / library adding and post_render cache can be really complicated, also it is difficult to see if there are cache hits or not.
In Drupal 7 render_cache module, I use twig style debug output, which works great.
Reference: https://github.com/LionsAd/render_cache/blob/770c615f6081e7d415dcae431ee...
Proposed resolution
In drupal_render_cache_get() (soon in renderer service) add something like (pseudo-code):
// _after_ get from cache
if ($this->use_debug) {
$elements = $this->addDebugOutput($elements, TRUE); // TRUE == Cache Hit
}
// _after_ set to cache
if ($this->use_debug) {
$elements = $this->addDebugOutput($elements, FALSE); // TRUE == Cache Miss
}
function addDebugOutput($elements, $cache_hit) {
$prefix = '<!-- START RENDER (#pre_render): ' . print_r($elements['#pre_render'], TRUE) . "\n" . ' CACHE INFO: ' . "\n" . print_r($elements['#cache'], TRUE);
$prefix .= "\nCACHE-HIT: " . $cache_hit ? 'YES' : 'NO' . "\n";
$prefix .= "\nATTACHED: " . print_r($elements['#attached'], TRUE) . "\n";
$prefix .= '-->';
$suffix = '<!-- END RENDER: ' . print_r($elements['#pre_render'], TRUE) . ' -->';
$elements['#markup'] = "\n$prefix\n" . $elements['#markup'] . "\n$suffix\n";
return $elements;
}
This is major, because it is almost impossible to debug render caching else. (I tried other ways ...)
The impact is minimal as when the option is turned off, there is no change in code.
Remaining tasks
- Make a patch!
User interface changes
- None
API changes
- New config option in services.yml to add render cache debug output.
Release notes snippet
A new developer feature to show debug markup for render caching has been added, this can be configured from services.yml and is disabled by default. Sites should ensure that their site-specific services.yml includes the new section.
| Comment | File | Size | Author |
|---|---|---|---|
| #104 | interdiff-2381797-102-104.patch | 1.38 KB | slashrsm |
| #104 | 2381797-104.patch | 9.22 KB | slashrsm |
Comments
Comment #1
wim leersI'd love this!
Comment #2
tom verhaeghe commentedComment #3
tom verhaeghe commentedI added the config option 'cache_debug' in services.yml. I also attempted to print the debug data (I omitted cache_info for debugging reasons). When I receive a cache miss, everything works as expected, but when there's a cache hit it doesn't work as I hope it would.
I guess I miss something here...
Comment #4
tom verhaeghe commentedComment #5
wim leersGood progress here, thanks :)
This service should be injected.
This is just temporary debug output, while working on this, right? :)
This is why the debug output is only appearing for you in case of a cache miss: this is the code path that deals with a cache miss and writing to the cache.
You'll also want to call it in this if-test, where a render cache hit is handled:
Needs a newline between these two braces.
Comment #6
wim leersA matching default value in
default.services.ymlis still missing for this.But… this actually doesn't belong in Twig. Twig is a theme engine. It's pluggable. Somebody could replace it.
This render cache debug output is independent of Twig. We probably want a
renderer.configparameter indefault.services.yml, just like we havetwig.config. And in there, we'd then have adebug: false, just like for Twig.Comment #7
tom verhaeghe commentedAdded a renderer.config array, injected it into the Renderer object and changed the way how the config variables are accessed.
Still stuck on where to put the $this->addDebugOutput code, it seems to never hit the cache and I don't know why ... need help :-)
Oh, and the addDebugOutput() method still needs work, i know :-)
Comment #8
wim leersI tested the patch and found a few important problems:
['parent' => ['#type' => 'container', '#cache' => […], 'child' => ['#markup' => 'this is the child', '#cache' => […]]]], then the child element's cache debug output is render cached. We don't want that, because then the cache debug output is no longer reliable: it'll continue to say that the child is retrieved from cache (or not), despite the child not having been retrieved separately at all; it's only the parent that was retrieved from render cache, not the child!if (isset($elements['#cache']['cid']) || isset($elements['#cache']['keys']).Comment #9
fabianx commentedWe might need to postpone that OR bring over the re-factoring of drupal_render_cache_set / drupal_render_cache_get to live in this class.
Wim, can you point to the issue where you had that conversion to saveToCache / retrieveFromCache, because I don't remember.
The debug output really belongs in those functions.
The rest of the patch looks good already. Thanks so much for working on this, Tom!
Comment #10
wim leersAgreed.
The patch you're looking for is at #2379741: Add Renderer::getCacheableRenderArray() to encapsulate which data is needed for caching a render array and have views use it.
Comment #11
tom verhaeghe commentedApplied patch #2379741: Add Renderer::getCacheableRenderArray() to encapsulate which data is needed for caching a render array and have views use it + rerolled parts of previous patch and printing cache tags and keys (see interdiff).
Comment #12
tom verhaeghe commentedComment #13
wim leersI've been pushing #2379741: Add Renderer::getCacheableRenderArray() to encapsulate which data is needed for caching a render array and have views use it further, to unblock this issue.
The patch in #11 that's marked as "do-not-test" should actually not be marked as "do-not-test". There should be a version of the patch that does NOT include #2379741, and THAT patch should be marked "do-not-test". Otherwise, we can't know if this is working or not :)
Comment #14
tom verhaeghe commentedOkay, renamed patch to ensure it's tested.
Comment #16
fabianx commentedThe interdiff looks great on making usage of the new container configuration parameter :).
No idea why the last patch failed testing, but as soon as Wim's issue is in, this one will be simpler to review.
Already RTBC (fingers crossed)
Comment #17
tom verhaeghe commentedOkay, found that I have to add the renderer.config default parameters in core.services.yml, so luckily it didn't have to do anything with Wim's issue.
Comment #18
tom verhaeghe commentedComment #20
tom verhaeghe commentedForgot to add parameter 'debug' in core.services.yml which caused many tests to fail. I added
debug: falsebut I'm not sure if this is the best place to put it there.Comment #21
wim leersReviewed the last interdiff, some nitpicks. But two more important, more fundamental challenges:
When rendered, the child is rendered first, is cached, and "cache miss" debug output is added to its
#markup. No problems so far. Now we render the parent. It's also a cache miss. But its output contains the output of the child, which includes the "cache miss" debug output. This is misleading in two ways:1) cache hits for the parent imply that caching of the child simply doesn't matter, so the render cache debug output for the child should not be there at all, 2) if it's going to be there, it should reflect reality, and reality is that the child has been cached in the mean time. But now that the parent is cached, it's going to say forever that the child is not cached, even though it is, both individually and part as of its parent.
The only mitigating factor is that you could decipher this on your own: HTML is hierarchical as well, so we could argue that when looking at the debug output, you should look at whether there are any parent DOM elements that have debug output, and if so, that output is the relevant output. The child's debug output then indicates whether the parent had to rebuild everything, or was able to reuse cached children or not.
Thoughts?
Comment #22
wim leers#2379741: Add Renderer::getCacheableRenderArray() to encapsulate which data is needed for caching a render array and have views use it finally landed, so now you can rip that part out of this patch; that'll make this patch easier to review and reroll :)
Comment #23
wim leersComment #24
fabianx commented#20 Do you plan to work on this anymore?
Comment #25
tom verhaeghe commentedYup, i'm going to pick this up really soon.
Comment #26
moshe weitzman commentedCrickets ... Anyone up for pushing this along?
Comment #27
fabianx commentedLets unassign Tom for now :)
I agree it is important, it should also be really easy now.
Comment #30
andypostComment #32
wim leersComment #33
wim leersComment #39
chr.fritschThis is a reroll of #20. #21 is still not addressed.
Comment #41
chr.fritschI fixed the failing tests and added a new one to check the debug output.
Regarding #21.2: I don't have a good answer on that. The only thing that came to my mind was changing the children to CACHE-HIT: YES by a regex after the parent was retrieved from the cache.
Comment #43
chr.fritschDamn, patch wrong created...
Comment #44
alexpottThis looks really helpful. I spent some time pondering if this should be merged with twig's debug flag. Because it hard to explain why putting the renderer into debug mode not put twig into debug mode. But I think it is okay to have the two settings because they are different things and having both on at the same time would be very noisy.
Why is
CACHE_HIT:indented and theCache tags:andCache keys:not. Also why is it in CAPS.Comment #45
chr.fritschI adjusted the output to be more in line with the twig debug output.
Comment #47
chr.fritschFixing the test...
Comment #48
geek-merlin#44:
> I spent some time pondering if this should be merged with twig's debug flag. ... But I think it is okay to have the two settings because they are different things and having both on at the same time would be very noisy.
Yes to this.
I also wonder how in the future we relate this to Renderviz ( also by Wim ;-)? First i thought this is renderviz in core but that one dumps the cache metadata (not hit/miss). (Kudos, i really fell in love with it!.) That one also includes Cache Context which this does not (see \Drupal
enderviz\Renderer).
@Wim?
Comment #49
moshe weitzman commentedLooks like all feedback has been addressed. And its been 5 years.
Comment #50
andypost++
Comment #51
larowlanThis looks good to go, can we get a change record please?
Comment #52
geek-merlin> This looks good to go, can we get a change record please?
So this should be NW.
Comment #55
johnwebdev commentedAdded change record. Rerolled and fixed test.
Comment #57
johnwebdev commentedComment #58
krzysztof domański1. Use
assertStringContainsString(). The code will be easier to understand.2. Coding standards.
Comment #59
johnwebdev commentedAdresses #58
Comment #60
krzysztof domańskiThanks @johnwebdev!
If installed Drupal core prior to version 9.1.0 the new option will not be in the
services.ymlfile (copieddefault.services.yml). Then you will see:That should be enough:
Comment #61
krzysztof domańskiLet's add a cache context to the debug output.
Comment #62
johnwebdev commentedAdresses #60 and #61
Comment #63
johnwebdev commentedAh... sorry about that.
Comment #64
krzysztof domański[edited]
Comment #65
krzysztof domański1. I tested manually. Works correctly. Before RTBC let's refactor the test. It will be more readable if we use the heredoc syntax.
2. I discovered two "\n" signs at the beginning of the markup. Is that necessary?
Comment #66
nevergonePlease review.
Comment #67
krzysztof domańskiBack to NW. See #65.
Comment #68
krzysztof domańskiAddressed #65.
Comment #70
anmolgoyal74 commentedComment #71
nevergonePlease test and review, thanks!
Comment #72
krzysztof domańskiComment #73
nevergoneDebug output with pre-bubbling cache datas and rendering time.
Please test, review and help extening test.
Comment #76
nevergoneHow could this be improved?
Comment #78
joachim commented> How could this be improved?
It would be great if we didn't have to repeat parameters in sites/development.services.yml which we're not changing, as that violates DRY. I have to do this:
when all I really mean is this:
But that's out of the scope of this issue, I think. Or not possible at all.
Comment #79
longwave@joachim I think that should be a separate issue, it is surely possible that container parameters could be merged, but it might be tricky when you want to explicitly remove members of an array in an override.
Comment #80
longwaveThis patch has been super helpful in debugging some caching issues on a complex site, and it still applies back on 8.9.x which I am grateful for. Thanks to everyone who has contributed so far!
I think this should be consistent, either RENDER or RENDERER each time.
Comment #81
dhirendra.mishra commentedI have re-rolled it against 9.3.x and updated fix from #80.
Comment #82
longwaveThanks, some coding standards work needed now - these are probably new since the patch was last worked on:
Comment #83
ankithashettyAddressed #82 suggestions, thanks!
Comment #85
tobiasb* Added native typehints
* Fixes the tests
* RENDER -> RENDERER
Comment #87
super_romeo commentedThank you for your work!
I guess that it will be useful to add some ID to
<!-- START RENDERER -->and<!-- END RENDERER -->.To understand where is start and end of each block. And to help searching.
E.g. random int will suit. But better
md5(serialize($element))but it takes more time.Comment #88
luksakThis needs a re-roll.
Comment #89
tobiasb@Lukas von Blarer
And why?
Comment #90
longwave#85 applies cleanly to 9.4.x and 10.0.x for me.
Comment #91
longwaveWe reworded similar sentences in #3166449: Improve wording around twig.cache setting for production environments
I suggest "Enabling render cache debugging is not recommended in production environments."
Otherwise this looks ready to go to me.
Comment #92
ankithashettyJust addressed the change mentioned by @longwave in #91, thanks!
Comment #93
longwaveThanks! As far as I can see there is nothing left to do here.
Comment #94
joachim commentedJust a small error in a comment:
Comment is wrong here, since the TRUE being passed in is $is_cache_hit.
Comment #95
yogeshmpawarAgree with @joachim. Addressed #94
Comment #97
slashrsm commentedI've applied and tested the patch at on of our projects and I can confirm that it works as expected (and it is super helpful too!).
Comment #98
fabianx commentedRTBC + 1
This looks really really good! Thanks for everyone that worked on this over the 8 years.
Let's get this finally in!
Comment #99
alexpottIf there is no
$elements['#markup']then this method is going to have to a load of work for no reason. Should we do an early return if $elements['#markup'] is empty as we have nowhere to attach the markup too... or should we be creating a #markup key/value...This looks very odd... It looks like we'll be losing the Makrup object-ness of $elements['#markup'] is the debug output is added. That is going to cause problems.
Comment #100
slashrsm commentedComment #102
slashrsm commentedComment #103
andypostThis digits makes me think about precision configuration of PHP -
0.0000000123456789Comment #104
slashrsm commentedGreat point. What about setting precision to 9 decimal places (nanoseconds) when generating output? I think that we don't need more than that.
I also made the regular expression a bit more specific to make sure that we are actually matching that line.
Comment #105
andypostThanks 👍 looks ready
Comment #106
longwaveThis didn't make it into 9.5.0-beta1 and so I don't think we are eligible to add it now. Bumping to 10.1.x which is the next place it can land; the patch still applies.
Comment #107
nevergonePlease backport 9.5.0.
Comment #109
catchCommitted/pushed to 10.1.x and cherry-picked to 10.0.x, thanks!
I think we might be OK to backport this to 9.5.x during beta - it's a developer-facing feature and doesn't have any API implications, will get another opinion.
Comment #110
xjmComment #112
catchDiscussed with @xjm and we both think this can be backported. But adding a release note for the changes to default.services.yml
Not sure where we stand on developer-facing features as release highlights, but tagging for 9.5 release highlights in case we want to include it.