Rails Conditional Fragment Cache

Posted by Jake Good
on Mar 02, 07

There have been lots of caching articles coming out for Rails recently… especially when things heat up and more Rails based sites are hitting digg and slashdot. There was even a recent PeepCode episode



And yet, when I’m caching things on my site… there are certain situations where I don’t have the right tools to do what I want.



Ignoring page and action caching, you have fragment caching left… and one of the drawbacks of using a memcache fragment cache is that you can’t use regular expressions to delete cache entries.



So imagine you have an inbox and you want to cache the pages of the inbox. Think about it, doing expensive queries on messages takes up a lot of resources… if there are a lot of users involved.



you have something like this setup: http://localhost:3000/inbox … and it’s paginated http://localhost:3000/inbox/page/1



it’s all going back to the same action/view no matter what page it’s on… you’ll end up fragment caching each page…



Well in the case of a message inbox, I had two problems:




  • Users pay most attention to the first page of their inbox… If you have pagination of 25 messages, you’ll rarely go back.


  • Since we’re caching pages, we’ll want to clear those caches when things happen (deletes, adds, etc)… but we can’t clear out N number of pages due to memcache not allowing regex deletions…




We have a few solutions:




  • Guess and/or calculate the number of pages that could be in the cache… and brute force the deletion of the cache. That can get expensive.


  • We could use a conditional cache and only cache the first page of the inbox (where I’m guessing the user will spend most of their time).




Conditionally cache? Well shit… how do I do that? The default fragment caching is a block structure.



Here’s something I played around with and found useful



With normal caching:




<% cache @my_cache_key do %>
# some very expensive or useless rhtml
<% end %>


But with a conditional cache, we can reuse the same HTML whether we’re paged or not.




<% cache_if @at_first_page, @my_cache_key do %>
# some very expensive or useless rhtml
<% end %>


Look cool? Here’s how I did it…



Crack open application_helper.rb and put in:




def cache_if(condition, name = {}, &block)
if condition
@controller.cache_erb_fragment(block, name)
else
block.call
end
end


Now there’s probably some nifty ruby syntactic sugar I could take advantage of here, but this will do!



Kinda nice… to be able to cache only one condition of a page… there you have it, my addition to the Rails caching extravaganza

Comments

Leave a response

  1. Christian BrousseauMar 02 07 @ 03:40PM

    This is great! Thanks a million, it helped me alot.

Comment