Problem/Motivation

Drupal.org has gotten quite slow over the time. Especially the D7 version. The more comments on a page, the slower the page load.

Proposed resolution

Comments can be cached near perfectly with render caching (https://www.drupal.org/project/render_cache) and a hash based approach:

- They need the modified timestamp of the comment
- They need the modified timestamp of the author
- They need a flag for is_viewer / is_author

DRUPAL_CACHE_PER_ROLE as base cache and thats it as the Cache ID is calculated at run-time.

This means that for normal issues the second time for most users all comments are cached.

Due to the hash changing, no expensive cache invalidation is needed.

Remaining tasks

- Check if entity module 7.x-1.5 is installed
- Check that instead of comment_view_multiple entity_view() can be used instead (that automatically enables render_cache module)
- Implement render_cache module on a sandbox / VM
- Make test suite pass
- Have a faster d.org :)

Deployment - window scheduled for Thursday, 24 July 2014 2-4pm PDT (21:00 UTC)

  1. Enable entity_modified 7.x-1.x-dev
  2. Enable render_cache 7.x-1.0
  3. Update drupalorg code
  4. cc all
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drumm’s picture

Issue summary: View changes

I generally like this idea.

We have dev sites - http://drupal.org/node/1018084. These are good for regression testing, but are nowhere near representative of production performance. That's okay, we mostly want to check for regressions. At the moment, this is mostly manual testing on dev. We have BDD tests, https://www.drupal.org/project/doobie, but they are not easy to run against dev yet. And even BDD can miss caching bugs. Dev doesn't have memcache enabled, but we could spin up a memcache server for testing.

What we will want in this issue is deployment instructions. What versions of projects to install, and configuration changes.

Fabianx’s picture

Sure, can I get read-only access to any dev instance plus XHProf to check which code path is used to show comments.

If its just the normal comment_view_multiple the tasks will be:

- Install and enabled entity, render_cache (once new release is out - due today), entity_modified modules
- Also enable render_cache_comments
- Add to a custom module like dorg_performance something like:


/**
 * Implements hook_render_cache_entity_default_cache_info_alter().
 */
function drupalorg_render_cache_entity_default_cache_info_alter(&$cache_info, $context) {
  // Disable entity_view() render caching by default to avoid side effects.
  $cache_info['granularity'] = DRUPAL_NO_CACHE;
}

/**
 * Implements hook_render_cache_entity_cache_info_alter().
 */
function drupalorg_render_cache_entity_cache_info_alter(&$cache_info, $entity, $context) {
  // Allow to disable render caching via variable.
  if (!variable_get('drupalorg_render_cache_enabled', TRUE)) {
    return;
  }

  // Always render_to_markup if possible.
  // Yes, please save rendered data.
  $cache_info['render_cache_render_to_markup'] = TRUE;

  // Check if render caching should be enabled for comments.
  if (variable_get('drupalorg_render_cache_comment_enabled', TRUE)
      && $context['entity_type'] == 'comment'
      && $context['view_mode'] == 'full') {
    $cache_info['granularity'] = DRUPAL_CACHE_PER_ROLE;
    // Store that this needs special comment processing.
    $cache_info['drupalorg_comment_processing'] = TRUE;
  }
}

/**
 * Implements hook_render_cache_entity_hash_alter().
 */
function drupalorg_render_cache_entity_hash_alter(&$hash, $entity, $cache_info, $context) {
  if (!empty($cache_info['drupalorg_comment_processing'])) {
    // Comment is displaying users, so need to add this to the hash, entity is already added.
    $hash['comment_uid'] = $entity->uid;
    $account = user_load($entity->uid);
    if (!empty($account->uid)) {
      $hash['comment_uid_modified'] = entity_modified_last('user', $account);
    }

    $hash['is_viewer'] = $entity->uid == $GLOBALS['user']->uid;

    $node = node_load($entity->nid);
    $hash['is_author'] = $entity->uid == $node->uid;
  }
}

And that should theoretically just work without knowing much about the internals.

I'll probably release a stable version of render_cache first (has been in production since almost a year now) and apply the RTBC patch for render_cache_comments (which I wrote), but this can be used to start playing around with this.

Memcache is not absolutely needed as cache_render bin is created by render_cache module, but DB could get full quite soon as there is no LRU obviously on DB Cache Backend ...

Though a drush cc all fixes that, its probably not a problem.

Also I suggest to keep the variables to disable at run-time and for dev-instances.

drumm’s picture

Project: Drupal.org infrastructure » Drupal.org customizations
Version: » 7.x-3.x-dev
Component: Drupal.org module » Code

Moving to the project with the site's custom code. I think we should patch this into drupalorg itself, I'm not a fan of lots of custom modules. And the variable_get() kill switch is more convenient than enabling/disabling a module.

drumm’s picture

Looks like you already have an account on devwww. Can you confirm ssh Fabianx@devwww.drupal.org works?

Fabianx’s picture

Yes, I I can still login to devwww.

Fabianx’s picture

The 1.0 version of render_cache and 1.1 version of entity_modified have been released.

I will now make a patch to drupalorg module and then all we need is testing this and this should be good to go ...

Fabianx’s picture

Adding an obviously untested patch for drupalorg module.

Fabianx’s picture

And a new patch, which also has update hooks and _dependencies() in the .install (if I got this right). Also adds to .info file.

drumm’s picture

The drupalorg.install portion isn't needed. We'll enable the modules manually either ahead of, or after, the code deployment. drupalorg_update_dependencies() is effectively dead code since the D7 upgrade is done, and we aren't running a few hundred hook_update_N()s.

Fabianx’s picture

