Problem/Motivation
Currently entity render caching sets #cache on the render array, but this only allows drupal_render() to cache the actual process of getting the HTML to a string.
We can also use #pre_render and defer the actual building of the bulk of the render array to cache misses as well.
The build and rendering process looks like this (in pseudo UML).
Note how the building of a renderable occurs before drupal_render()
is invoked. This means that the cost of building an entity and its fields is incurred before the cache check occurs. Regardless of whether a cached entry is returned and rendering is avoided, the cost of building the entity and its fields is not avoided.
Proposed resolution
This issue proposes to move the costly building of an entity and its fields to after the cache check. To do this, we will undertake the building in a #pre_render
callback on the entity. The flow looks a bit like this (again in pseudo UML).
User interface changes
None.
API changes
Ideally, module developers will not notice this subtle shift in when the view and build hooks are invoked. To a developer, the build and render flow will appear to be the same.
This is not true for entities that override the EntityViewBuilder::view()
and EntityViewBuilder::viewMultiple()
methods. These methods, when the parent method is invoked, will return a minimal set of properties for the entity that include view mode, language and caching information. Entity classes should only override these methods in order to augment the information returned by EntityViewBuilder
or to replace the pre render optimization strategy. Otherwise, the entity classes may provide an additional pre render function that will be run after the default EntityViewBuilder::buildEntity
and/or EntityViewBuilder::buildEntityMultiple
have run, providing the full entity (and its fields) for manipulation. Even better is to just implement the view and build hook and alter methods for specific changes in the build process.
Once the result of rendering is cached, the EntityViewBuilder::buildEntity
and EntityViewBuilder::buildEntityMultiple
code paths will not be run again until the cache for the entity is invalidated.
The signatures of the following hooks will change:
hook_ENTITY_TYPE_view
hook_entity_view
Before
function hook_entity_view(\Drupal\Core\Entity\Entity $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) {}
After
function hook_entity_view(array &$build, \Drupal\Core\Entity\Entity $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) {}
Fields that reference entities; caching considerations
In order to collect cache tags from an entity, that entity must be run through a View Builder or rendered. In the case of a field that references another entity, such and the entity_reference
field, the formatter's viewElements
method must render the referenced entity in place in order to make its caching information available to the referencing entity. This recursive building would have happened before the changes introduced in this change notice's patch (#2099131). Looking at the viewElements
method of the entity_reference
EntityReferenceEntityFormatter
plugin, we see the entity build and render here:
$referenced_entity_build = entity_view($item->entity, $view_mode, $item->getLangcode());
drupal_render($referenced_entity_build, TRUE);
$elements[$delta] = $referenced_entity_build;
The CommentDefaultFormatter
is another example of a field plugin that must render its referenced entities in order to bubble up cache tag information to the parent entity.
diff --stat test coverage changes
.../Tests/CustomBlockBuildContentTest.php | 41 -----
.../Tests/CustomBlockCacheTagsTest.php | 15 +-
.../Drupal/comment/Tests/CommentCacheTagsTest.php | 17 +-
.../comment/Tests/CommentContentRebuildTest.php | 46 -----
.../Tests/CommentDefaultFormatterCacheTagsTest.php | 124 ++++++++++++++
.../Drupal/datetime/Tests/DateTimeFieldTest.php | 4 +-
.../Tests/EntityReferenceFormatterTest.php | 117 +++++++++++--
.../image/Tests/ImageFieldDefaultImagesTest.php | 16 +-
.../Drupal/image/Tests/ImageThemeFunctionTest.php | 25 ++-
.../lib/Drupal/node/Tests/NodeBuildContentTest.php | 38 -----
.../lib/Drupal/node/Tests/NodeCacheTagsTest.php | 2 +
.../node/Tests/NodeEntityViewModeAlterTest.php | 2 +-
.../lib/Drupal/node/Tests/NodeRSSContentTest.php | 4 +-
.../lib/Drupal/node/Tests/SummaryLengthTest.php | 5 +-
.../lib/Drupal/simpletest/WebTestBase.php | 48 ++++++
.../lib/Drupal/system/Tests/Common/RenderTest.php | 68 ++++----
.../Tests/Entity/EntityCacheTagsTestBase.php | 20 ++-
.../system/Tests/Entity/EntityTranslationTest.php | 9 +-
.../Entity/EntityWithUriCacheTagsTestBase.php | 15 --
.../system/Tests/Theme/TwigDebugMarkupTest.php | 9 +-
.../Drupal/entity_test/EntityTestViewBuilder.php | 12 +-
.../views/Tests/Entity/RowEntityRenderersTest.php | 5 +-
Comment | File | Size | Author |
---|---|---|---|
#216 | pre-render-2099131-216.patch | 145.59 KB | jessebeach |
#216 | interdiff.txt | 145.59 KB | jessebeach |
#211 | interdiff.txt | 7.75 KB | jessebeach |
#211 | pre-render-2099131-211.patch | 145.73 KB | jessebeach |
#201 | interdiff.txt | 1.79 KB | Wim Leers |
Comments
Comment #1
moshe weitzman CreditAttribution: moshe weitzman commentedIf we do this, I'd say that we move only stuff thats slow. If you move °everything*, then modules have nothing to alter after building node an entity display for example. Something to consider anyway.
Comment #2
effulgentsia CreditAttribution: effulgentsia commentedPer #2151459-2: Enable node render caching.
Comment #3
yched CreditAttribution: yched commentedIIRC, #pre_render is also currently used for "pre-theming reorganizations or the render array":
- reordering according to weights configured in Field UI '"Manage display" screen
- setting #access = FALSE on "extra fields" that are configured to be "hidden"
- nesting by field_groups.module...
If we push the generation of the formatters itself to #pre_render too, we'll need to be cautious about the order of execution.
Comment #4
jessebeach CreditAttribution: jessebeach commentedI've put together a first pass at moving field renderable building of an entity into
#pre_render
so that the field renderables are not built on a cache hit.Cold-cache starts are a little worse. Warm-cache starts are a little better. I've performed XHProf runs of cold-cache vs. cold-cache and warm-cached vs. warm-cache.
cold-cache vs. cold-cache
and just
drupal_render
warm-cached vs. warm-cache
and just drupal_render
Comment #5
BerdirThanks for starting this!
Hm, in #2023571: Support preprocessing in EntityViewBuilder, we discussed something like this as being problematic because we are then carrying around the view builder object in the render array, as if that wouldn't already contain enough stuff :)
Not sure how much of a problem it is.
Comment #7
jessebeach CreditAttribution: jessebeach commentedI should explain how I did the performance testing in #4. I set up a minimal installation and the created a content type with every field that core ships with. I created a single node page with content in each field. From that page, I removed the extraneous content from the page.html.twig and html.html.twig files so that only the
page_top
,page_bottom
andcontent
variables were printed. I attempted, as much as possible, to limit the processed content down to the single node.Comment #8
jessebeach CreditAttribution: jessebeach commentedRight, this pattern is not ideal. We could also make
entityViewBuilderPreRender
a static method, but the method refers to$this
, so that isn't an option.We might be able to build a Closure and pass that with
NULL
as the invocation object. I know how I'd do this in JavaScript; it's quite simple. I'm not sure how sophisticated Closure support in PHP 5.4 is. I can give it a shot.Comment #9
BerdirHm. No idea how that closure stuff would work :)
It's a bit ugly, but you could have a a static method that does nothing else but call \Drupal::entityManager()->getViewBuilder($entity_type)->actualMethod().
IMHO doesn't matter that it can't be injected/unit tested, as it's going to be routed through code that will not be unit testable anyway in 8.x and the actual logic still could have unit tests, assuming that there are no function calls left in it.
Comment #10
jessebeach CreditAttribution: jessebeach commentedBerdir, I'm finalizing the patch now, but this is what the Callback stuff looks like. It's working for me locally without issue:
Comment #11
jessebeach CreditAttribution: jessebeach commentedOk, here's the updated patch without the
#pre_render
callback that requires a reference to the class instance. We use an anonymous function that's bound to an object (the class instance) as a callback.Comment #12
yched CreditAttribution: yched commentedlol @ $that = $this;
JS you say ? ;-)
[edit: I thought closures couldn't be serialized though ?]
Comment #13
jessebeach CreditAttribution: jessebeach commentedtimplunkett mentioned serialization of these callbacks as well and now I fear that I'm missing some bigger issue. Why would we need to serialize a
#pre_render
function? This information isn't cached and I don't think we need to transfer it in any way.Comment #14
BerdirWell, we do like to serialize/cache all the things, maybe it could happen in a preview form where the entity is then stored in $form_state (I guess that's cloned first).
We already had all sorts of crazy issues with serialization, I wouldn't be surprised if we're hit here as well.
And yes, not sure how this is better than a direct reference, that still has it, just indirectly, no?
Comment #15
yched CreditAttribution: yched commented@jessebeach: Yeah, sorry, I still easily get confused by the render cache flow. In itself it doesn't serialize render arrays.
'#callback' => array($this, 'methodName') is something that we "try" to avoid on the form side, because $form arrays do get systematically serialized.
Less a problem for non-form render arrays by default, even though :
- as Berdir points, wo do tend to happily serialize lots of things
- it's then a question of setting & encouraging good practices. "Fine in render arrays but problematic in forms" is a bit leaky.
At any rate, agreed with Berdir, the closure approach doesn't seem to bring much over array($this, 'methodName') ?
Comment #16
jessebeach CreditAttribution: jessebeach commentedPosting an intermediate patch so that @benjifisher and I can work on it during the #drupal Global Sprint day.
More refactorings to
EntityViewBuilder::viewMultiple()
.Comment #17
jessebeach CreditAttribution: jessebeach commentedThe latest for benjifisher and me to review before posting a patch for testbot.
Comment #18
jessebeach CreditAttribution: jessebeach commentedThis comment no longer makes sense. We should explain why this is being merged and not assigned.
This approach is still being debated, but I'm leaving it like this for now.
Add a comment that this is where we build the entities field renderables.
Add a comment about how the view hooks should not be allowed to override the default build information here like
#theme
. This behavior maintains current 8.x behavior for view hooks.Comment #19
Berdir2. Yeah, I don't think we should do that :) It doesn't actually change anything, we still have the reference to the view builder in there, just one additional layer of indirection. And while serialization a view builder might be bad, this will fatal error, so that's not better :) (Unless we want it to)
Comment #20
benjifisherThe attached patch adds a few comments as suggested in #18. It also removes the default value from the middle parameter of
getBuildDefaults()
.Testbot, what do you think?
Comment #22
jessebeach CreditAttribution: jessebeach commentedExperimenting with the field-level errors from the test run in #20. The
view
method is being invoked on different fields compared to 8.x infield_invoke_method
. I'm trying to determine what is causing this -- most likely a difference in display and view mode that this patch has introduced.Comment #23
jessebeach CreditAttribution: jessebeach commentedThe test-case I'm working on is a comment preview:
/comment/reply/node/1/comment
The CommentDefaultFormatter is checking the status of the comment, but instead of returning an
int
, the status is returningDrupal\Core\Field\ConfigFieldItemList
as the value of the status property of the field. In fact, all the properties of the field have theDrupal\Core\Field\ConfigFieldItemList
as their value.I can't yet determine where the values of the field are assigned to understand why the status field is getting mis-assigned its value.
Comment #24
jessebeach CreditAttribution: jessebeach commentedComment #25
jessebeach CreditAttribution: jessebeach commentedComment #26
yched CreditAttribution: yched commentedNot able to point to a specific place in the patch that would cause this, but it might be a good idea to wait on #2167267: Remove deprecated field_attach_*_view(), that is going to touche those exact same places.
Comment #28
BerdirThe comment stuff is related to the change in #2151459: Enable node render caching as we've found. As suggested there, instead of the back/forth in the comment preview logic, I would simply clone the commented entity and then change that.
Question: Do we need a new method/something that gives field formatters and other code that would now run too late a chance to add cache keys/tags?
Comment #29
catchFor tags, no:
Cache tags are collected only once the array has been fully rendered on a cache miss, including #pre_render callbacks. So drupal_render_collect_cache_tags() has the chance to find tags added anywhere in that process - including those in the $element returned from a pre_render callback. Similarly if nested elements are cached, we get their cache tags out of the cache entry (i.e. when the pre_render isn't called because it's a cache hit). Same goes for #attached (except for direct drupal_render() calls and the theme system which still break this - but that's not any better or worse here).
For cache keys, it's not possible for nested elements to affect those - since you need to know cache keys both on cache hits and cache misses. The only options there are #post_render_callback so you don't have to change the cache keys at all, or #2099137: Entity/field access and node grants not taken into account with core cache contexts to change them at the top level independently.
Comment #30
BerdirQuickly discussed in IRC, one example of what I'm talking about is comment_entity_view_alter(), which alters the cache key for the pager.
This patch moves that hook into pre_render(), which means that it won't be executed (early enough) anymore. This will need something to handle this, @catch mentioned that it's probably the same hook as we'll need for #2099137: Entity/field access and node grants not taken into account with core cache contexts. I expect that some test failures are related to that.
Comment #31
BerdirRe-roll (not sure if I did everything correctly, but it seems be working). Fixed the comment preview stuff.
Now we have a very interesting problem however. And that's a nested #pre_render call that is adding a #post_render element (comment form). Post render thingies are collected after pre render caches are executed, but only on the top level element. Not sure how to fix that...
We might also be seeing more field instance/formatter related failures now that this is actually affecting the render cache. For example the comment form settings.
Comment #33
martin107 CreditAttribution: martin107 commentedLooking at #31 and I can see a tantalising thread of something .... that I want to unpick
Looking at \Drupal\book\Tests\BookTest first warning message
Undefined variable: url_targetDrupal\simpletest\WebTestBase->clickLink('Printer-friendly version') Drupal\book\Tests\BookTest->testBookExport() Drupal\simpletest\TestBase->run() simpletest_script_run_one_test('46', 'Drupal\book\Tests\BookTest')
undefined variable is code for an unpatched bug report
notice in clickLink if link does not exist() --- https://drupal.org/node/1452896
So the WebTestBase clickLinks() in Drupal\book\Tests\BookTest are suddenly invalid... hmm
Unfortunately book is undergoing rapid changes recently and the warning message no longer reflect the source code so I am asking for a retest before proceeding further,,,
Comment #34
martin107 CreditAttribution: martin107 commented31: pre-render-2099131-31.patch queued for re-testing.
Comment #36
jessebeach CreditAttribution: jessebeach commentedmartin107, it's great to have you looking at this issue with us.
It seems that the links aren't printing on nodes. The missing link passed
clickLink
is a link added inpost_render
processing.Which is probably related to the conflict in processing order that Berdir points out in #31:
Wim and I discussed this conflict a couple months back and we knew this day of reckoning would come after we enabled entity render caching. So, here we are; it's time to untangle the mess!
Comment #37
jessebeach CreditAttribution: jessebeach commentedTo reproduce the links issue, follow these steps:
Comment #38
jessebeach CreditAttribution: jessebeach commentedActually, if you refresh the article a second time in your logged-in session, the links appear. bizarre.
Comment #39
jessebeach CreditAttribution: jessebeach commentedWe're not processing the
<drupal:render-cache-placeholder>
on the first pass after a cache clear.I think I know where to look now.
Comment #40
jessebeach CreditAttribution: jessebeach commentedThis patch separates the one location in
drupal_render
wherepost_render_cache
functions are rolled into two locations: Once at every component in the renderable that will be cached (e.g.isset($elements['#cache'])
) and again at those components that are the top of recursive chains e.g. the "nodes" key that contains a list of nodes to render.The first call to
drupal_render_collect_post_render_cache
at the discovery of a cacheable component is necessary to retrieve the post render cache callbacks before they become hidden behind cache. The second call todrupal_render_collect_post_render_cache
, at the end ofdrupal_render
, is necessary to scoop up any post render cache callbacks that may have been introduced by pre_render functions that build the renderables for child elements.Let's see what the bot says about it.
I expect test fails, but hopefully the bulk of fails that resulted from post-render elements not being printed to the page will be resolved.
Comment #41
jessebeach CreditAttribution: jessebeach commentedMoved the second call to
drupal_render_collect_post_render_cache
to beforedrupal_render_cache_set
to get the Comment module tests to pass. I need to look into why this causes to the tests to pass.Comment #44
BerdirDrupal\comment\Tests\CommentPagerTest is exactly the test fail that I expected to happen based on comments #28-#30.
Comment #45
andypostCommentPagerTest
should be fixed in some issue, please try this hunkEDIT
filed #2210177: Use getSetting() in comment formatterComment #46
webchickCommitted the patch referenced in #45.
Comment #47
andypost41: pre-render-2099131-41.patch queued for re-testing.
Comment #48
jessebeach CreditAttribution: jessebeach commentedPosting to see what the bot says. This patch resolves the comment pager test fails and perhaps others.
It introduces
hook_entity_defaults_alter
. It's still rough. If it resolves more test failures, I'll post another comment describing the changes in great detail and my concerns with them.Comment #53
jessebeach CreditAttribution: jessebeach commentedStill dirty, but I'm moving along resolving test failures. Once I get everything green, we'll do some performance analysis and decide if the gains are enough to warrant the refactoring that will be necessary to get these changes in for realz
Comment #55
jessebeach CreditAttribution: jessebeach commented"639 fails", hmmm, 4 steps forward, 635 steps back. I think I know what I did to roll out all these fails. I'll fix it in the morning.
Comment #56
Berdir$entityType seems unnecessary and strangely initialized, could just as well use $this->entityTypeId directly below?
Hm, you're just moving this, but ->format should really live in the text formatters and ->entity in EntityReferenceFormatterBase.
Then you can also make it unconditional.
The latter is interesting, because you can see that the function there filters out non-accessible references for the user, so wondering if we should only add tags for that, but that would be pretty expensive, as we need to load them there then.
Also, looks like you're not moving the array initialization above in view() won't that unset the cache tags again there?
Comment #57
jessebeach CreditAttribution: jessebeach commentedYes, something like that is happening. I think that's what caused the flood of fails. I'm debugging now.
Noted about
format
andentity
.Comment #58
jessebeach CreditAttribution: jessebeach commentedAgain, very dirty patch. I fully plan to take all these changes into consideration in totality once I've at least gotten the tests to pass. Plus I'm learning these sub-systems as I go, so the changes lack a certain finesse.
Hopefully this patch gets the errors back into the "couple dozen" range.
Comment #59
jessebeach CreditAttribution: jessebeach commentedThe interdiff to #58.
Comment #60
jessebeach CreditAttribution: jessebeach commentedsorry for all the noise :/
Comment #62
jessebeach CreditAttribution: jessebeach commentedAnother go at a full round of testing.
Comment #64
martin107 CreditAttribution: martin107 commentedReroll
Comment #65
martin107 CreditAttribution: martin107 commentedtriggering testbot
Comment #67
martin107 CreditAttribution: martin107 commentedhmm
#58 - l 138fails, 234 exceptions
#64 969 fail(s), and 62 exception(s).
not sure how much of that is jessebeach's change in #62 or my reroll... I will start looking into my changes and try to find any obvious gotchas.
looking at the diff between the #62 and #64
2 changes A and B
A)
B)
B - seems like the right thing to do .. not sure about A
Comment #68
jessebeach CreditAttribution: jessebeach commentedWhat about A is setting off alarms for you?
I'm digging into these fails, too. This is such a whack-a-mole game. I fix one test and break 100 more.
Comment #69
martin107 CreditAttribution: martin107 commentedOk A and B are the only changes in reroll.... I did not want to give you a tricky to fix problem when you are analysing a hard problem.
I'm at the end of my day here... I will look at test fails from a different perspective tomorrow..
book is failing again ... your insight of creating a manual test was good ... I will start there.
Comment #70
jessebeach CreditAttribution: jessebeach commentedThe spike in errors has to do with the
<drupal:render-cache-placeholder />
element not being replaced on the first pass. I introduced this regression in the patch to fix the fails inRenderTest
. So, I just need to makeRenderTest
andBookTest::testBookExport()
pass and we should then be making progress again.Comment #71
jessebeach CreditAttribution: jessebeach commentedGetting
RenderTest
andBookTest::testBookExport()
to pass just about break my brain. I think I tried about 40 different approaches and walked through a debugger for several hours before landing on the following solution:1. Collect all
post_render_cache
entries before the theming starts for anything that has a#cache
property or the root of a recursive chain call todrupal_render
.2. After theming, make one more effort to collect
post_render_cache
tags on the root of a recursive chain if the first attempt turned up empty (because its a list of nodes that havepre_render
functions that hadn't run yet).So, let's see if there's a third case now that fails because these two are resolved =D
Comment #73
jessebeach CreditAttribution: jessebeach commentedAfter a few more hours banging my head on the remaining test failures, I've come to the conclusion that we cannot use
#pre_render
functions to delay the building of renderables until after a cache-check. The current behavior of drupal post render cache requires that the complete renderable be available to traverse either when a renderable element has a#cache
property or when the renderable is the root of a renderable tree. By delaying tree node building until subsequent recursive calls todrupal_render
, we subvert the drupal post render cache's collecting of post render renderables.I have found no way to re-process an element to collection post render cache renderables without duplicating post render information or failing to render a post render cache renderable, thus output the
drupal>
render element to the DOM.The only way I can think to solve this is to process the complete build of the view on the first call to
drupal_render
, producing the same behavior we had before, with the complete build step just slightly delayed until after a cache check.I've thrown together a rough mockup of the concept and profiled it. Everything but number of function calls improves.
I profiled a normal Article node with tags, an image and a couple comments (one awaiting moderation approval). The performance gains are good enough on first glance to warrant consideration. But I don't want to pursue this strategy without gathering opinions about it.
It's still going to need more work if we decide to move forward. As of now, it pass
RenderTest
,CommentAdminTest
andBookTest
: the three tests that I've only ever been able to get 2 of which to pass at any one point in developing on this issue.Comment #75
jessebeach CreditAttribution: jessebeach commentedThe test failures in #73 are the result of too many function calls. We cannot attempt to build out the complete renderable tree in
drupal_render
because it leeds to too many recursive function calls. Controllers know how to build a renderable;drupal_render
is too generic a function to know (at least at the moment) how to build a specific renderable (e.g. block, page, entity, View).I am completely out of ideas for how to proceed and frankly I'm not convinced that this issue is really critical. The performance gains are theoretical since we can't get a patch to green. And there are other layers of caching in Drupal that alleviate the cost of building a renderable.
I'd like to propose that we keep this issue Critical for now, but remove the beta blocker designation. I'll bring up this issue in Szeged when there will be more brain power available to puzzle it out.
Comment #76
jessebeach CreditAttribution: jessebeach commentedRemoving the beta blocker tag.
I am not convinced that this proposed change will give us performance increase that will justify the complexity we'll have to introduce.
Both the original issue summary and the first comment by Moshe concede that the proposed change would be tricky. After spending several weeks on this issue, I'm inclined to agree. I don't think this issue should block a beta release.
Comment #77
jessebeach CreditAttribution: jessebeach commentedA less memory intensive version of #73, inspired by #2215719: Cache tags set by #pre_render callbacks are lost in page cache.
Comment #79
jessebeach CreditAttribution: jessebeach commented77: pre-render-2099131-77.patch queued for re-testing.
Comment #81
jessebeach CreditAttribution: jessebeach commented77: pre-render-2099131-77.patch queued for re-testing.
Comment #83
jessebeach CreditAttribution: jessebeach commentedPulling 8.x and re-rolling, just in case that has anything to do with the login failures.
Comment #85
jessebeach CreditAttribution: jessebeach commented83: pre-render-2099131-84.patch queued for re-testing.
Comment #87
catchStarted having a look at this. Haven't got anywhere yet but here's a re-roll.
Comment #88
catchGetting an up-to-date test bot run.
Comment #90
catchStill lots to do but here's an interim patch. I hit xdebug max function nesting level, but setting it to 500 I got 100% pass on the comment links test, and a bit of manual testing of node links was OK.
Comment #91
jessebeach CreditAttribution: jessebeach commentedCatch, can you post an interdiff for #90?
Comment #92
catchAnd the interdiff.
The main change is removing support for #type => 'render_cache_placeholder'. This makes it impossible to reliably collect #post_render_cache callbacks because you have to render the placeholder to get the callback set.
Instead I'm converting the cases that use this to just use #post_render_cache directly - that removes the chicken/egg situation and seems to be working.
Comment #93
catchMore or less completely removed support for #type => 'render_cache_placeholder' and the tests supporting it. That hopefully means we'll be down to real test failures / failures due to stuff that's not converted yet.
Also getting RenderTest to passing. I removed assertions checking that the element itself can't be altered. While we only care about #attached and #markup changes, I'm not convinced it matters if the element can or cannot actually be changed?
Comment #95
jessebeach CreditAttribution: jessebeach commentedSo an element that will be replaced in post render cache is responsible for generating a placeholder as its #markup.
And then responsible for replacing the placeholder after caches have been set.
Having tried 101 ways to get pre render to work within the recursive drupal render pipeline, I'm encouraged by this approach. It does place the onus of setup and processing on the developer...BUT, post render cache items will be rare and even the established system is sufficiently complex to grok. Good docs can support this more manual approach.
Comment #97
jessebeach CreditAttribution: jessebeach commentedAdded the related issue #2227383: EntityViewBuilderInterface::view() should accept an EntityViewDisplay, not only a view mode ID to deal with wonky comment preview code.
Comment #98
catchFor now here's a brute force removal of that code. The long comment in the interdiff explains what's happening. Dynamically setting a field value on an entity, rendering it, then setting it back is not going to work with entity caching at all. So we really need the real view mode and/or proper previews.
One other thought that occurred to me - this is only an issue if the comment form is shown on the same page as the entity, and the entity gets rendered on the comment reply form. In that case the only way to get to this page is to manually navigate it, so we could just skip it for that case maybe and leave if the form won't be shown.
Comment #99
catchCNR for the bot.
Comment #100
catchFixes RowEntityRenderersTest.
Comment #103
catchFixes NodeTitle test - just the comment reply behaviour change.
Should be green this time.
Comment #104
jessebeach CreditAttribution: jessebeach commentedSo, big picture, patches #87 through #103 do the following:
1. Remove the
render_cache_placeholder
type.2. Move placeholder rendering into build method of an element (entity/field/etc).
3. Move post render element building/rendering and placeholder replacement into the
#post_render_cache
callback registered on the initial build array of an element.4. Alters and deletes some tests. I'm going back into see if these should be restored or reinstated, especially
testDrupalRenderChildrenPostRenderCache
.Overall, I like the approach.
WimLeers, we definitely need your input here.
Comment #105
Wim LeersAWESOME work, Jesse & catch! I can't quite believe you've actually gotten it green! :)
Now let's get all tests undeleted and green again :)
I focused specifically on the
#post_render_cache
stuff, but I also reviewed the other changes at the same time.#90/#92:
Why?
This will "infect" a parent element that has
#cache
set.(Also see later, in the review of #103.)
#98:
Great find!
#103:
Why not keep this comment? It's still accurate?
This is where that comment belongs.
More importantly, this move introduces a subtle change:
#post_render_cache
callbacks are collected after#post_render</code callbacks run. However, that shouldn't matter: <code>#post_render
callbacks work on the final output, and cannot introduce additional#post_render_cache
callbacks. So it's fine.I just want us to be aware that this is the case.
Why is this change necessary?
There's a few trailing commas here that shouldn't be there :)
$context
is uninitialized, but should contain the langcode.Since there are no test fails, we have no test coverage for this hook…
I'm also not sure there's much value in this. And it's in the critical path for rendering entities. So maybe we should remove it?
This is no longer necessary here, can/should be moved up.
This whole
$entity->content
thing seems really bizarre. First we fill$entity->content
, then we callbuildContent()
on it which also has to do strange things with it. Then we futz it back to$build
, ***unset*** it, and invokealterBuild()
with$build
.AFAICT we can just always use
$build
? :)Dead code.
It feels like this should be
getCacheTags()
. There's no reason for this to be in the form of a render array: it's just unnecessary wrapping; it makes it impossible to use for other types of caching than render caching.FormatterInterface
should probably extendCacheableInterface
.Why set these defaults? It used to match
EntityViewBuilderInterface::getBuildDefaults()
.Let's put this in a helper function? :)
I'm confused by this: I don't understand why we're setting
$entity->content
. But apparently it may cause some fields to be rendered already, and others not just yet. Especially because we're still generating each formatted field and the third quoted line is just adding those keys that aren't already rendered… which I don't understand at all.This feels unrelated?
Why remove this? It implies that it is now possible to do this…?
We must keep this one but rename it to
testDrupalPostRenderCacheWithPlaceholder
. This test will no longer use#type = render_cache_placeholder
, but instead it will have to generate its own placeholder (using the helper method).We most definitely cannot remove this. It was added to cover an important case that was breaking node render caching.
See #2151459-37: Enable node render caching & #2151459-38: Enable node render caching.
We need to get these back now that the patch is green :)
Comment #106
jessebeach CreditAttribution: jessebeach commentedI've put the following tests back:
testDrupalRenderRenderCachePlaceholder
testDrupalRenderChildElementRenderCachePlaceholder
Comment #107
catchThanks for putting the tests back Jesse!
@Wim, thanks for the review. I've fixed the easy stuff, commented on some I don't think we should tackle here and/or need a bit more of a look.
#1. Fixed.
#2. Yes I think that's fine, exactly where it should go I went back and forth on, but 'near the end', seemed good :)
#3. I don't know, will ask Jesse and/or try removing in a side issue to see what happens.
#4. Fixed.
#5. node_test_entity_view_mode_alter() implements it. I fixed the argument for now.
#6. Fixed.
#7. It is bizarre, but it was already here. It would be fantastic to just use $build here, but let's open a separate issue to clean just that up.
#8. Fixed.
#9. Reading through I'm not convinced we need this code, will try removing in a helper issue and see what breaks.
#10. Jesse did this to make things consistent with code elsewhere. Leaving in for now but we could possibly revert that change and handle it somewhere else.
#11. Fixed.
#12. per #7 I think we should have a dedicated issue to deal with
$entity->content
#13/#17 will look at those in a bit.
#14. Yes it's possible to do that now, but it won't have any practical difference. I'm not sure we can absolutely lock it down or need to particularly.
Comment #108
catchGahhh wrong patch uploaded.
Comment #109
catchComment #111
catchFixing some silliness.
Comment #113
catch111: 2099131-110.patch queued for re-testing.
Comment #114
Wim Leers#108:
2. Heh :)
7. Ok. I just fear that by not cleaning it up now, we break things in subtle ways.
9. Yeah, good point. Cache tag bubbling should already take care of this. I think this is a remnant of Jesse's earlier attempts to work around the problems she was having.
10. Confirmed — it's fine — sorry for the noise!
12. Sure, we can do clean-up in another issue, but this code did not exist before…
14. Thanks to the move you did (point 2), that's indeed true now, but before you could set
#theme
for example, or unset#cache
or whatnot.Comment #115
catchNew patch. I've removed FormatterBase::getDefaults() because it shouldn't know about the specific field types. Added cache tags to the render array returned by the formatter plugins instead. Note that the full entity reference formatter plugin doesn't need to explicitly add tags, because the referenced entity rendering handles that itself.
I think this addresses everything in Wim's review now except the $entity->content stuff. I tried removing one of the changes there, but it broke horribly - pretty sure it's because we add the defaults before it gets there. We'd have to add the all again if we went back to completely clearing out $entity->content again. Jesse do you remember how you ended up with those changes?
Cache tags tests pass locally, hopefully I caught them all.
Comment #116
catchComment #117
yched CreditAttribution: yched commentedWoah, dunno when that code got added, but happy to see it go away :-)
$addition[$field_name] looks wrong though, keying the render array by field name is supposed to be the job of the formatter's caller now.
For the record, I've been wondering about why TF we bother with $entity->content as well, and would love to see it die in flames too - here or in a separate issue :-)
Comment #118
Wim Leers#117: It's from this patch, it's not in core :)
Maybe I'll try to get the
$entity->content
->$build
thing done. I want to see it die miserably.Comment #120
catchShould fix the test failures...
Comment #122
catchMissed one apparently.
Comment #123
catchOK new patch with Berdir's fix from #30 for the comments, which I missed. There's now no regression in comment preview for the entity reviews, so this is mostly a revert of those changes.
Also did some profiling, saves 15% of function calls when viewing an article node on the standard profile with a handful of tags and comments. The main saving here is that when viewing the node, we don't have to load any comments or taxonomy terms at all since that only happens on cache misses now.
Would be worth some profiling with a much more loaded node or more fields on comments and/or a longer list of nodes, but this is a really nice improvement.
Comment #124
effulgentsia CreditAttribution: effulgentsia commentedComment is wrong. It's assigned as a #pre_render. Given that, why not name the function
preRender()
? I don't think prefixing the method name with class name as buying us anything.Per #117, this should not be necessary.
Comment #125
effulgentsia CreditAttribution: effulgentsia commentedPer #1977266-43: Fix ContentEntityBase::__get() to not return by reference, I'd also love to see us remove
$entity->content
entirely. I think that will require changing the API of hook_entity_view() and hook_ENTITY_TYPE_view() though. I don't see that as in scope for this issue, but if others think it is, I'll be ok with that. Either way, a huge +1 to doing it, whether in this issue or not.Comment #126
catchI made a start on $entity->content in #2228043-25: Helper for #2099131 but it's very far from passing. I don't mind if we do it here, but also wouldn't want to hold this issue up on that.
Comment #127
catch#124 yeah I think the #build is a legacy from a previous patch, just needs the comment fixed and re-naming. I'll try to re-roll soonish and see if we can revert the other change.
There's not much of an explicit API change here, but it does affect what you get back if you call entity_view() very significantly. Given the 16k function call saving and the fact this is passing now, moving back to beta blocker.
Comment #128
Wim LeersI've been working in #2228043, continuing where catch left off, getting rid of
$entity->content
. It's now green :)This identical to #2228043-36: Helper for #2099131, and should therefore also be green. I'll be working on a follow-up reroll that also cleans up some loose ends.
Note: the attached interdiff is against #2228043-23: Helper for #2099131, i.e. catch's work since #123. I've also created an interdiff for his work, so that only reading the interdiffs allows you to see all changes.
Comment #129
Wim Leers<entity type>BuildContentTest
tests are now obsolete, so deleting those.$replace
/$markup
/@todo
that catch added.CommentCacheTagsTest
caused it to verify that a nonsensicalfilter_format:
cache tag was present. The cause: apparently one can create a comment with just'comment_body' => 'The comment text.'
or even'comment_body' => array('value' => 'The comment text.')
, rather than the proper'comment_body' => array('value' => 'The comment text.' , 'format' => 'plain_text')
. In the first two examples,$item->format === NULL
, which causesfilter_format:
to be generated as a cache tag. i.e. either this is a bug in the Comment entity, or it's a explicitly allowed yet bizarre feature of it. In any case, I fixed that.Crypt::randomBytesBase64()
with calls todrupal_render_cache_generate_token()
.One more reroll coming up that makes
EntityViewBuilder
quite a bit less crufty, but might break things. Then I'll unassign.Comment #130
Wim LeersAs said, making
EntityViewBuilder
less crufty.Also moving back
RenderTest::testDrupalRenderRenderCachePlaceholder()
andRenderTest::testDrupalRenderChildElementRenderCachePlaceholder()
to their original locations, their unnecessary relocation within the same file makes the patch more difficult to review. (And in doing so, I found two lingeringdebug()
calls :)) That reduced the patch size by a nice 18 KB :)Review
WebTestBase::drupalBuildView()
is too ambiguous. What about::drupalViewEntity()
?CommentViewBuilder
:has become obsolete now. Because
CommentViewBuilder
usesEntityViewBuilder::viewMultiple()
, the default render strategy kicks in, which is to use#pre_render
. If we use#pre_render
, thenbuildContent()
is always called on a per-entity basis. Now, because this code will run less often (right now it still runs on every render cache hit!), it may not be necessary anymore.If we are certain that we want to keep this, because comments are usually rendered many-at-a-time, then we could optimize
CommentViewBuilder::viewMultiple()
for that: instead of a#pre_render
callback per entity (i.e. per comment), we could make a single#pre_render
callback for all comments, which then could again callbuildContent()
with all comments at once. This would make a cache miss faster.Comment #131
rbayliss CreditAttribution: rbayliss commentedTaking a look at this as part of the NYCCamp sprint.
Comment #132
rbayliss CreditAttribution: rbayliss commentedTook some time to look this over. It makes sense. I have to say that the use case where you need to add an uncacheable element to an entity is very confusing, but I don't think improving DX for that should block this issue. One nitpick:
Should probably be $this->entityTypeId.
Comment #133
jessebeach CreditAttribution: jessebeach commentedI'd like leave the word "build" in the method name, since it is building. So I've renamed it to
drupalBuildEntityView
. This method was also missing docs. I added them.I'm still mentally processing Review comments #2 and #3 from comment #130.
Comment #134
jessebeach CreditAttribution: jessebeach commentedHere are some numbers to put behind this issue.
TESTING CONTEXT: standard article node with an image and tags. The node has 30 comments. xdebug is turned off and xhprof is running with the single flag
XHPROF_FLAGS_MEMORY
.I ran a general performance analysis for this patch, warm-vs-warm cache; I took five runs of each condition and then selected the fastest of those five for each. I ended up with a 77ms overall improvement:
I then compared cold-vs-cold cache, 8.x vs. 8.x-patched using the same 5 runs methods. Focusing in on the
Drupal\comment\CommentViewBuilder::buildContent
method, we find a 19ms overall deterioration for the request.The cold cache decrease in performance of 19ms is only a 0.5% increase in total request time because the request itself, on a cold cache, takes 6.3 times longer to process than a warm cache request. So 19ms is far less of a increase in total processing time on the slow request than the 77ms ends up being in a relatively quick request time. So, we can compare the percentages here. We need to compare the milliseconds.
Personally, I'm willing to hive off the refactor of
CommentViewBuilder
's implementation of#pre_render
to a major and take the 19ms hit on a cold cache so that we get the bigger refactor in now, establish the pattern in core and get a bigger "fast by default" win.Comment #135
jessebeach CreditAttribution: jessebeach commentedre #130:3, The solution to the hack in comment module to render a the entity that a comment preview is associated with is to simply not render the entity. The code that did this is removed in the patch:
I am fine with this change. This is a minimally viable solution and we have an issue to improve the UI: #2227383: EntityViewBuilderInterface::view() should accept an EntityViewDisplay, not only a view mode ID. Any improvement to this view will be introduced as an optional view mode, so existing sites will have the chance to enhance the comment preview page by selecting a new view mode for it.
rbayliss and I did a thorough review of the code in NYCCamp.
So in my opinion, the only blocking issue here is the performance regression for cold cache requests on pages that have numerous comments. In the service of getting a beta blocker resolved, I suggest we note the regression in an issue, take the overall improvement now, and get the architectural improvement in place.
I'll start writing up the change notice!
Comment #136
catchIt looks like the latest patch might have been based off #122 instead of #123? Either way I got the comment preview issue 'fixed' in #123 without a UI regression via berdir's suggested change from #30 (which we'd overlooked until he pointed it out again in Szeged). See the interdiff https://drupal.org/files/issues/interdiff_3373.txt- hopefully just applying that interdiff will be enough.
Having #pre_render work on multiple for the cold cache issue would be great. I tried this a bit with performance_hacks but couldn't find a clean solution (hence the name of that module). The problem I ran into is that the cache get on the render cache is single - so to know which items you want to do multiple operations on, you need to be able to operate only on those items that are cache misses, but when rendering an individual item, it doesn't know if it's part of a list or not. What it came down to was 1. just take the hit on misses 2. Do some multiple logic on all items, even if they're all cache hits - which means a performance regression on hits 3. Nasty hack to enable running the multiple logic on cache miss items only. It's definitely a problem though so good if we can resolve it.
Comment #137
jessebeach CreditAttribution: jessebeach commentedI'll sort this out and post the patch soon.
This is what I ran into last night...just when I thought I had it! I'm experimenting with some ideas to do multiple item building in the pre_render/cache-miss cycle.
Comment #138
jessebeach CreditAttribution: jessebeach commentedThis is the work since #133 with the interdiff from #123 applied.
I'm continuing to work on the multiple/single building issue.
There's also a refactor of an anonymous function into a method.
Comment #139
jessebeach CreditAttribution: jessebeach commentedSetting to Needs Work since that more accurately represents the state of the patch.
Comment #140
jessebeach CreditAttribution: jessebeach commentedI think I have multiple build working along side single build. I ran
RenderTest
locally, which is, historically, a good canary.Please review the approach. I've got some code duplication and docs to write if this comes back green or close to it.
Comment #142
jessebeach CreditAttribution: jessebeach commentedWeird, I merged in changes from 8.x locally and didn't run into any conflicts. No interdiff; chasing HEAD.
Comment #144
jessebeach CreditAttribution: jessebeach commentedThis should sort the failing tests.
Comment #146
jessebeach CreditAttribution: jessebeach commentedThe Drop is moving fast today. Chasing HEAD, no interdiff.
Comment #147
jessebeach CreditAttribution: jessebeach commentedCode cleanup and consolidation.
Comment #148
jessebeach CreditAttribution: jessebeach commentedSome performance numbers.
First case: 8.x to 8.x-patched, warm cache to warm cache, article node with 10 multi-value fields added (various default fields). The article has 50 comments on it.
We get a 50ms gain in this case.
Second case: 8.x to 8.x-patched, warm cache to warm cache, article node with 10 multi-value fields added (various default fields).
The overall gain is 44ms, which is consistent with the first case, given that this node does not have comments to render, so it should built a little faster.
Third case: 8.x to 8.x-patched, cold cache to cold cache, article node with 10 multi-value fields added (various default fields). The article has 50 comments on it.
In this case, we're specifically looking at
CommentViewBuilder::buildContent()
, which had been building comment entities one at a time (rather than in multiples) in #138 and before. Now entities are build in multiples, when possible, in the new pre render functions.We get a speed up in processing time for the cost of a little more memory usage. There is now no regression in time vs. 8.x HEAD.
Comment #149
jessebeach CreditAttribution: jessebeach commented#147 came back green. All of Wim's concerns from #130 have been addressed.
The latest patch needs a review from someone who isn't me, but as far as I'm concerned, this patch is ready to go. It gives us a huge performance win.
Assigning to catch.
Comment #150
yched CreditAttribution: yched commentedSo getBuildDefaults() is called with a $view_mode, alters it first thing, and calls getBuildCacheDefaults() with the altered $view_mode. Feels potentially confusing, plus altering the view mode doesn't sound like something you'd expect from a method named getBuildDefaults().
I'd think the alter would be better off done by the caller of getBuildDefaults() instead - i.e. the foreach($entities) loop in viewMultiple().
Sorry if this has been discussed earlier, but EntityViewBuilder::entityViewBuilderFoo() ?
Why would two random methods in the class be prefixed by the class name itself ?
Also: "single" cannot be treated as a degenerate case of "multiple" anymore ? It's a bit sad to have to maintain those two separate code paths :-/
Also: nitpick - methods order in the class does not really reflect code execution, makes it a bit hard to get a real mental view of the process. Reordering might make the reviews harder though, so maybe a followup ?
Looks out of place in a method named callViewHooks() :-)
(if there was a way to re-unify the single / multiple code paths, we wouldn't have to find a name for the method that does the shared bits ?)
unused, says PHPstorm.
missing space after foreach :-)
Woah, that doesn't really help clarifying the code flow :-)
So that's basically all viewMultiple() does now - "call getBuildDefaults(), that'it, done until #pre_render if cache miss"
In that case, the method name is now a bit misleading. it's not about "defaults" anymore, it's the actual, "final" (before #pre_render) build.
Similarly, buildContent(), that does the actual rendering at #pre_render time, is now misleading too.
It would help understanding the new (somewhat complex :-p) flow if method names clearly relfected at which step they kick in.
(hm, this being said, EntityViewDisplay::buildMultiple() is now kicked out of the 'build" phase into the "pre render" phase - a bit unfortunate - well, let's say at least method names within EVB could be internally consistent with the new split ?)
Aw THANK YOU :-)
Not sure why those changes are needed ?
getBuildDefaults() is an internal, protected method, so calling code should take care of providing defaults ?
I don't get that change.
In current HEAD, $formatter->view() returns a build array that is *not* wrapped in a $field_name key, it's EVDisplay::buildMultiple that sets $build[$key][$field_name] = the result of the formatter.
So, not sure why we're using $build_field[$field_name], nor why the (isset($build[$key][$field_name]) check is needed (I can't see why it would be set) ?
Hmm - matter of taste, but I find the current shape easier to parse :-) We get inline anonymous functions in PHP now, why not use them in straightforward cases like this ?
At any rate, this is not really related ?
Is that really needed ? This prints a numeric ID, it won't change even if the entity itself changes ?
(aw - is that for the case where the referenced entity gets deleted ? Maybe worth a comment then ?)
Comment #151
xjmComment #152
catchDid you profile a warm cache between #138 and the latest patch? Reviewing visually I'd expect the the multiple #pre_render to always run, therefore a performance regression in the cached case. If it doesn't it'd be good to document how that doesn't happen.
Comment #153
sunComment #154
jessebeach CreditAttribution: jessebeach commentedyched, great review! I'm putting together a response to your questions/points.
catch, #148, cases one and two are both warm-cache tests. The multiple build #pre_render is only called when
EVB::viewMultiple()
is called outside of a call toEVB::view()
. The#pre_render
key is dropped here on a single entity view call.I'll explain more in my forthcoming response to yched's review.
Comment #155
catchI understand it's only called for listings, but isn't called both when there's both a warm and cold cache?
Comment #156
jessebeach CreditAttribution: jessebeach commentedI can't find a case in core where we render a list of things that isn't also a View. And Views renders lists of things on a row-level, meaning as singles. For comments, those get cached, as a field, on their entity, so we don't build them again after the first time.
Yes, we might have a situation where entities are passed, as a set, to
EVB:viewMultiple()
and each individually item might have a cache entry. In that case, we'd end up building each individual item (same as 8.x HEAD). The single-item pre_render is removed if it's built in the multiple-item pre_render, so we never build twice. In this case (that I can't reproduce in core), we'd end up building the renderable and then getting the cache entry...same as 8.x now. So, my patch from #138 optimizes for nodes with long lists of comments, eliminating the regression for comment building. BUT, you're right, it does open up the possibility for a performance regression if a developer renders a list of entities outside of a View. But I can't think of anywhere in core where we do this that I can use as a test case!Maybe we could optimize at the multiple level with a cache entry that lists all of the entity IDs in the list? And if one of them gets invalidated, we can build that one again, but skip the rest?
Comment #157
jessebeach CreditAttribution: jessebeach commentedcatch, alternatively, I can move this single/multiple pre_render strategy to
CommentViewBuilder
and restore the single pre_render strategy toEntityViewBuilder
. That'll give us same gains pre-#138 and resolve the multiple comment building regression noted in #130. These strategies are deep enough in the builder classes that we can continue to optimize post release. And what I'd like to see in this issue is that we lock in a solid win.Comment #158
catchOK here's a scenario in core/views. Agreed though, when the full list gets render cached as a single chunk of HTML, this is significantly less of a problem anyway.
Node gets rendered with 50 comments.
One comment gets updated - this invalidates both the render cache item for the node, and for that comment. The other 49 comments stay the same.
Node gets rendered again, now we have 49 comments render cached, and one that isn't.
There's two trade-offs:
1. On the full cache miss. We'll either render all the items individually (regression), or multiple build.
2. On the partial cache miss, we'll either render only the items that need it, or multiple build all of them to render one or two (not a regression compared to HEAD but a regression compared to previous patches).
Same situation for views listings.
The only way to completely avoid the trade-offs would be check the cache entries for the child render arrays first, then do the multiple pre-render on the remaining items, which we don't have a mechanism for.
I think it's fine for this issue to go in with either approach (apart from resolving yched's review), and we should open a major follow-up for the single/multiple stuff to figure it out more.
Also I'd stick with doing this on the main EntityViewBuilder - whatever we do it makes sense to do that consistently across all entity types.
Comment #159
jessebeach CreditAttribution: jessebeach commentedIt took me a couple hours to respond to this :) Thank you for the review yched!
#150:1
Agreed, I've made this change.
#150:2-1
I've changed the method names to
buildEntity
andbuildEntityMultiple
, in parallel withview
,viewMultiple
andbuildContent
.#150:2-2
Good point.
EVB:view()
andEVB:viewMultiple()
is the pattern we're trying to emulate and delay here, so I've done that in the build functions as well.buildEntity
now works likeview
in that it packages an array, sends it tobuildEntityMultiple
and then returns the zeroeth item from the array to the calling function.#150:2-3
Let's just do that now with the new methods.
#150:3
Yes, totally. Great suggestion. Done :)
#150:4 - Removed.
#150:5 - space added.
#150:6
Added more commentary.
#150:7
in my understanding, these are defaults for cache checking and, if needed, kicking off a full build on a cache-miss. The bare minimum set of information we need to know about a renderable to make a decision to get a cache entry or proceed with building and some info about the view mode, langcode and theme.
I agree that
view
andviewMultiple
are a bit misleading now, as evidenced by the methodWebTestBase:drupalBuildEntityView
, which performs a mix of build and render to get the full renderable for an entity. I think this complexity is the current cost of a render process (drupal_render()
) that has procedural build steps and render steps baked into it.This is the way that
EntityViewBuilder
implementsEntityViewBuilderInterface
in order to optimize for performance. I think outside of this class, those method names make sense given how another class of this interface might choose to interpret whatview
andviewMultiple
should do.I'm definitely holding my nose a little here for the sake of faster cache-hit requests. Is it a little better with the pre_render method renames and refactoring the method order?
#150:8 - ya!
#150:9
Agreed, and
\Drupal\Core\Entity\EntityViewBuilder
doesn't include these defaults in its signature. I've pulled them out of the sub class method signatures.#150:10
Right, we must have started this work while the fields were still keyed by field name. I've updated the code.
#150:11
The anonymous function caused a minor performance regression (~3.5ms) because it gets created thousands of times. I was looking at this with msonnabaum. We were able to eliminate the regression by defining a method and passing it as a callback.
#150:12
catch, I think you added this code. Can you speak to this?
Comment #161
catchThe cache tags for the ID formatter I added because the new entity cache tags tests were failing. Also yes we need them in real life due to deletions.
Comment #162
jessebeach CreditAttribution: jessebeach commentedThis should resolve the failing tests. I neglected to reference the entity
$key
on the$build
array passed to theview
hook implementations during the build step.I also noticed that the
$referenced_entity
variable in theEntityReferenceidFormatter
is not assigned an initial value and when this code path is followed, results in a whitescreen. Wondering how that would have passed tests, I learned that we do not have any test coverage for this code. Indeed, none of the entity reference formatters have test coverage for their view methods. So I've added test coverage for the code this issue changes. I logged an issue to add coverage for the other formatters. I'd rather not take on that work in this issue, which is already quite large.#2246655: Expand the entity reference field formatter test coverage
Comment #163
jessebeach CreditAttribution: jessebeach commentedComment #164
jessebeach CreditAttribution: jessebeach commentedComment #165
jessebeach CreditAttribution: jessebeach commentedComment #166
Wim LeersGreat progress here!
In this reroll:
::buildEntityMultiple()
and::buildEntity()
#pre_render
callbacks unnecessarily complex. Having one#pre_render
callback unset another feels … dirty. We can make it much simpler. Instead of setting::buildEntity()
ingetBuildDefaults()
, we just only set it in::view()
, just like::buildEntityMultiple()
is set by::viewMultiple()
.This works fine in theory, however… Views calls
::viewMultiple()
, but then extracts individual entities… which breaks this. However, there is no performance advantage to calling::viewMultiple()
, but then still callingdrupal_render()
on individual entities (this still triggered::buildEntity()
, not::buildEntityMultiple()
), so the solution was simple: just make Views'\Drupal\views\Entity\Render\DefaultLanguageRenderer
and\Drupal\views\Entity\Render\RendererBase
a lot simpler: always use::view()
. Problem solved, and much simpler code both inEntityViewBuilder
and Views' entity rendering.EntityViewBuilder::buildContent()
higher in the file again, now the diff for it is tiny instead of huge.(See the
interdiff-166-[NUMBER].txt
files for each of the numbered changes above.)Review
We are very close, I have very little left to remark upon :)
This new hook is not yet documented in
entity.api.php
,node.api.php
, etc.Why not just rename these
build()
andbuildMultiple()
?There is no ambiguity around what we're building, so IMO that'd be just fine.
Why are the
ContentEntityInterface
checks necessary?Comment #168
catchThose are the ~85 test failures.
Comment #169
yched CreditAttribution: yched commentedThanks @jessebeach and @Wim Leers, that's a ton clearer indeed !
Howewer :
Hm, this is true with the patch, but wasn't true so far: when it included building the formatted field values, viewMultiple() ran formatter's prepareView() (which typically includes loading referenced entities) on all entities in one pass, while this now runs entity by entity on cache misses.
But I guess that can't be avoided as long as Views does separate drupal_render()s.
Also, this means Views does a series of single cache gets instead a multiple one ? That's a bit sad :-/
In short, not for this issue, but Views doing separate drupal_render()s has a lot more negative impact now than in current HEAD (even if the overall result of HEAD vs. patch is still likely a win).
Not sure we really need to move from += to array_merge ? The former is more fluent.
Not introduced here, but : we add $build['#cache']['tags'] even if we find out on the next line that the entity / view mode do not support caching ?
camelCase var ? Some folks in here must write JS code from time to time ;-)
(buildEntity() calls it $buildlist)
-> $build_list ?
Probably not introduced here, but for clarity, we should rename the $build variable used within viewMultiple() (it's the "build" for the multiple entities, whereas everywhere else $build is used for the "build" of a given entity)
--> $build_list like in view() / buildEntity() ?
$this->moduleHandler ?
$this->moduleHandler ?
Still don't get why we suddenly need to be extra cautious now. Why would the $build[$key][$field_name] entry exist already ?
If it really can exist, then it probably deserves an explanation in a comment :-)
Then we might as well inline the $display_content var, it was only there because you can't "use" $this in closures in 5.3 :-)
Missing empty line after last method
Comment #170
yched CreditAttribution: yched commentedAlso :
but buildContent() feels a bit out of place in the middle.
--> rename it to buildEntity() ? We'd have build() / buildMultiple() / buildEntity()...
No biggie but looks like '#theme' doesn't need to be set here, and would rather belong in buildContent() / buildEntity() ?
- getBuildDefaults() runs in a completely different phase than build() / buildMultiple() / buildEntity(), and is mostly in charge of assembling the #cache data and saving its params for the pre_render steps. It's not really about "defaults" anymore.
- hook_node_defaults_alter() is not really telling about what the hook does and when it kicks in the process.
Comment #171
jessebeach CreditAttribution: jessebeach commentedI'm right there with you. What about something with "pre render", for example
preRender()
hook_pre_render_alter
Comment #172
jessebeach CreditAttribution: jessebeach commentedThis should resolve the
EntityReferenceFormatterTest
fail and the Views translation test fails. Wim, the code in thepreRender
function that your removed regarding$count_langcodes
was necessary to build an entity multiple times in a view in different languages. I pushed that processing up toTranslationLanguageRenderer
. I also had to fix a fatal inCurrentLanguageRenderer
; it needs a no-opgetLanguage()
method so that it will return null and force the view builder to use the current language.The rest of the fails appear to be in the Entity and Page cache checking, which leads me to believe the root of the problem is in some base or common class.
I'd like to resolve these fails in #166 before addressing the subsequent reviews.
Comment #174
jessebeach CreditAttribution: jessebeach commented172: pre-render-2099131-172.patch queued for re-testing.
Comment #176
Wim LeersWorking on this, so assigning to prevent double work. I have 99% of yched's feedback addressed, but have now run into a problem that will probably keep me busy for a bit longer.
Comment #177
Wim Leers#169:
True.
Exactly.
Render cache always retrieves (now and in the past) single cache entries — because
drupal_render()
only considers a single the current subset of the renderable array, and therefore must always do individual cache gets.Correct on both counts.
However… once we get to the point where Views bubbles up cache tags correctly, we'll be able to render cache the entire View output. So this will become moot.
#172: Aha! I didn't realize the same entity could be rendered multiple times, but in different languages. Good catch!
Tests fixed. The tests were correctly asserting that the
<entity type>_view
tag was present of the referenced entity, but were only using theEntityReferenceLabelFormatter
. Now I've switched toEntityReferenceEntityFormatter
, so now the assertions pass. Having done that, though, it turns out that a few other assertions were incomplete: some assertions correctly asserted that the entity type's "view" cache tag was present, but failed to assert that the cache tags associated with the fields within a fully rendered view of the entity were also present. So, fixed that also.Finally, doing all that revealed a big problem (the one that kept me busy for quite a bit longer) in
EntityReferenceEntityFormatter::viewElements()
: that function runs during the#pre_render
phase (it's called byEntityViewBuilder::buildContent()
, which is called byEntityViewBuilder::buildMultiple()
, which is called byEntityViewBuilder::build()
, which is the#pre_render
callback of a single entity view), after which all cache tags must be collected. But…::viewElements()
itself renders another entity, for which another#pre_render
callback would need to run to collect its cache tags in turn! The only solution there is to immediately invoke the rendered referenced entity's#pre_render
callbacks immediately.Feel free to convince yourself that this is necessary by commenting the added code in
EntityReferenceEntityFormatter
(if the updated tests' failures are insufficient), creating a node using the "Basic HTML" text format, commenting on it using the "Plain text" text format, clearing the render cache, visiting/node/1
and then looking at the cache entries in{cache_render}
: you'll see that theentity_view:comment:1:…
render cache entry has thefilter_format:plain_text
cache tag, but thatentity_view:node:1:…
render cache entry doesn't have it, even though it does contain the comment! Uncomment the code, clear the render cache again, reload the page, and you'll see that the comment's text format's cache tag is correctly bubled up.(See
interdiff-177-cachetags.txt
.)#169:
buildEntityMultiple()
's$build
parameter to$build_list
andEntityViewDisplay::buildMultiple()
's local$build
variable.(See
interdiff-177-169.txt
.)#170:
EntityViewDisplay
also has::build()
and::buildMultiple()
, so it's perfectly in line with that :)RE:
::buildContent()
, I haven't done that rename yet because I don't wholeheartedly agree with::buildEntity()
. To me, either::buildComponents()
or::buildFields()
sounds like a better name, because that's precisely what it does: it fills the renderable array with the components corresponding to the Entity's Fields. Thoughts?::buildContent()
is responsible for only the components (fields) within the entity (i.e. children of the renderable array), whereas this#theme
property affects the entire renderable array.hook_[ENTITY TYPE]_defaults_alter()
tohook_[ENTITY TYPE]_build_defaults_alter()
may be sufficient.getBuildDefaults()
is still at-least-kind-of-appropriate — we haven't changed anything noteworthy about it? If you really want to modify the actual built render array, then you must override::alterBuild()
, just like before.::getBuildDefaults()
for the very sparse, high-level renderable array;::alterBuild()
for altering the final renderable array. It just happens to be that the sparse renderable array is transformed to the final one in a later phase, through a#pre_render
callback, but that doesn't affect any of the (many) alter hooks.hook_[ENTITY TYPE]_defaults_alter()
now, otherwise you're too late. So, I think renaming it tohook_[ENTITY_TYPE]_pre_render_alter()
may be acceptable, though not preferable.Thanks to the preceding explanation, I realized
hook_entity_view_mode_alter()
cannot possibly work anymore, since it runs too late. Apparently we don't have test coverage for that hook… fixed it.(See
interdiff-177-170.txt
.)Comment #178
catchRight, this is really important, and I missed it when discussing the single vs. multiple load stuff. Render caching of views lists means a single cache entry for the entire list.
If one entity in the list is updated, then the cache entry will be invalidated. However it will then fall back to the render cache entries for each item being rendered, which may or may not be hits. It's only when the list isn't cached that we even check those.
In Drupal 7 views you could cache the output of the entire list, but when that was a cache miss, you'd have to do a full render of each entity - it's this that we're replacing with the render cache get.
Comment #179
jessebeach CreditAttribution: jessebeach commentedStraight re-roll. Patch no longer applied.
Comment #180
jessebeach CreditAttribution: jessebeach commentedbuildContent()
is building Displays. So let's call itbuildDisplays()
.Comment #182
yched CreditAttribution: yched commented- buildEntity(): yeah, I can see why it's shaky. We're not "building" an $entity.
- buildDisplays() is misleading IMO. "Display" / $display in the context of EVB is EntityViewDisplay objects. This doesn't "build an EntityViewDisplay" :-)
- buildFields() : yeah, you could say that it's what the base implementation does, but it's only the base implementation. The method is also intended to be the go-to override entry point for entity-type-specific subclasses to add ad-hoc elements to the render array. So buildFields() seems too specific in that regard...
-> buildEntityContent() ? or, well, maybe just stick to buildContent() then :-)
(in which case, well, assigning '#theme' = $this->entityTypeId could fit in the scope of the method :-p)
OK, I guess keeping getBuildDefaults() + renaming hook_[ENTITY TYPE]_build_defaults_alter() works for me.
Oops, sorry, I was the one who asked for this change :-(. Don't fully get the reason, but if it's needed, well...
A bit beyond me :-), but that makes me wonder whether this formatter should do a multiple_view() across all deltas ?
(or even across all entities of the parent multiple_view(), by pushing the entity_view() up in EntityRefEntityFormatter::prepareView() - this is hair pulling territory, probably for a followup if at all...)
Comment #183
catch#4 confuses me.
In the case of a referenced entity, then we specifically don't want to force running the #pre_render callbacks, because that bypasses the entity render caching for that entity. If it's cached, we get the cache tags from cache.
So either we should just pass the render array back up (and eventually the cache tags will get collected). Or worst case do a drupal_render() and pass back cache tags and #attached back up.
Comment #184
Wim Leers#182
buildComponents()
? :)Will reply to #182.4 & #183 in a next comment.
Comment #185
jessebeach CreditAttribution: jessebeach commentedRenamed
hook_ENTITY_TYPE_defaults_alter
tohook_ENTITY_TYPE_build_defaults_alter
andhook_entity_defaults_alter
tohook_entity_build_defaults_alter
.I added these hooks to
entity.api.php
and updated the draft change notice.Also,
buildContent()
is nowbuildComponents()
(I hope that's ok!). I put that interdiff in a separate file so it can be rolled back easily :) Also, if folks disagree I can roll back easily from my local branch.Now, that should leave us with just the entity reference cache tag issue to resolve before we're at the final review, perf testing and maybe committing(??). I can dream...
Comment #186
Wim Leers#182.4/#183: Without this change (and the improved change in this patch, thanks to #183 — see below), this is the bug being fixed:
node_view:1
,node:<node ID>
,user:3
andfilter_format:full_html
cache tagscomment_view:1
,comment:<comment ID>
andfilter_format:basic_html
cache tagsnode_view:1
,node:<node ID>
,filter_format:full_html
and thecomment_view:1
andcomment:<comment ID>
cache tags — note thatfilter_format:basic_html
is missing!In other words: by moving the bulk of the work to a
#pre_render
callback, cache tags set by field formatters of entities that are rendered inside of another entity through some formatter are not bubbled up! (In D8:EntityReferenceEntityFormatter
andCommentDefaultFormatter
.)Why exactly: because of how the theme system and the render system interact. Roughly, this is what happens:
node/1
, we have a render array (let's call it RRA, for Root Render Array) that contains a node (array('#theme' => 'node, '#node' => NODE_OBJECT, 'title' => …, 'body' => …, 'comment' => …)
).drupal_render()
will call_theme($elements['#theme'], $elements)
, which will causetemplate_preprocess_node()
to be called, which will copy the children of RRA (so the values for the keys'title
,'body'
and'comment
), like this:$variables['content']['comment'] = $page['nodes'][1]['comment']
, etc.node.html.twig
template will render{{ content|without('links') }}
, which will cause$variables['content']['comment']
to be rendered.drupal_render()
does not recurse over subtrees of a tree for which a#theme
function (or equivalent template) is successfully applied…#pre_render
callbacks added by field formatters are never executed in the scope of RRA! Or, more generally,drupal_render()
has not run on any of the subtrees of RRA.(This is generally fine, because the
#pre_render
callbacks are also executed when individual subtrees are rendered by a Twig template, so everything will be rendered correctly. However, we have a more advanced need: we need them to run on the RRA, because that's how we get our cache tags to bubble up!)#183: hrm, good point! I didn't consider that. You're right that would mean a rendered referenced entity would always need to be rendered… but I wonder if that's such a big problem, since it's going to be cached as part of the referencing entity anyway?
To answer your two alternatives:
drupal_render()
almost works. If I also fixrender()
to not callshow()
+drupal_render()
(which sets#printed = FALSE
, causing the content to show up doubled) in case the content is already pre-rendered/.#186: lovely :)
In this reroll:
#pre_render
callbacks directly, rundrupal_render()
instead.show()
drupal_pre_render_render_cache_placeholder()
, which was still a remnant from#type = render_cache_placeholder
.EntityReferenceEntityFormatter
, but nowCommentDefaultFormatter
is also fixed.TextDefaultFormatter
andTextTrimmedFormatter
that would cause the first filter format cache tag to remain the only one (because a string instead of an array was being used,NestedArray::mergeDeep()
would not be able to merge multiple different filter format cache tags).The
*-fail.patch
is the same as the actual patch, but withinterdiff-fix-only.txt
subtracted from it.Comment #188
jessebeach CreditAttribution: jessebeach commentedRerolled. Wim, your patches keep failing on the removal of the file
core/modules/node/lib/Drupal/node/Tests/NodeBuildContentTest.php
. Are you usinggit rm
to remove it?Comment #190
Wim LeersRebased. HEAD changes very quickly these days!
Apparently I also forgot to include
CommentDefaultFormatterTest
in #186. So ignore everything in #186, this redoes it completely.Comment #191
jessebeach CreditAttribution: jessebeach commentedThis is all the goodness of the patch in #190, now with fewer warnings :)
Comment #194
jessebeach CreditAttribution: jessebeach commented191: pre-render-2099131-191.patch queued for re-testing.
Comment #196
jessebeach CreditAttribution: jessebeach commentedFixed
\Drupal\image\Tests\ImageThemeFunctionTest
. The test was being spar about reuse of a renderable and it was getting caught in the new check for double-rendering inrender()
.Now,
\Drupal\comment\Tests\CommentDefaultFormatterCacheTagsTest
poses a bit of a problem. It's failing on Testbot, but not locally. The failing case produces the same markup locally, so maybe it's just a difference in the way two arrays are compared in some set of conditions that's different on Testbot. Needs more investigation...Comment #198
sunHonestly, recent comments in here didn't sound at all very reassuring...
How can we ensure that "Rendering of special case XYZ" won't break due to this patch?
Absolutely none of our current test coverage takes a potential render cache (or for that matter, any possibly existing caching) into account. Existing tests are obviously not guilty — a test author cannot preemptively account for any possible future framework enhancements that might introduce a render cache layer (or not). In addition, (all) integration tests essentially have to be doubled/duplicated; once with enabled caching, and once without, whereas expectations are identical for both.
Committing this pretty much means to hope that everything still works as expected. I didn't look at the patch yet, but in general, I'm certainly in favor of the idea. However, is everyone ready to accept that this will back-fire in the sense of many follow-up issues over the course of the next months?
Comment #199
Wim Leers#198: I understand your concern, but it's not quite that bad. The only test failures fail because
render()
rather thandrupal_render()
(not a single other test does this AFAICT), but that's wrong, becauserender()
should only be used by the theme engine/layerIn other words: this has only uncovered flaws in existing tests, and there's only one of them.
The key problem is that our render + theme system are convoluted and awkwardly intertwined. Most rendering-related tests are also convoluted. We have to make do with what we have. Refactoring those systems towards sanity is a D9 thing.
If there will be follow-up issues, I promise I will handle them.
#196: While your proposal works, I disagree with the fix. In this reroll, I'm fixing things in a "better" way. And to find the cause of that last failure, I'm temporarily removing the comment in
CommentDefaultFormatterCacheTagsTest
to see what cache tags *are* found.Finally: why does testbot no longer test the
-fail.patch
patch files? :(Comment #201
Wim LeersAfter two attempts over at #2228043: Helper for #2099131, this patch should be green.
May this be the last reroll!
The cause: run-tests.sh has subtle differences in the logged in user.
Plus,
$entity->get('comment_field_name')->comment_count
remains at zero. That was masked in #186 and later because the web test runner creates a user that has permission to administer comments. The condition to render comments inCommentDefaultFormatter
looks like this:In the web test runner, the comment count was zero but the user was allowed to administer comments, hence comments were rendered.
In the CLI test runner, the comment count was zero, and the user was not allowed to administer comments, hence comments were not rendered, hence cache tags were missing.
I should have loaded
$commented_entity
again, because only then doescomment_count
get initialized. So I did that. But then I discovered another flaw: calling$comment->save()
should reset the commented on entity's storage cache, but that's not yet happening. I talked to Berdir in IRC, and #597236: Add entity caching to core is already fixing that, so for now doing an entity load with$reset = TRUE
, #597236 can then remove that.In other words: a scary mix of subtle mistakes, subtle environment differences and subtle assumptions… but not in any way related to
#pre_render
!Comment #202
jessebeach CreditAttribution: jessebeach commentedThis issue surfaced so many of these. I'm glad we undertook it. And as Wim mentions, the
#pre_render
code was a catalyst that precipitated these issues, not a reagent in their production!I'm profiling the patch in #201 now. I'll post results soonly.
Comment #203
jessebeach CreditAttribution: jessebeach commentedI found this comment threading issue while setting up the performance cases. It exists on 8.x HEAD, so not introduced by this patch, in case you run into it while testing: #2254181: Comment indentation is incorrect for comments following a replying-comment: don't render cache comments for which threading is enabled.
Comment #204
jessebeach CreditAttribution: jessebeach commentedSome performance numbers.
Single article
Scenario: single article with additional text, longtext and list fields. The article has 40 comments on it. This test compares the fastest run of 6, warm cache to warm cache. The run1 is 8.x HEAD; run2 is 8.x with the patch from #200 applied. xdebug is turned off at the server level.
The
#pre_render
build postponing results in a 106ms gain in processing time for a single node page.Next I'll look at a list of nodes on the front page.
Comment #205
jessebeach CreditAttribution: jessebeach commentedAnd some numbers for lists of articles:
10 articles, standard teasers
Scenario: 10 articles, standard teasers, 5 with images. This test compares the fastest run of 6, warm cache to warm cache. The first is 8.x HEAD; second run is 8.x with the patch from #200 applied. xdebug is turned off at the server level.
We get a gain of 108ms which is similar to the single article. I think this is because the entity views are built in a viewMultiple call in the
EntityViewBuilder
, so the savings for one entity will be the same as the savings for multiple entities in a group.We start to see savings accrue as we add more stuff to the entities.
10 articles, 3 extra fields and comments
Scenario: 10 articles, enhanced teasers, each with three extra fields displayed (2 text fields and a list field), 5 with images and three articles with comments (one with 30 displayed). This test compares the fastest run of 6, warm cache to warm cache. The first is 8.x HEAD; second run is 8.x with the patch from #200 applied. xdebug is turned off at the server level.
Now we get a savings of 216ms. As the nodes become more complex, the cost savings to build them accumulates.
Comment #206
jessebeach CreditAttribution: jessebeach commentedComment #207
jessebeach CreditAttribution: jessebeach commentedComment #208
jessebeach CreditAttribution: jessebeach commentedI just did a full review of the code changes again. I think this is it. I'm so psyched about this patch!
Comment #209
jessebeach CreditAttribution: jessebeach commentedAll code review points have been addressed in #201. We're green and the perf numbers (these should be verified) look good.
Assigning to catch.
Comment #210
catchNumbers looks great. I found a few nitpicks but it's mostly in tests at this point.
Assertion message is commented out, we could just delete that, more useful to have the two arrays in the test output.
We still have #2168111: Allow drupal_render() to pass up #attached to parents open but I think there's another issue somewhere for the way that #attached and cache tags go missing in preprocess. If I can't find it, worth creating one for this I think. The comment here should try to explain the theme() problem a bit more IMO, it's not clear from the comment why this is necessary.
I think we probably need a follow-up to fix the tests with these assumptions. It ought to be possible to have the tests check the rendered markup instead or similar.
Looks like these still need to be resolved?
Comment #211
jessebeach CreditAttribution: jessebeach commented#210:1
I don't see this commented out code in the file adding in the patch from #201. This is the patch file I'm reviewing, just so we're on the same page: https://drupal.org/files/issues/pre-render-2099131-201.patch
#210:2
This is currently "works as designed". We moved the processing attachments above theme processing in #2171071: Rename drupal_add_library() to _drupal_add_library() and remove its uses. This was done so that attachments could be added to top-level elements such as
html
. Before this change,#attached
was processed after theming had taken place, which meant that pre_/process function could add#attached
to the render array and have them attached. But in the case of a top level element likehtml
, the output string would be rendered and thus, none of the attachments would be included because the string ship had already sailed. So now we process all#attach
before theming, which means you can't alter the build array with#attached
and have them injected into the output; you need to (1) run the#attached
array throughdrupal_render()
in place or (2) as the table type does, add#attached
in a#pre_render
.#cache
is processed after theming, so pre_/process functions can still alter a build array to add these. But I would tell any themer to avoid this. because we won't be able to retrieve any cache tags added in this manner when we do cache entry check at the top ofdrupal_render()
.I hope I understood your comment correctly :)
I've reworked the comments in the
EntityReferenceEntityFormatter::viewElements()
and theCommentDefaultFormatter::viewElements()
methods.#210:3
Added #2255031: Refactor tests to compare rendered output rather than values in build arrays.
#210:4
I've turned the tests back on with a few changes to render the entities before asserting values.
Comment #214
jessebeach CreditAttribution: jessebeach commentedNothing I did in #211 should be causing
LanguageFallbackTest
to fail so either something changed in HEAD that our patch is undoing or testbot is being wonky.I also can't reproduce the test failure locally;
LanguageFallbackTest
passes just fine. So I'm inclined to believe that testbot is being wonky.Comment #215
catchFrom the interdiff there's still a commented-out assertion message.
Otherwise the changes look good. I don't think I have anything left and looks like patch was green after a retest.
Could fix that nitpick on commit so leaving CNR so there's a chance for others to look. I don't think I can commit this without at least an in-depth review from someone else since I've been pretty heavily involved. I'll try to beg Alex Pott for one.
Opened #2255543: Cache tags of nested entity renders are not bubbled up for the nested entity render issue - not introduced here and that's a tricky one.
Comment #216
jessebeach CreditAttribution: jessebeach commentedAck! Leftover from debugging. I added the string back to the assertion.
Comment #217
catchOK I think this is ready, or at least I've looked at it too much to spot any other issues.
Moving to RTBC, assigning to Alex Pott to review/commit since I've worked on this a bit too much to be neutral.
Comment #218
Dries CreditAttribution: Dries commentedI just looked at this and the code looks great. One of the best issue summarizes I've seen in a while, and I absolutely love the performance results. Great work. Committed to 8.x.
Comment #220
effulgentsia CreditAttribution: effulgentsia commentedGreat work, everyone! I'll have several clean up follow ups to open. Here's the first: #2257421: Convert CommentDefaultFormatterCacheTagsTest from web test to entity unit test.
Comment #221
yched CreditAttribution: yched commentedAs per #150-2 :
Patch posted in #2258091: Reorder methods in EntityViewBuilder.
Comment #222
Wim LeersBoth #2257421: Convert CommentDefaultFormatterCacheTagsTest from web test to entity unit test and #2258091: Reorder methods in EntityViewBuilder have been committed, which means all follow-ups are now committed as well.
Comment #223
Wim LeersXano pointed out that the
#post_render_cache
change notice was still taking about therender_cache_placeholder
element type over at https://drupal.org/node/2151609, even though that was removed in this issue. Updated the change notice to reflect the new situation/best practices introduced here. See https://drupal.org/node/2151609/revisions/view/6762699/7240115.