views:

46

answers:

1

What's the best way to cache a paginated result set with rails and memcached.

For example

posts controller

def index
@posts = Rails.cache.fetch('all_posts') do
Post.paginate(:conditions => ['xx = ?', yy], :include => [:author], :page => params[:page], :order => 'created_at DESC')
end
end

This obviously doesn't work when the params[:page] changes...I can change the key to "all_posts_#{params[:page]}_#{params[:order]_#{last_record.created_at.to_i}"

But then there could be several possible order (recent, popular, most voted etc) and there will be a combination of pages and orders..lots of keys this way.

Problem #2 - It seems when I implement this solution the caches get written correctly and the page loads fine during the first call do a paginate action but when I click back on the same page i.e. page1, with "recent" order, it seems the browser does not even make a call to the server. I don't see any controller action being called in the production log??

I am using passenger, REE, memcached, and rails 2.3.5. Firebug shows no requests being made....

Is there a simples/more graceful way of handling this?

Thanks

A: 

When it comes to caching there is no easy solution. You might cache every variant of the result, and thats ok if you implement auto-expiration of entries. You can't just use all_posts, because this way you will have to expire dozens of keys if posts will get changed.

Every AR model instance has the .cache_key based on updated_at method, which is prefered way, so use this instead of last record. Also don't base your key on last record, because if some post in the middle will get deleted your key wont change. You can use logic like this instead.

class ActiveRecord::Base         
  def self.newest
    order("updated_at DESC").first
  end    
  def self.cache_key
    newest.nil? ? "0:0" : "#{newest.cache_key}:#{count}"
  end
end

Now you can use Post.cache_key, which will get changed if any post will get changed/deleted or created.

In general I would just cache Post.all and then paginate on this object. You really need to do some profiling to find bottle necks in your application.

Besides, if you want to cache every variant, then do fragment/page caching instead.

If up to you how and where to cache. No one-way here.

As for the second part of the question, there is way to few hints for me to figure an answer. Check if the browser is making a call at all LiveHTTPHeaders, tcpdump, etc.

mdrozdziel
Thanks. For the second problem, I think this other question might give you more hints. http://stackoverflow.com/questions/3743268/max-age-with-nginx-passenger-memcached-rails2-3-5. for some reason the max-age is getting set even though I am not doing anything in my rails app, still trying to understand the problem. I hear nginx is a reverse proxy as well, may be it has someting to do with that?
badnaam
with this "order("updated_at DESC").first" approach, will that query run everytime a request comes in?
badnaam
Yes, it will run everytime, unless you combine this with another caching approch. But in general there is no other way to do it. You have to check if the cache is valid by looking into your database. But in most cases this speed things up a lot anyway.
mdrozdziel