Okay, cool. Thanks for the explanation.

Keep the change to the .info though?

drumm’s picture

Yep, .info is good.

Fabianx’s picture

Status: Active » Needs review
FileSize
2.86 KB

And a new patch with suggested changes that I am gonna try on my dev instance now.

Fabianx’s picture

TL;DR: 30% performance improvement on a page with just 44 comments ...

I have to do two changes to entity_modified and drupalorg to skip a rather extensive, but unnecessary user_load(), but besides that this is finished it seems:

Full Page

Just the callback showing the comments

Interdiff

diff --git a/drupalorg/drupalorg.module b/drupalorg/drupalorg.module
index cc78da1..f70c94d 100755
--- a/drupalorg/drupalorg.module
+++ b/drupalorg/drupalorg.module
@@ -2857,9 +2857,8 @@ function drupalorg_render_cache_entity_hash_alter(&$hash, $entity, $cache_info,
   if (!empty($cache_info['drupalorg_comment_processing'])) {
     // Comment is displaying users, so need to add this to the hash, entity is already added.
     $hash['comment_uid'] = $entity->uid;
-    $account = user_load($entity->uid);
-    if (!empty($account->uid)) {
-      $hash['comment_uid_modified'] = entity_modified_last('user', $account);
+    if (!empty($entity->uid)) {
+      $hash['comment_uid_modified'] = entity_modified_last_id('user', $entity->uid);
     }
 
     $hash['is_viewer'] = $entity->uid == $GLOBALS['user']->uid;

Likely to get the new release for entity_modified out today with that little change ...

killes@www.drop.org’s picture

Maybe we could re-enable the mailing of complete comments in notifications after this hast been deployed...

Fabianx’s picture

Status: Needs review » Needs work

CNW, I forgot the new_first and new flags that are set within classes and different markup.

Fabianx’s picture

Status: Needs work » Needs review
FileSize
1.68 KB
2.82 KB

Okay, implemented is_new, first_new, in_preview and divs and divs_final properties.

I verified NEW functionality works perfectly now again.

Fabianx’s picture

Without whitespace change...

webchick’s picture

Priority: Normal » Major

This should really be at least major.

Loading #2183231: Make ContentEntityDatabaseStorage generate static database schemas for content entities (486 comments) takes around 1.2 minutes on my machine (via Safari's network inspector). A 30% speed increase from that sounds like a great start!

Fabianx’s picture

On special request by webchick two screenshots of issues with 220 and 146 comments:

node/15365 - threaded; handbook

74% saved time

node/2280195 - issue

50% saved time

Fabianx’s picture

I did some more testing and I still think this is ready. After more investigation I think I'll be able to remove the @todos ...

@drumm: Any chance you'll be able to review that?

I will open a new issue to track the new render_cache-7.x-2.x stuff that should theoretically allow advanced node, block and page caching - similar to Drupal 8 render_cache. This is a huge WIP still, so this will continue to serve as the stable version here, but putting that here, because it means my sandbox could be unstable and is not usable for testing the change once I deploy the experimental features test wise.

drumm’s picture

Issue summary: View changes

@drumm: Any chance you'll be able to review that?

I've done a bit of preliminary review while on staff retreat and vacation, but not enough to really comment on substantively quite yet. Everything does look great as far as I can tell, so we went ahead and scheduled a deployment window, since we announce those ahead of time now.

I started a quick deployment section of the issue summary.

drumm’s picture

drumm’s picture

drumm’s picture

    // @todo Maybe disable caching for preview?
    $hash['in_preview'] = isset($entity->in_preview);

Let's disable that. There isn't much of a point in caching previews, especially if it alters the hash.

    // @todo Maybe disable caching for threaded comments?
    $hash['divs'] = isset($entity->divs) ? $entity->divs : '';
    $hash['divs_final'] = isset($entity->divs_final) ? $entity->divs_final : '';

Can you explain why we might want to disable for threaded comments? I can't think of any drawbacks offhand. I expect divs to stay the same for any particular comment.

Fabianx’s picture

Status: Needs review » Needs work

#24: Thanks for the review!

- Yes, I will disable comment caching if the preview flag is set.

- I think for nested discussions the divs can change some, but on the other hand it is a valid variation and the speed ups are very nice there, too. I guess we will just need to monitor memcache cache growth to see the whole impact of this feature. I'll add a kill switch for nested divs - just in case.

drumm’s picture

Issue summary: View changes

  • drumm committed 8e7f33b on 7.x-3.x authored by Fabianx
    #2299547 Implement render_cache module for comments
    
drumm’s picture

Issue tags: +needs drupal.org deployment

I went ahead and committed #17, with completely removing the in_preview-related code. In my testing, I didn't see caching being hit at all on comment preview.

A killswitch for nested divs isn't a blocker to deployment, so proceeding with getting this on staging.

drumm’s picture

This is running on https://git7site.devdrupal.org/ for testing.

drumm’s picture

Issue tags: -needs drupal.org deployment

Now deployed on Drupal.org. https://www.drupal.org/node/2183231 loads in 10-20 seconds for me now.

I'm tempted to mark this as fixed, I'm not sure we need a killswitch for nested divs.

Fabianx’s picture

Status: Needs work » Fixed

Yes, this is fixed! Thanks so much!

The preview is never needed to be added to the hash as its displayed via comment_view() instead of entity_view(), which means that it is not run through the caching function.

tim.plunkett’s picture

  • drumm committed 9ae34bf on 7.x-3.x
    #2299547 Remove unnecessary todo.
    

Status: Fixed » Closed (fixed)

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

Wim Leers’s picture