Updated: Comment #0

Problem/Motivation

Entity rendering got a lot slower in Drupal 8 (citation/numbers needed). That's why we worked very hard on #1605290: Enable entity render caching with cache tag support. That issue solved the problem for most entity types… but not for the most commonly used entity type of all: nodes.

Render caching for nodes is blocked on:

Many of these only became fixable after #2118703: Introduce #post_render_cache callback to allow for personalization without breaking the render cache landed a few weeks ago. We're almost at the point where can finally enable node render caching :) That will happen here!

Proposed resolution

Enable node render caching once all blockers have been fixed.

Remaining tasks

Fix all blockers.

User interface changes

None.

API changes

None.

CommentFileSizeAuthor
#80 drupal_render_no_comment.png89.7 KBBerdir
#80 full_no_comment.png38.43 KBBerdir
#80 drupal_render.png85.71 KBBerdir
#80 full.png39.99 KBBerdir
#78 enable_node_render_cache-2151459-78.patch38.38 KBjessebeach
#78 interdiff.txt2.05 KBjessebeach
#76 interdiff.txt3.77 KBjessebeach
#76 enable_node_render_cache-2151459-76.patch38.35 KBjessebeach
#69 enable_node_render_cache-2151459-69.patch39.33 KBWim Leers
#67 interdiff.txt797 bytesWim Leers
#67 enable_node_render_cache-2151459-67.patch39.35 KBWim Leers
#64 interdiff.txt3.72 KBWim Leers
#64 enable_node_render_cache-2151459-64.patch39.25 KBWim Leers
#61 enable_node_render_cache-2151459-61.patch36.22 KBplach
#59 enable_node_render_cache-2151459-59.patch36.21 KBplach
#59 enable_node_render_cache-2151459-59.interdiff.txt958 bytesplach
#55 enable_node_render_cache-2151459-55.patch35.39 KBplach
#55 enable_node_render_cache-2151459-55.interdiff.txt675 bytesplach
#54 enable_node_render_cache-2151459-54.patch35.35 KBplach
#50 interdiff.txt1.39 KBWim Leers
#50 enable_node_render_cache-2151459-50.patch36.21 KBWim Leers
#46 enable-node-render-cache-2151459-46-interdiff.txt930 bytesBerdir
#46 enable-node-render-cache-2151459-46.patch34.09 KBBerdir
#43 interdiff.txt659 bytesjessebeach
#43 enable-node-render-cache-2151459-43.patch33.12 KBjessebeach
#41 enable-node-render-cache-2151459-41.patch33.07 KBjessebeach
#41 interdiff.txt417 bytesjessebeach
#38 interdiff-comment_node_op_links.txt10.14 KBWim Leers
#38 interdiff.txt19.68 KBWim Leers
#38 enable_node_render_cache-2151459-38.patch33.94 KBWim Leers
#37 enable-node-render-cache-2151459-36-fail-do-not-test.patch16.89 KBjessebeach
#37 interdiff.txt10.55 KBjessebeach
#28 enable_node_render_cache-2151459-28.patch7.41 KBjessebeach
#22 enable_node_render_cache-2151459-22.patch6.33 KBamateescu
#15 interdiff.txt4.85 KBamateescu
#15 enable_node_render_cache-2151459-15.patch6.47 KBamateescu
#7 interdiff.txt780 bytesWim Leers
#7 enable_node_render_cache-2151459-6.patch1.86 KBWim Leers
#3 enable_node_render_cache-2151459.patch960 bytesamateescu
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

xjm’s picture

Issue tags: +beta blocker

Discussed with @effulgentsia, @Dries, @webchick and others. It's important to expose contrib authors to this concept early and this is going to have an important impact on performance, so we are categorizing this as a beta blocker.

catch’s picture

If this is a beta blocker, I'd probably add #2099131: Use #pre_render pattern for entity render caching as well -that's going to affect when certain hooks run and possibly what's available to them just as much as render caching at all.

amateescu’s picture

#drupal8wins! :)

If we have any test failures when all the issues from the OP are committed, we probably need to add some of the manual cache clears from the interdiff in #1605290-135: Enable entity render caching with cache tag support.

