How to implement CACHING IN RAILS 3

Why we need to implement?

  • Caching is extremely useful to implement for web applications.
  • When an application is getting a lot of requests and New Relic detects a strain on your instances, it’s probably time to look into caching.
  • Increase performance of the web application.

Serving static content to a visitor rather than something generated upon request.

There are basically five types of Rails caching techniques in Rails 3:

  1.  HTTP Caching
  2.  Page Caching
  3. Action Caching
  4. Fragment Caching
  5. SQL Caching

Rails provides by default HTTP Caching.

  • Rails caching is disabled by default in the development environment. Make sure you have below the parameter value below set to true in your Rails app config file.

# Inside config/environments/development.rb
         config.action_controller.perform_caching = true

We can also set the page cache directory
config.action_controller.page_cache_directory = “#{Rails.root}”+”/public/cache_directory/”

We can also set the default cache directory
        config.action_controller.cache_store = :file_store, “#{Rails.root}” +”/tmp/private_cache/”

HTTP Caching:

  • By default it is included in Rails 3 for better performance of Asset pipeline.
  • HTTP caching works at the protocol level. It uses a combination of headers and response codes to indicate whether the user agent should make a request or use a locally stored copy instead.
  • Rails added HTTP cache headers to prevent re-fetching identical assets across requests. Requests for assets come with multiple headers defining how that asset should be stored locally:
  • Uses HTTP headers (Last-Modified, ETag, If-Modified-Since, If-None-Match, Cache-Control) to determine if the browser can use a locally stored version of the response or if it needs to request a fresh copy from the origin server.
  • Rails makes it easy to use HTTP caching, however the cache is managed outside your application.

We can also implement in controller action as:

Ex:
def index
@articles = Article.all
expires_in 3.hours, :public => true
end

Some other strategies are also there:
expire_now if param[:id] == ‘1’   #conditional

Headers of HTTP Cache:
1. Age
2. Cache-control
3. Etag
4. Last-Modified

Suppose we don’t want to set HTTP cache, then we can achieve it by using before filter

before_filter :set_as_private

def set_as_private
expire_now
end

Page Caching :

  • Page Caching has been removed from Rails 4(moved to a separate gem).
  • In Rails Page Caching, whenever a request is sent to the server, the Rails server would check for the cached page and if that exists it would be served. If it does not exist, Rails server generates the page & cache it. Hence the Rails app won’t have to generate it again during the next request.
  • The output of an entire controller action is cached to disk, with no further involvement by the Rails dispatcher.
  • Significantly speedup the page load.
  • Reduce resource usage on server.

To implement this:

In controller:
class ArticlesController < ApplicationController
caches_page :index

def index
@articles = Article.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @articles }
end
end
end

  •       It will create a page inside public folder as public/articles.html.

In console, you can see

            Started GET “/articles” for 127.0.0.1 at 2013-07-01 12:54:35 +0530
            Processing by ArticlesController#index as HTML
            Article Load (0.4ms) SELECT `articles`.* FROM `articles`
            Write page /home/Amit/rails-cache-example-app/public/articles.html (0.4ms)
            Completed 200 OK in 1077ms (Views: 618.3ms | ActiveRecord: 171.6ms)

  • We will put our expire logic in sweepers.
  • To load the sweepers

#  config/development.rb

 config.autoload_paths += %W( #{Rails.root}/app/sweepers )

Then, in app/sweeper/article_sweeper.rb

class ArticleSweeper < ActionController::Caching::Sweeper
observe Article

def after_save(article)
expire_cache(article)
end

def after_destroy(article)
expire_cache(article)
end

def expire_cache(article)
expire_page :controller => ‘articles’, :action => ‘index’
end
end

Fragment Caching:

  • With fragment caching we can cache individual parts of a view.
  • Reduction of server load and faster web page generation, which means increased usability.

To implement this:

EX:

<% cache ‘recent_articles’ do %>
<div id=”recent_articles”>
<h2>Recent Articles</h2>
<ul>
<% for article in Article.find_recent %>
<li><%= article.title %></li>
<% end %>
</ul>
</div>
<% end %>

  • So this fragment will be be saved as html.
  • If you refresh the page, can see in the console

    Started GET “/articles” for 127.0.0.1 at 2013-07-01 12:54:35 +0530
    Processing by ArticlesController#index as HTML 
    User Load (10.8ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
    Article Load (0.3ms) SELECT `articles`.* FROM `articles` ORDER BY created_at desc LIMIT 2
    Write fragment views/recent_articles (26.9ms)

> On second request, you can see the difference

    Article Load (0.4ms) SELECT `articles`.* FROM `articles`
    Read fragment views/recent_articles (0.2ms)

> To expire the fragment, we can do it by sweeper

EX:
def expire_cache(article)
expire_fragment ‘recent_articles’
end

Action Caching:

  • The output of an entire controller action is cached to disk, but the Rails dispatcher is still involved in subsequent requests, and controller filters are executed.
  • If there’s anything that has to change on every request or specific to an end user’s view of that page, page caching is not an option. On the other hand, if all we need to do is run some filters that check conditions before displaying the page requested, the caches_action method will work. It’s almost like page caching, except that controller filters are executed prior to serving the cached HTML file. That gives you the option to do some extra processing or even redirect if necessary.

EX:

class AriclesController < ApplicationController

before_filter :check_logged_in,   :only => [:index]

caches_action :index

def index
@articles = Article.limit(10)
end

private

def check_logged_in
redirect_to :action => ‘different_action’ unless logged_in?
end

end

> Clear the cache by sweeper

EX:
def expire_cache(article)
expire_action :index
end

SQL Caching:

  • Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again.

We can manually store a cache

EX:

> rails c

# Rails.cache.write takes two values: key and a value
> Rails.cache.write ‘foo’, ‘bar’
=> true

# We can read an object back
> Rails.cache.read ‘foo’
=> “bar”

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s