Developer:Caching

From myExperiment

Jump to: navigation, search

Caching

By Tom Eveleigh, 18th September 2008


Contents


Caching in myExperiment

myExperiment uses fragment caching in a number of places on the site, including the sidebar, the homepage, and the listings of workflows, files, packs and groups.

The path where the cache is stored is configurable and will be referred to here as [cache_path]. The default location is 'tmp/cache/hostname'.

Sidebar

Four sections of the sidebar are cached for each logged-in user as they are specific to the user. They are:

  • the user monitor, stored in [cache_path]/sidebar_cache/user_monitor/user_id.cache,
  • the asset manager, stored in [cache_path]/sidebar_cache/asset_manager/user_id.cache,
  • the user favourites, stored in [cache_path]/sidebar_cache/user_favourites/user_id.cache,
  • and the user tags, stored in [cache_path]/sidebar_cache/user_tags/user_id.cache

The popular tags section is cached once and served to all users as it is not user specific, and is stored in [cache_path]/sidebar_cache/tags.part=most_popular_tags.cache

Homepage

Five sections of the homepage are cached for each logged-in user as they are specific to the user. They are:

  • the list of most recently updated items, stored in [cache_path]/home_cache/updated_items/user_id.cache,
  • the latest groups, stored in [cache_path]/home_cache/latest_groups/user_id.cache,
  • the latest comments, stored in [cache_path]/home_cache/latest_comments/user_id.cache,
  • the latest reviews, stored in [cache_path]/home_cache/latest_reviews/user_id.cache,
  • and the latest tags, stored in [cache_path]/home_cache/latest_tags/user_id.cache

The announcements section is cached once and served to all users as it is not user specific, and is stored in [cache_path]/home_cache/announcements.cache

Listing Pages

On the listing pages for workflow, files, packs and groups, a cache fragment is created for each object listed on the page and is stored in:

  • Workflows - [cache_path]/workflows_cache/listing/workflow_id.cache
  • Files - [cache_path]/files_cache/listing/blob_id.cache
  • Packs - [cache_path]/packs_cache/listing/pack_id.cache
  • Groups - [cache_path]/groups_cache/listing/network_id.cache

A cache fragment for the tags relating to that class is also created and stored in [cache_path]/[workflows | files | packs | groups]/all_tags.cache

Miscellaneous

The page footer is also cached and stored in [cache_path]/global_cache/footer.cache

Sweepers

Sweepers are responsible for observing models and expiring cache fragments when the model changes. There is generally one sweeper per model, although one sweeper can monitor multiple models if necessary.

The sweepers are stored in the app/models/ directory and use the naming convention [model_name]_sweeper.rb. They implement the callbacks 'after_create', 'after_update' and 'after_destroy' so that they are called when the model changes.

As many of the sweepers in the code expire the same fragments, many cache expiry methods are in the module lib/caching_helper.rb, and are called from the sweepers. This avoids code repetition and keeps the system maintainable.

A Simple Example

app/views/announcements/index.html:

 <% cache(:controller => 'announcements', :action => 'index') do -%>
   <% announcements.each do |a| %>
     <%= a.body %>
   <% end -%>
 <% end -%>

The above view code would cache the list of announcements in [cache_path]/announcements/index.cache

To expire this cache fragment when an announcement is added, modified or deleted, we would need the following sweeper that monitors /app/models/announcement.rb

app/models/announcement_sweeper.rb:

 class AnnouncementSweeper < ActionController::Caching::Sweeper
   observe Announcement
 
   def after_create(announcement)
     expire_fragment(:controller => 'announcements', :action => 'index')
   end
 
   def after_destroy(announcement)
     expire_fragment(:controller => 'announcements', :action => 'index')
   end
 
   def after_update(announcement)
     expire_fragment(:controller => 'announcements', :action => 'index')
   end
 end

The final step is for the controller wake the sweeper up when it receives a request that may alter the model.

app/controllers/announcements_controller.rb:

 class AnnouncementsController < ApplicationController
   cache_sweeper :announcement_sweeper, :only => [ :create, :update, :destroy ]
 
   ...
 end

The 'cache_sweeper' line in the controller wakes the sweeper up whenever the controllers' create, update or delete methods are called. These three steps are all that is required for a simple event-based cache expiry system.

A User Specific Cache Example

If we were to alter the way announcements in the above example worked, so that each user is presented with only announcements that they have created, we would have to update the way the caching works so that one cache fragment is created for each user, as shown in this example.

app/views/announcements/index.html:

 <% cache(:controller => 'announcements', :action => 'index', :id => (logged_in? ? current_user.id : 0)) do -%>
   <% announcements.each do |a| %>
     <%= a.body if a.user_id == (logged_in? ? current_user.id : 0) %>
   <% end -%>
 <% end -%>

The cache fragment is now stored in [cache_path]/announcements/index/user_id.cache, with the user_id being the id of the current user or 0 if they are not logged in.

The sweeper then needs to be updated to expire the correct cache based on the user_id of the announcement that has changed.

app/models/announcement_sweeper.rb:

 class AnnouncementSweeper < ActionController::Caching::Sweeper
   observe Announcement
 
   def after_create(announcement)
     expire_fragment(:controller => 'announcements', :action => 'index', :id => announcement.user_id)
   end
 
   def after_destroy(announcement)
     expire_fragment(:controller => 'announcements', :action => 'index', :id => announcement.user_id)
   end
 
   def after_update(announcement)
     expire_fragment(:controller => 'announcements', :action => 'index', :id => announcement.user_id)
   end
 end

Using an ID as the name of the cache can be extended to any type of object, for example workflows or groups.

Personal tools