Wim Leers’s picture

Issue tags: +needs profiling, +Spark, +sprint

I can't believe this patch comes down to *so* few lines! I started looking at the #1605290: Enable entity render caching with cache tag support patch that got committed, looking for things you might've forgotten. But if you did, I don't see it!
And AFAICT even zero tests need to be written, the \Drupal\system\Tests\Entity\EntityRenderTest that was added in #1605290 seems sufficient.

Wow :) A 2-line patch that will hopefully unlock quite the performance boost!

Wim Leers’s picture

#2151457: Comment pager breaks the render cache landed 3 weeks ago, #1979468: ".active" from linkGenerator(), l() and theme_links() forces an upper limit of per-page caching for all content containing links is good to go but needs a review to get to RTBC. #1979468 is the last blocker for this patch. Please review it!

Berdir’s picture

Status: Postponed » Needs review

Let's try it then.

Wim Leers’s picture

OMG YAY! #1979468: ".active" from linkGenerator(), l() and theme_links() forces an upper limit of per-page caching for all content containing links has landed, which unblocks this!


amateescu's patch is perfect, and still applies.


However, I found one EntityViewBuilder::resetCache() call that shouldn't be happening: rendered nodes that contain rendered comments should have cache tags to reflect that they contain rendered comments (and that's in fact the case), so we shouldn't be doing that separately ourselves.

In other words, I don't see why the comment field type needs to do this whenever its settings are changed:

\Drupal::entityManager()->getViewBuilder($instance->entity_type)->resetCache();

If the comment field type needs it, then every field should need it. And not a single other field type has it.

(I discovered this in #2151457: Comment pager breaks the render cache but didn't want to delay that patch any longer.)

The last submitted patch, 3: enable_node_render_cache-2151459.patch, failed testing.

The last submitted patch, 7: enable_node_render_cache-2151459-6.patch, failed testing.

catch’s picture

I don't think we should tag nodes with all their comments. Forum sites for example can have several thousand comments on a single issue, which won't scale - the comment references the node, not vice-versa.

If we had a unique comment thread ID we could tag nodes with that then clear it when a comment in that thread is updated/inserted - might work.

Wim Leers’s picture

#10: Wow, I did not see that coming :) Changing the cache tags on nodes is completely out of scope for this issue IMO. Let's get this in first, and only then worry about finalizing/fixing/optimizing our use of cache tags.

The patch in #7 only removes something that was unnecessary, in the current state of cache tags.


To reply to your actual points, but please let's not derail this issue with this discussion:

  1. Nodes themselves aren't cached with comments; this is about node *render cache* entries that are tagged with all comments that are *visible* on a specific "comment page". So on the default install, there will only be up to 50 comment cache tags per node render cache entry. On d.o, that would be up to 300. So the problem isn't quite as bad as you describe (probably because I explained it poorly).
  2. Comment thread IDs don't help, because comment threading may be disabled.
  3. The very next thing I'll be working on, is looking into cache tags: "Are all required cache tags set?", "How the hell does this scale when there are, say, 1000 cache tags on a single render cache entry?", and so on. Hence I will definitely tackle this there.
catch’s picture

Replying under-the-line ;)


Whether threading is enabled or not, adding new comments/updating/deleting can change what will be rendered on other pages as well - especially when comment threading is enabled but not only. So if we just used a tag for this, something like the comment field name + entity ID would be the closest (i.e. the specific set of comments that the one being added/updated/deleted appears in).

Berdir’s picture

The last submitted patch, 7: enable_node_render_cache-2151459-6.patch, failed testing.

amateescu’s picture

FileSize
6.47 KB
4.85 KB

As I suspected, we need to bring back a few parts from the interdiff in #1605290-135: Enable entity render caching with cache tag support. I also moved the 'in_preview' special casing to the NodeViewBuilder because this doesn't apply to all entity types.

Drupal\comment\Tests\CommentAnonymousTest will be fixed by #2099105: Clean-up render cache when permission changes

Note sure what's the best way to fix the failures in Drupal\statistics\Tests\StatisticsAdminTest as I'm not familiar with how the statistics ajax thing works, maybe we just need to clear the render cache in the test?

