Home arrow Ruby-on-Rails arrow Caching and Performance Optimization

Caching and Performance Optimization

In this fourth part of a six-part series on optimizing the performance of a Ruby on Rails application, you'll learn about action caching, fragment stores, and more. This article is excerpted from chapter 13 of the book Practical Rails Projects, written by Eldon Alameda (Apress; ISBN: 1590597818).

Author Info:
By: Apress Publishing
Rating: 5 stars5 stars5 stars5 stars5 stars / 4
December 13, 2010
  1. · Caching and Performance Optimization
  2. · Fragment Stores

print this article

Caching and Performance Optimization
(Page 1 of 2 )

Action Caching

Action caching is the second level of caching in Rails. With action caching, the whole page output is still cached, but this time, the request goes through ActionController, and thus the filters are run before the rendering. The most important consequence of this is that you can use action caching even for pages that need authentication.

Action caching is turned on in the same way as page caching. Addcaches_action :indexto the controller, and callexpire_action :indexto expire the action. You won’t get the same raw speed as with page caching, but you will get more flexibility with filtering the requests. The speed would still be plenty fast.

Action caching shares many, but not all, of the problems with page caching. There is still no way to make the page contents dynamic, and complete personalization isn’t possible (although action caching can use the user ID as a key in the cached page).

If the results of page caching are stored in the file system, where do the action caches go? The answer is that it depends. Action caching uses internally the third built-in caching scheme in Rails, fragment caching.

Fragment Caching

Fragment caching is the most granular of the standard caching mechanisms in Rails. With it, you can cache parts of a page. For example, you could cache the contents of a shopping cart like this (in app/views/layouts/application.rhtml):

<% if @cart %>
  <% cache(:controller => "cart", :action => "show",
           :id => @cart) do %>
    <div id="shopping_cart">
    <%= render :partial => "cart/cart" %>
  <% end %>
<% end %>

This would cause the contents of the cache block to be cached, and we could avoid a perhaps expensive database trip on every request a particular user makes.

Thecachemethod takes a hash as its parameter and usesurl_forto build a URL to be used as a key to the cached item. Needless to say, this should be unique. Note that it doesn’t have to be a real, existing URL. In our case, for example, there is no action calledshowinCartController. However, the cache key of a stored cart would be something likeemporium.com/cart/show/179.

Cached fragments are expired with theexpire_fragmentmethod, which takes a hash as its argument, similar to the argument for thecachemethod. In our case, we need to expire the fragment whenever the shopping cart is changed—when we add or remove books to the cart, clear the cart, or check out.

Let’s start withCheckoutController(inapp/controllers/checkout_controller.rb):

def place_order
    @page_title = "Checkout"
    @order = Order.new(params[:order])
    @order.customer_ip = request.remote_ip

    if @order.save
      if @order.process
        flash[:notice] = 'Your order has been submitted,
                         and will be processed immediately.'
        session[:order_id] = @order.id
        # Empty the cart

        expire_fragment(:controller => "cart",
                        :action => "show",
                        :id => cart)
        redirect_to :action => 'thank_you'
        flash[:notice] = "Error while placing
        render :action => 'index'
      render :action => 'index'

Now whenever an order is processed and the shopping cart is cleared, the cached cart fragment is expired.

ForCartController, we use a different approach—a cache sweeper. A cache sweeper is a special kind of an observer. It observes the lifeline of an object and can sweep cached stuff when specific changes (such as create, update, or destroy) are made to the object in question. Create a file calledcart_sweeper.rbinapp/modelsand add the following code to it:

class CartSweeper < ActionController::Caching::Sweeper
  observe Cart, CartItem

  def after_save(record)
    cart = record.is_a?(Cart) ? record : record.cart
    expire_fragment(:controller => "cart",
                    :action => "show",
                    :id => @cart)

You can see that the sweeper looks just about the same as a normal observer. In this case, we observe both theCartobject and theCartItemobjects that belong to it. When either kind of object is saved, we find the relevantCartobject and expire the fragment that belongs to that cart. To make the sweeper work, we need to call it inCartController (app/controllers/cart_controller.rb):

class CartController < ApplicationController
cache_sweeper :cart_sweeper
  before_filter   :initialize_cart

That’s all. Since we want the sweeper to work on all the actions inCartController, we don’t have to specify anything else. If we wanted to restrict the sweeper to only certain actions, we could use the:onlyparameter for that.

blog comments powered by Disqus

- Ruby-on-Rails Faces Second Security Flaw in ...
- Ruby 2.0 Prepped for February 2013 Release
- Why LinkedIn Switched from Ruby on Rails
- Adding Style with Action Pack
- Handling HTML in Templates with Action Pack
- Filters, Controllers and Helpers in Action P...
- Action Pack and Controller Filters
- Action Pack Categories and Events
- Logging Out, Events and Templates with Actio...
- Action Pack Sessions and Architecture
- More on Action Pack Partial Templates
- Action Pack Partial Templates
- Displaying Error Messages with the Action Pa...
- Action Pack Request Parameters
- Creating an Action Pack Registration Form

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 

Developer Shed Affiliates


© 2003-2019 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials