A way to skip the render process of all displayed entities.
For a project, we needed a caching solution which is not time-based and deals with logged in users. So we can't use Varnish and the caching methods of Views and Panels.
During the developement we built a module called Display Cache.
The idea
An entity, which does not change, does not need to render again and again.
So we provide a wrapper function, which calls entity_view() and drupal_render() and caches the result.
Next time the function is called, just the cached html will be returned.
Every time an entity changes in any way, the cache entries for this entity will be flushed.
Views and Panels integration
We always use Panels and Views in our projects, so the next step was to provide plugins for ctools and views. These plugins call the wrapper function.
With the combination of the wrapper function and the plugins we were able to skip the render process of all entites.
So, what is your opinion?
I created a sandbox project with a little more explanation: http://drupal.org/sandbox/Caseledde/1970904
Comments
Great!
Entity rendering is crazy slow, so I have a very similar solution for one of my sites, and it makes a huge difference. So, needless to say I think this is great, but caching is hard, and as soon as you have any fanciness going on with your entities, things start becoming less useful. A couple of problematic things that I found are:
Anyways, it sounds like a lot of work has gone into this, so I will probably drop my custom module. I just hope that there are plenty of places to hook in so that other modules can add the kind of functionality needed to address the previously given problematic cases.
Looking forward to checking the code.
I've been working on caching
I've been working on caching whole lists of entities in my entity_lister module. After reading fmizzell's post I saw I hadn't been caching with roles in mind -- oops. So, I fixed this by checking the user's role(s), imploding them into a comma-separated string and adding them to my cache ID. Looks like the same approach would work in display_cache_get_rendered_entity().
@Caseledde, I will keep an eye on your progress to see what I can learn. Just as you have done, I avoid the time-based approach. I delete the cache entry whenever there are relevant content changes. As your module progresses I may provide an optional integration for those who would rather cache at the individual entity level, rather than (or in addition to?) the list level. Looks like it would be quite easy for me to provide that option.
Thx
Thanks for your
Thanks for your responses.
Yep, caching is hard and I know the problems you have mentioned.
Here is just a little code snippet to show how we solve the relations problem:
<?php
/**
* Implements hook_node_insert().
*/
function my_module_node_insert($node) {
my_module_node_save($node);
}
/**
* Implements hook_node_update().
*/
function my_module_node_update($node) {
my_module_node_save($node);
}
/**
* Shared saving routine between my_module_node_insert() and
* my_module_node_update().
*/
function my_module_node_save($node) {
if ($node->type === 'sub_content') {
$field = 'field_reference';
}
else {
return;
}
$item = field_get_items('node', $node, $field);
if (!empty($item)) {
$parent_id = $item[0]['target_id'];
// Change parents changed date to flush caches from entitycache and display_cache.
$parent = node_load($parent_id);
$parent->changed = $node->changed;
node_save($parent);
}
}
?>
Cache by roles
Display Cache now provides independent cache entries for distinct role sets. It is configurable for each plugin instance and can be used in display_cache_get_rendered_entity($entity_type, $entity_id, $view_mode, $roles = array()) as well.
$roles is optional. If $roles is set it has to be an array with a set of roles.
Update
After a complete refactoring, some facts has changed:
Quick question ...
First of all, beautiful! But I saw this code:
$entity = entity_key_array_by_property(array($entity), $info['entity keys']['id']);
$render_array = $info['entity view callback']($entity, $view_mode, $langcode, $entity_type);
$render_array = $render_array[$entity_type][$entity_id];
cache_set($cid, $render_array, 'cache_display_cache');
So we are actually not skipping the rendering step, just the building of the render array. Shouldn't we render that array, and save the html in the cache?
The caching of the renderable
The caching of the renderable array ist just additional and just possible if entity_view() is called. It does not work with node_view() for example.
The caching of the rendered html is located in display_cache_entity_view_alter() and display_cache_field_attach_view_alter().
These functions add the '#cache'-attribute to the renderable array. Thus the core is responsible for the caching:
<?php
$build['#cache'] = array(
'keys' => $keys,
'bin' => 'cache_display_cache',
'granularity' => $display_settings['granularity'],
);
?>
The real caching mechanism is located in drupal_render():
<?php
// Try to fetch the element's markup from cache and return.
if (isset($elements['#cache'])) {
$cached_output = drupal_render_cache_get($elements);
if ($cached_output !== FALSE) {
return $cached_output;
}
}
?>
Ahhh
I thought that the #cache key was something internal to display_cache, but now I see it is some functionality provided by core. Great!
During my testing I noticed that nothing was getting cached b/c entity_entity_info_alter was being called after display_entity_info_alter. I noticed you took care of making sure that entity_view_alter is called last, but I am guessing that the same should be done for entity_info_alter.
I guess I should move this kind of stuff to the issue queue :)
Ready for review
I added this module to the issue cue for drupal.org project applications.
http://drupal.org/node/2001710
I would be glad to all reviews.