catch’s picture

Didn't look into the specific failures, but it case it helps: the AJAX handles collection, but statistics are displayed by PHP. So cached rendered nodes could have stale statistics counts. I think this is fine and we could manually clear the render cache for tests. If someone decides it's not fine at a later point, then it's another use case for #post_render_cache.

Berdir’s picture

  1. +++ b/core/modules/comment/comment.module
    +++ b/core/modules/comment/comment.module
    @@ -247,7 +247,6 @@ function comment_field_instance_create(FieldInstanceInterface $instance) {
    
    @@ -247,7 +247,6 @@ function comment_field_instance_create(FieldInstanceInterface $instance) {
      */
     function comment_field_instance_update(FieldInstanceInterface $instance) {
       if ($instance->getType() == 'comment') {
    -    \Drupal::entityManager()->getViewBuilder($instance->entity_type)->resetCache();
         // Comment field settings also affects the rendering of *comment* entities,
         // not only the *commented* entities.
         \Drupal::entityManager()->getViewBuilder('comment')->resetCache();
    

    Not sure why the first one can be removed and the other not?

    Isn't the point of this that there can be comment setting changes that affect how comments are rendered (e.g. changing the pager id) that can't be catched through cache tags?

    And yes, maybe it should be done on all changes.

    If you look at field.module, it currently has tons of calls to clear the field (soon entity) caches, so maybe that should be extended to clear the render cache as well? And possibly move to the entity system with #597236: Add entity caching to core

  2. +++ b/core/modules/system/lib/Drupal/system/Tests/System/DateTimeTest.php
    @@ -65,6 +65,7 @@ function testTimeZoneHandling() {
     
         // Set time zone to Los Angeles time.
         $config->set('timezone.default', 'America/Los_Angeles')->save();
    +    \Drupal::entityManager()->getViewBuilder('node')->resetCache(array($node1, $node2));
    

    Not related to this but wondering if there is a reason the API for the render cache resetCache() and the storage is different (just ID's vs. complete objects)?

amateescu’s picture

Oops, I forgot that I need post things under-the-line :D


Re #16:

I'll let @Wim answer that as he's probably more familiar with the statistics stuff. I'm inclined to say that we do want to implement #post_render_cache for view counts.


Re #17:

Not sure why the first one can be removed and the other not?

Because the 'parent' entity (e.g. node) render cache is also tagged with the comment IDs, so it gets cleared by that virtue.

Not related to this but wondering if there is a reason the API for the render cache resetCache() and the storage is different (just ID's vs. complete objects)?

We need the full objects here because we have to get the bundle of the entity. See #1712456-19: How to leverage cache tags in Views :)

The last submitted patch, 15: enable_node_render_cache-2151459-15.patch, failed testing.

amateescu’s picture

The last submitted patch, 15: enable_node_render_cache-2151459-15.patch, failed testing.

amateescu’s picture

FileSize
6.33 KB

Rerolled.

Status: Needs review » Needs work

The last submitted patch, 22: enable_node_render_cache-2151459-22.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Postponed
jessebeach’s picture

Issue summary: View changes
Status: Postponed » Needs work

[1979468-258] was recently committed. This issue is unblocked now.

jessebeach’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 22: enable_node_render_cache-2151459-22.patch, failed testing.

jessebeach’s picture

Status: Needs work » Needs review
FileSize
7.41 KB

Rerolled.

The following failed to apply in NodeType.php

// Invalidate the render cache for all nodes.
\Drupal::entityManager()->getViewBuilder('node')->resetCache();

Status: Needs review » Needs work

The last submitted patch, 28: enable_node_render_cache-2151459-28.patch, failed testing.

The last submitted patch, 28: enable_node_render_cache-2151459-28.patch, failed testing.

dsnopek’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 28: enable_node_render_cache-2151459-28.patch, failed testing.

Wim Leers’s picture

On it.

The failures are legitimate. The statistics-related failure I should definitely be able to figure out. The search ones hopefully also.

jessebeach’s picture

Status: Needs work » Needs review
jessebeach’s picture

The test fails noted in #28 for SearchMultilingualEntityTest just passed on my local setup, so I've sent the patch for retesting. Maybe something changed in the matrix and these now pass. I mean, it has been 6 days since those tests were first run.

Status: Needs review » Needs work

The last submitted patch, 28: enable_node_render_cache-2151459-28.patch, failed testing.

jessebeach’s picture

The Statistics module test failures are indicative of a gap in the drupal_render() testing. I've added what I hope is an additional test that should fail until we've addressed the issue plaguing the Statistics' tests -- namely, that jumping into the render tree midway for an entity (such as when links are rendered from a #prerender function) or when a property of an entity is rendered separately from the top-level node (such as content.links in the node template) causes the post render cache placeholder to be replaced with content and then cached. We want instead for the placeholder to be cached and replaced on render outside of a cache hit.

I simulated jumping into the render tree at the child level with this code:

// Simulate element rendering in a template, where sub-items of a renderable
// can be sent to drupal_render before the parent.
$child = &$element['test_element'];
$element['#children'] = drupal_render($child);
// Render the parent element.
$output = drupal_render($element);
Wim Leers’s picture

I made the modified & added tests consistent with the others. I've figured out a work-around. The tests jessebeach added now pass. Statistics are now working fine too, and those tests now pass also :)


Also, I failed to update the comment module's node links in #2151439: Run node op links ("Read more", "Add child page", "Printer-friendly version", "X views" + contrib) through #post_render_cache to prevent render caching granularity being per-user. Fixed that in this reroll also (see the separate interdiff for that).

Status: Needs review » Needs work

The last submitted patch, 38: enable_node_render_cache-2151459-38.patch, failed testing.

The last submitted patch, 38: enable_node_render_cache-2151459-38.patch, failed testing.

jessebeach’s picture

Status: Needs work » Needs review
FileSize
417 bytes
33.07 KB

Just a small hunk rejection in comment.module over a conflicting use statement addition.

Status: Needs review » Needs work

The last submitted patch, 41: enable-node-render-cache-2151459-41.patch, failed testing.

jessebeach’s picture

This should clear up the exceptions.

jessebeach’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 43: enable-node-render-cache-2151459-43.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
34.09 KB
930 bytes

Interesting, looks like that code path did never run before or works somehow different now.

comment_preview() incorrectly messed with the field on the commented entity, first it got the whole field (field item list), then it set the status, which due to internal defaults/magic ends up on $field->status, and then sets the whole field item list the same way, which also ends up on ->status, so you have $entity->$field->value = $field :)

Maybe the Entity Field API should better handle that and support this but I think the changed code makes more sense anyway, but is unfortunately a bit problematic, apparently:

If I understand it correctly, the new hook now runs after we set the original value back, because it now runs when the render array is rendered. That could be rather unexpected. Maybe we should clone the entity instead, then we don't have to worry about setting back the original value?

Status: Needs review » Needs work

The last submitted patch, 46: enable-node-render-cache-2151459-46.patch, failed testing.

jessebeach’s picture

comment_preview() incorrectly messed with the field on the commented entity, first it got the whole field (field item list), then it set the status, which due to internal defaults/magic ends up on $field->status, and then sets the whole field item list the same way, which also ends up on ->status, so you have $entity->$field->value = $field

This is EXACTLY the behavior I encountered in #2099131-23: Use #pre_render pattern for entity render caching!! I thought it was something I did or neglected to do in that patch, but now I think it goes deeper.

larowlan’s picture

Please forgive my incorrect use of the ->get/->set, the comment makes it clear what it is supposed to do, and the interdiff at #46 works for me, I wasn't aware that get/set worked differently. I am now. Thanks.

Wim Leers’s picture

Fixed the failure in Drupal\node\Tests\NodePageCacheTest.

Drupal\search\Tests\SearchMultilingualEntityTest passes locally… even though I didn't touch it at all. So here's hoping it will pass on testbot too.

Status: Needs review » Needs work

The last submitted patch, 50: enable_node_render_cache-2151459-50.patch, failed testing.

Berdir’s picture

I can reproduce, but only when I execute the tests with run-tests.sh. Not sure what's going on there, but the translations are somehow switched, the 0/1 keys there.

Wim Leers’s picture

Assigned: Wim Leers » plach

Following berdir's advice and assigning to plach — he's rumored to have magical bugsquashing powers in the multilingual area :)

@plach: the 3 remaining fails are all multilingual, and we have no idea why it's happening. Your insight would be most welcome :)

plach’s picture

Status: Needs work » Needs review
FileSize
35.35 KB

The patch no longer applied: here's a plain reroll as I am seeing warnings all over the place. Let's see what the bot says.

plach’s picture

Not sure what went wrong with the previous patch, but this one should have less warnings :)
Now I am looking at the test failures for reals...

The last submitted patch, 54: enable_node_render_cache-2151459-54.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 55: enable_node_render_cache-2151459-55.patch, failed testing.

The last submitted patch, 55: enable_node_render_cache-2151459-55.patch, failed testing.

plach’s picture

Assigned: plach » Unassigned
Status: Needs work » Needs review
FileSize
958 bytes
36.21 KB

This should fix the multilingual search failures. Sorry for breaking other stuff, but I really have no time to work on core these days. I think this interdiff can be applied to a saner reroll to make everything green :)

However we might need more test coverage actually as the root cause was cache key not including language. Surprisingly test failed when run by an anonymous user (this is why locally they pass with the Simpletest UI), I am wondering whether that's somehow related to the role granularity.

Status: Needs review » Needs work

The last submitted patch, 59: enable_node_render_cache-2151459-59.patch, failed testing.

plach’s picture

Status: Needs work » Needs review
FileSize
36.22 KB

Core moves fast these days...

Status: Needs review » Needs work

The last submitted patch, 61: enable_node_render_cache-2151459-61.patch, failed testing.

plach’s picture

As you can see the SearchMultilingualEntities failures are gone, so the interdiff in #59 should be good :)

Wim Leers’s picture

Assigned: Unassigned » Wim Leers
Status: Needs work » Needs review
FileSize
39.25 KB
3.72 KB

Thank you SO much, plach! :)

In this reroll:

  • Started from #50, rerolled against HEAD.
  • Then applied the #59 interdiff, this indeed fixed the multilingual entity search test failures — plach++! :) The #59 interdiff is also part of this interdiff. Therefor, this is an interdiff that shows all changes against #50.
  • Then fixed test failures in PageCacheTagsIntegrationTest.php, which is a new test in latest D8 HEAD.
  • Then fixed additional test failures that we didn't get before.

I'm hopeful this one will be green — finally! :)

amateescu’s picture

The last patch looks pretty good to me. I won't comment on the post_render_cache parts to not derail this issue, and I only found one thing that doesn't seem right:

+++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
@@ -191,6 +190,13 @@ public static function postDelete(EntityStorageControllerInterface $storage_cont
+  public function referencedEntities() {
+    return array($this->getCommentedEntity());
+  }

Why are we excluding any other reference fields that might exist on a comment entity? See what the parent method does.

Berdir’s picture

+++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
@@ -190,6 +190,13 @@ public static function postDelete(EntityStorageControllerInterface $storage_cont
    */
+  public function referencedEntities() {
+    return array($this->getCommentedEntity());
+  }

This should include the parent/default implementation too? The comment might have an entity reference field and has an author and so on...

We might also want to add a todo to add a, possibly computed field that does expose the entity as a field, so that the generic code can deal with it, that's currently however blocked on #2114707: Allow per-bundle overrides of field definitions.

... Yeah, I can see that it too me too long to post this as i got distracted again, @amateescu basically said the same :)

Wim Leers’s picture

FileSize
39.35 KB
797 bytes

Oh, YES, great point #65/#66! Incredibly stupid of me!

Status: Needs review » Needs work

The last submitted patch, 67: enable_node_render_cache-2151459-67.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
39.33 KB

Straight reroll. (Apparently, in the mean time HEAD had changed a cache() to \Drupal::cache().)

jessebeach’s picture

This is a quick xhprof comparison of a single article (node/1) page load in a standard install. The first table is the page load, the second just the drupal_render function.

Inclusive wall time improves significantly overall as one would expect with aggressive caching turned on. Exclusive wall time for drupal_render increases as, I assume, this function is now doing more work to assemble the cached output. Maybe, just a guess there :) But the increase is negligible.

And we should be able to get these numbers even lower for a warm cache with #2099131: Use #pre_render pattern for entity render caching.

Run #5308d1926d3de Run #5308d19918fe3 Diff Diff%
Number of Function Calls 447,929 86,061 -361,868 -80.8%
Incl. Wall Time (microsec) 3,983,047 953,639 -3,029,408 -76.1%
Incl. CPU (microsecs) 3,741,872 915,517 -2,826,355 -75.5%
Incl. MemUse (bytes) 33,397,920 20,257,576 -13,140,344 -39.3%
Incl. PeakMemUse (bytes) 33,675,344 20,379,552 -13,295,792 -39.5%
drupal_render Run #5308d1926d3de Run #5308d19918fe3 Diff Diff%
Number of Function Calls 5 5 0 0.0%
Incl. Wall Time (microsec) 3,300,855 632,835 -2,668,020 -80.8%
Incl. Wall Time (microsec) per call 660,171 126,567 -533,604 -80.8%
Excl. Wall Time (microsec) 228 291 63 27.6%
Incl. CPU (microsecs) 3,107,335 609,528 -2,497,807 -80.4%
Incl. CPU (microsecs) per call 621,467 121,906 -499,561 -80.4%
Excl. CPU (microsec) 199 263 64 32.2%
Incl. MemUse (bytes) 24,512,144 11,588,672 -12,923,472 -52.7%
Incl. MemUse (bytes) per call 4,902,429 2,317,734 -2,584,694 -52.7%
Excl. MemUse (bytes) 65,984 66,152 168 0.3%
Incl. PeakMemUse (bytes) 24,888,064 11,939,184 -12,948,880 -52.0%
Incl. PeakMemUse (bytes) per call 4,977,613 2,387,837 -2,589,776 -52.0%
Excl. PeakMemUse (bytes) 26,304 57,272 30,968 117.7%
Wim Leers’s picture

NICE! That's looking pretty good there! :) Let's also test what the effect is on a more complex node type: one with more fields. I think you'd also estimate/predict that the relative improvement should be even bigger, since the absolute time required should be pretty much exactly the same (i.e.: cache get).

It'd also be interesting to compare this to Drupal 7's numbers… :)

Unless somebody beats me to it, I'll do those comparisons.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

I reviewed this and found nothing to complain about. The performance gain is substantial.

webchick’s picture

Assigned: Wim Leers » catch

w00t w00t!! :) Sending over catch's way.

