Expiring Cached PagesProblemYou'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. SolutionTo 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. DiscussionRails 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
|