Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Caching particular partial rails 3.0.x

I have a home page which has some partials rendered all over the page.And it also has session header as well(login). Partial contains set of books paginated. Now I want to cache this partial as it is getting updated once in a week.

  • Question 1 : How do I cache that particular partial (Without hitting db) ?
  • Question 2 : How do I delete(expire) cached content when I update that books model ?
like image 216
Ghost Rider Avatar asked Dec 03 '12 17:12

Ghost Rider


People also ask

How do I cache a page in rails?

In order to use page and action caching you will need to add actionpack-page_caching and actionpack-action_caching to your Gemfile . By default, caching is only enabled in your production environment. You can play around with caching locally by running rails dev:cache , or by setting config. action_controller.

How use Redis caching in Rails?

To use Redis as a Rails cache store, use a dedicated cache instance that's set up as an LRU (Last Recently Used) cache instead of pointing the store at your existing Redis server, to make sure entries are dropped from the store when it reaches its maximum size.

What are partials in Rails?

Rails Guides describes partials this way: Partial templates - usually just called "partials" - are another device for breaking the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file.

What are view caches?

View caching in Ruby on Rails is taking the HTML that a view generates and storing it for later.


2 Answers

You're looking for fragment caching here, which occurs on the view layer. Fragment caching and expiration of stored contents is surprisingly easy to do. You have a list of books, so let's say your view looks a bit like this:

<ul>
  <% @books.each do |book| %>
    <li><%= book.name %></li>
  <% end %>
</ul>

To enable caching for just this bit, simply wrap it in cache:

<% cache do %>
  <ul>
    <% @books.each do |book| %>
      <li><%= book.name %></li>
    <% end %>
  </ul>
<% end %>

Of course, this doesn't name the cache or do anything really special with it... while Rails will auto-select a unique name for this cache fragment, it won't be really helpful. We can do better. Let's use DHH's key-based cache expiration technique and give the cache a name relating to its content.

<% cache ['book-list', *@books] do %>
  <ul>
    <% @books.each do |book| %>
      <li><%= book.name %></li>
    <% end %>
  </ul>
<% end %>

Passing arguments into cache builds the cache key from the supplied arguments. Strings are passed in directly -- so, here, the cache will always be prefaced with 'book-list'. This is to prevent cache collisions with other places you might be caching the same content, but with a different view. For each member of the @books array, Rails will call cache_key: for ActiveRecord objects, this yields a string composed of its model, ID, and crucially, the last time the object was updated.

This means that when you update the object, the cache key for this fragment will change. In other words, it's automatically getting expired -- when a book is updated, this cache statement will search for a nonexistent key, conclude it doesn't exist, and populate it with new content. Old, stale content will linger in your cache store until evicted by memory or age constraints (memcached does this automatically).

I use this technique in a number of production applications and it works wonderfully. For more information, check out that 37signals post, and for general caching information in Rails, see the Ruby on Rails caching guide.

like image 166
Veraticus Avatar answered Oct 27 '22 02:10

Veraticus


"There are only two hard problems in Computer Science: cache invalidation and naming things."
-- Phil Karlton

Caching


The Rails Guide to caching is probably always a good entry point for the built in caching strategies rails has to offer. Anyway here comes my very easy approach to caching

# _partial.html.erb
<% cache(some_key, :expires_in => 1.week) do %>
   <%# ... content %>
<% end %>

Now some some_key can be any string as long as it is unique. But then again lets try to be a bit more clever about it and make the key somehow dependent on the list of books. Say you actually pass in the array of books some query returned then rails calls cache_key on each of its entries and eventually constructs a unique key for this collection. So when the collection changes the key changes. Thats because cache_key is implemented in ActiveRecord::Base and thus available on all Models. And further more it even uses the timestamps if available.

But then again this will hit the db every time a request is made. The same in code:

# controller_method
@weekly_books = Books.where 'condition'

# _partial.html.erb
<% cache(@weekly_books) do %>
   <%# ... content %>
<% end %>

To avoid hitting the db to often you can also cache the query its self by wrapping the call:

# Book.rb
def self.weeklies
  Rails.cache.fetch("book_weeklies", :expires_in => 1.day) do
    Books.where 'condition'
  end
end

# controller_method
@weekly_books = Books.weeklies

# _partial.html.erb
<% cache(@weekly_books) do %>
   <%# ... content %>
<% end %>
like image 32
krichard Avatar answered Oct 27 '22 01:10

krichard