catch’s picture

The improvement is great but the overall numbers are horrible, three seconds for a simple node page is worse than Drupal 7 ever got iirc. At least this gives us a good opportunity to see what's left on a warm cache now, and there's still the #pre_render patch to go too.

I haven't reviewed latest patch yet, would be interesting to see Wim's numbers too but won't block a commit on it.

catch’s picture

Status: Reviewed & tested by the community » Needs work

Few minor things.

  1. +++ b/core/includes/common.inc
    @@ -4284,8 +4284,19 @@ function _drupal_render_process_post_render_cache(array &$elements) {
    + * Therefor, drupal_render_collect_post_render_cache() must be called *before*
    

    Therefore.

  2. +++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
    @@ -37,7 +37,6 @@
    - *   render_cache = FALSE,
    

    whooo.

  3. +++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
    @@ -40,7 +40,6 @@
    - *   render_cache = FALSE,
    

    whoo.

  4. +++ b/core/modules/node/lib/Drupal/node/Entity/NodeType.php
    @@ -196,6 +196,9 @@ public function postSave(EntityStorageControllerInterface $storage_controller, $
    +      \Drupal::entityManager()->getViewBuilder('node')->resetCache();
    

    How come?

    And do we need to clear the node type above if we clearing all nodes?

  5. +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php
    @@ -804,18 +804,26 @@ function testDrupalRenderRenderCachePlaceholder() {
    +    $this->assertTrue($nodes->length > 0, 'The token attribute was found in the cached markup');
    

    Just assertTrue($nodes->length) no?

  6. +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php
    @@ -804,18 +804,26 @@ function testDrupalRenderRenderCachePlaceholder() {
    +    if ($nodes->length > 0) {
    

    Same again.

jessebeach’s picture

Status: Needs work » Needs review
FileSize
38.35 KB
3.77 KB

This patch also needed a reroll.

+++ b/core/includes/common.inc
@@ -4284,8 +4284,19 @@ function _drupal_render_process_post_render_cache(array &$elements) {
+ * Therefor, drupal_render_collect_post_render_cache() must be called *before*
Therefore.

Fixed.

+++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php
@@ -804,18 +804,26 @@ function testDrupalRenderRenderCachePlaceholder() {
+ $this->assertTrue($nodes->length > 0, 'The token attribute was found in the cached markup');
Just assertTrue($nodes->length) no?

Zero evaluates to false in PHP, so yes, the great than check is superfluous. Removed.

+++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
@@ -37,7 +37,6 @@
- * render_cache = FALSE,
whooo.

Agreed. Amaze. Such cache.

+++ b/core/modules/node/lib/Drupal/node/Entity/NodeType.php
@@ -196,6 +196,9 @@ public function postSave(EntityStorageControllerInterface $storage_controller, $
+ \Drupal::entityManager()->getViewBuilder('node')->resetCache();
How come?

And do we need to clear the node type above if we clearing all nodes?

I do not know. Leaving to Wim to chime in. Hopefully he remembers whey this code was added.

Status: Needs review » Needs work

The last submitted patch, 76: enable_node_render_cache-2151459-76.patch, failed testing.

jessebeach’s picture

Status: Needs work » Needs review
FileSize
2.05 KB
38.38 KB

cache can no longer be called as a global function.

cache() to \Drupal::cache()

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

Regarding > 0 vs. assertTrue(): I prefer explicitness. Asserting an integer evaluates to a boolean triggers the pedantic alarm in me. But of course it's equally correct.

Regarding + \Drupal::entityManager()->getViewBuilder('node')->resetCache(); in NodeType.php: amateescu added that in #15. I don't remember the specifics, but AFAICT it's there because NodeType != Node, yet NodeType affects Node. Therefor, a change in a NodeType config entity (i.e. node entity bundle, e.g. "article") should cause the cache entries tagged with nodes of that type to be cleared. But we don't create cache tags for each node type (i.e. node entity bundle, e.g. "article"), therefor we must reset the Node's view builder's cache completely.
(I hope I didn't make any mistake in that reasoning — it's been a long day and I've been on the train for hours and still am.)

#78's interdiff looks good.

Back to RTBC since all nitpicks have been fixed, and I think/hope the answer above to the one question is sufficiently explanatory.


The improvement is great but the overall numbers are horrible, three seconds for a simple node page is worse than Drupal 7 ever got iirc.

Am I misreading Jesse's numbers? Before the patch it's 4 seconds, after it's 1 second.
That's precisely why I wanted to do additional benchmarks, to get a sense of where we're at versus Drupal 7.

At least this gives us a good opportunity to see what's left on a warm cache now, and there's still the #pre_render patch to go too.

Yes! :)


I'm fine with this being committed. I agree additional profiling is necessary to get a more complete sense of where Drupal 8 is at node-rendering-performance-wise, but I think it's clear this is a step forward. Additional profiling is necessary in all corners of Drupal 8, so I don't think that should hold up a commit of this patch :)

Berdir’s picture

@#70: Hm, not sure how you got those numbers, is it possible that the before is a cold start that needs to build up caches? There's no way that we could go down from 440k function calls to 86k just with this?

Did some runs myself, these are the numbers I came up with:

- node/1 with an article with a few tags, image, body and comment form.
- PHP 5.5 with opcache. Optimized MySQL and *fast* hardware.
- xhprof with only the memory flag (cpu is a huge overhead), enabled through devel.module, so we're missing the early bootstrap stuff, but that doesn't change anyway.
- I also had to patch xhprof to not track php functions as it segfaults otherwise on 5.5. I'm not sure how it exactly affects the results, probably at least the number of function calls is missing them, so I also did some good old ab tests to cross-check the numbers
- Logged in as uid 1

Full:

drupal_render()

There's a lot of post render cache stuff going on, so I closed comments and tested again.
Full:

drupal_render()

ab -n 100 -c 1 http://d8/1:

HEAD:

Requests per second:    6.52 [#/sec] (mean)
Time per request:       153.487 [ms] (mean)

Patched:

Requests per second:    7.37 [#/sec] (mean)
Time per request:       135.672 [ms] (mean)

=> 12% faster.

Numbers are pretty stable, I did multiple runs. As mentioned, xhprof wasn't covering the bootstrap part, so that should explain why those numbers are almost the same.

Because I usually can't stop when I start to profile stuff, here are some additional notes/experiments:

10% is spent in two calls to entity_create(), one due to EntityDisplayBase::getFieldDefinitions() and one in user_attach(). The first is going away in #2114707: Allow per-bundle overrides of field definitions, the second I'm not sure, can possibly be optimized to at least only run when we actually need it, it's only used as a fallback when a user can't be loaded.

Also, with a temporary hacked together combination of this, #597236: Add entity caching to core, #2182239: Improve ContentEntityBase::id() for better DX, #2114707: Allow per-bundle overrides of field definitions and removing the entity_create() in user_attach_accounts() I'm down to 118ms. One other thing is the mentioned pre_render() issue for running field formatters. While we certainly still have a lot to do, I have a feeling that Entity Field API is going to be pretty well optimized compared to to other parts at that point.

Need. to. stop. and. go. to. bed. :)

Berdir’s picture

Arg. Just noticed that I had xdebug + xhprof enabled, xhprof becomes much slower when xdebug is enabled. Relatively, the numbers should still be ok'ish, although fewer function calls might suggest more improvements than there actually are.

So just a few quick numbers, disabling xdebug, xhprof, devel, enabling apc classloader and then the same ab tests again:

HEAD:

Requests per second:    16.89 [#/sec] (mean)
Time per request:       59.213 [ms] (mean)

With this patch:

Requests per second:    19.23 [#/sec] (mean)
Time per request:       52.003 [ms] (mean)

With my fun issue mix-up from above, I'm getting this:

Requests per second:    21.40 [#/sec] (mean)
Time per request:       46.723 [ms] (mean)

Before you get too excited about that, here's the same for D7 node/1:

Requests per second:    67.70 [#/sec] (mean)
Time per request:       14.771 [ms] (mean)

So still 3x slower.

catch’s picture

Status: Reviewed & tested by the community » Fixed

OK that looks a lot more like I was expecting on the profiling.

We should dsm() every comment submission that mentions xhprof with 'did you warm the cache both times and did you disable xdebug?' :P

Regarding + \Drupal::entityManager()->getViewBuilder('node')->resetCache(); in NodeType.php: amateescu added that in #15. I don't remember the specifics, but AFAICT it's there because NodeType != Node, yet NodeType affects Node. Therefor, a change in a NodeType config entity (i.e. node entity bundle, e.g. "article") should cause the cache entries tagged with nodes of that type to be cleared. But we don't create cache tags for each node type (i.e. node entity bundle, e.g. "article"), therefor we must reset the Node's view builder's cache completely.
(I hope I didn't make any mistake in that reasoning — it's been a long day and I've been on the train for hours and still am.)

The reasoning sounds right, we might want to revisit those very high level cache tags a bit, but since the patch only introduces the tag invalidation that's plenty for me.

Committed/pushed to 8.x, thanks!

Wim Leers’s picture

Issue tags: -needs profiling, -sprint

Wow — that's quite different. The numbers in #81 are … somewhat depressing, but definitely interesting. It'll be interesting to see where else we're going to find the gains we need to find.

Just to make sure: everything in #81 are authenticated user numbers, just like in #80, right?

Berdir’s picture

#81/ab was done with anonymous users, but without page or block caching enabled.

chx’s picture

This broke comment stubbing. #2207907: Comment stubbing is broken

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.