Recipe 12.4. Expiring Cached Pages


Problem

You're caching pages of your application using the Rails page-caching mechanism, and you need a system for removing cached pages when the data that was used to create those pages changes.

Solution

To remove pages that have been cached when content is updated, you can call expire_page in the update action of your controller; for example:

def update   @recipe = Recipe.find(params[:id])   if @recipe.update_attributes(params[:recipe])     flash[:notice] = 'Recipe was successfully updated.'     expire_page :controller => "recipes", :action => %W( show new ),                                            :id => params[:id]     redirect_to :action => 'show', :id => @recipe    else         render :action => 'edit'   end end

Caching expiration often gets more complicated when you have pages that share content form related models, such as an article page that displays a list of comments. In this case, when you update a comment, you need to make sure that you expire the cache of the comment you're updating as well as its parent article. Adding another expire_page call takes care of this:

def update   @comment = Comment.find(params[:id])   if @comment.update_attributes(params[:comment])     flash[:notice] = 'Comment was successfully updated.'     expire_page :controller => "comments", :action => "show",                                             :id => @comment.id     expire_page :controller => "articles", :action => "show",                                             :id => @comment.article_id     redirect_to :action => 'show', :id => @comment   else     render :action => 'edit'   end end

This example removes the cached page of the comment being updated as well as the related article page based on the article_id from the @comment object.

Discussion

Rails page caching usually starts out being a simple solution to performance problems but can quickly become a problem of its own when page cache expiration becomes more complex. The symptoms of caching complexities are usually pages that don't get expired when they should.

One approach to cache expiration complication is to delete all the files in a particular area of the cache when any of the cached data has changed or been deleted. Additionally, Rails provides a facility for organizing your cache expiration code called sweeper classes, which are sub classes of ActionController::Caching::Sweeper.

The following shows how to use a sweeper to remove all cached files in an application when either an article or comment is updated or deleted.

First, let's assume you've set your page cache directory to a directory beneath public:

config/environment.rb:

config.action_controller.page_cache_directory = \                                           RAILS_ROOT+"/public/cache/"

To keep things organized, you can store your cache sweepers in app/cachers. To get Rails to include this directory in your environment, add the following to your configuration via environment.rb:

config/environment.rb:

Rails::Initializer.run do |config|   # ...   config.load_paths += %W( #{RAILS_ROOT}/app/cachers ) end

Then define a CacheSweeper class with the following:

class CacheSweeper < ActionController::Caching::Sweeper   observe Article, Comment   def after_save(record)     self.class::sweep   end      def after_destroy(record)     self.class::sweep   end      def self.sweep     cache_dir = ActionController::Base.page_cache_directory     unless cache_dir == RAILS_ROOT+"/public"       FileUtils.rm_r(Dir.glob(cache_dir+"/*")) rescue Errno::ENOENT     end   end end

The CacheSweeper acts as an observer (observing changes to the Article and Comment classes) and also as a filter. The filtering behavior is set up by passing the name of the sweeper class and conditions about what actions it is to filter to the cache_sweeper method in your controller:

class ArticlesController < ApplicationController   caches_page :show   cache_sweeper :article_sweeper, :only => [ :edit, :destroy ]   #... end

Any time an article record is saved or deleted the following is called:

FileUtils.rm_r(Dir.glob(cache_dir+"/*")) rescue Errno::ENOENT

This action simply removes the entire contents of your cache directory. Whether you choose this method or a more granular cache expiration method depends on the specific performance requirements of your application.

See Also

  • memcached can be set up to automatically expire your cache; see Section 12.7"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net