Gotcha with cache_fu and permalinks
This is an issue I had recently in a project with cache_fu. Models that I found and cached based on permalinks weren't expiring the cache correctly when getting updated. Here's an example scenario.
Say you have a blog with posts. However, instead of using a url like http://paulscoolblog.com/posts/23 you want something that's more search engine friendly and readable for the user. So you use a permalink (maybe using the permalink_fu plugin) that's auto-generated based on the title of the post. This post would have a url that looks something like http://paulscoolblog.com/posts/gotcha-with-cache_fu-and-permalinks.
In your controller's show method you'd probably find the post like this:
@post = Post.find_by_permalink(params[:permalink])
However, you'd want to do the caching thing so you'd actually do this:
@post = Post.cached(:find_by_permalink, :with => params[:permalink])
The problem that I ran into, which is probably obvious to anyone familiar with cache_fu, was that when updating the post, it wouldn't expire the cache. That part of the post model looks like this:
class Post < ActiveRecord::Base
before_save :expire_cache
...
end
Do you see it? The issue is that when expire_cache gets called on the object, it expires the key Post:23 from the cache (assuming 23 was the id of the post). However, when the post was cached using the cached(:find_by_permalink ...) method, it put the post object into the cache with a key of Post:find_by_permalink:gotcha-with-cache_fu-and-permalinks.
Luckily, it's a fairly simple fix. If you have a model that is commonly accessed through permalinks, just write your own cache expiry method that looks for both keys and expires them.
No disrespect but this post is useless if you're not going fully explain the solution to the problem.
Off the top of my head the model should look something like:
class Post < ActiveRecord::Base
after_save do |post| # or before_save (does it even matter?)
post.reset_cache
Post.expire_cache("permalink:#{post.permalink}")
end
def self.permalink_cached(permalink)
get_cache("permalink:#{permalink}") do
find_by_permalink(permalink)
end
end
end
Then in your controller you would do:
@post = Post.permalink_cached(params[:permalink])
I haven't tested the above code but it should get one on the right track. Hopefully this will help someone who came to this page experiencing this problem and is saying WTF due to many rails bloggers not completely explaining a topic they are blogging about.
Posted by: hypa | November 15, 2008 at 02:21 AM
First, you must have missed the title of my blog. Second, I didn't include exact code because I thought the harder part of the problem was figuring out what was going on. When I hit this problem it took me a while to figure out what was happening and seconds to implement a fix. I assumed that readers using cache_fu would understand what The last sentence of the post meant, which tells you what to do to fix it.
However, this is the great part about comments on blogs. The readers are able to contribute. Your comment adds that extra piece for people who may not have understood what I meant. Congratulations on contributing. Comments are where it starts, ditching the attitude is next.
Posted by: Paul Dix | November 20, 2008 at 01:58 PM