Broken links when doing fragment caching in Rails
Suppose there is a blog with posts, comments and users which can comment. Users have SEO-friendly URLs such as http://localhost:3000/users/john (this can be easily done by using permalink_fu).
The model uses touch to simplify caching:
class Post
has_many :comments
end
class Comment
belongs_to :post, :touch=>开发者_如何学JAVA;true
end
And the view code would be something like this:
<%= cache @post do %>
<h1><%= @post.title %></h1>
<%= @post.content %>
<%= @post.comments.each do |comment| %>
<%= link_to h(comment.user), comment.user %> said:
<%= comment.content %>
<% end %>
<% end %>
Now suppose John changes his nick to Johnny -- his URL changes to http://localhost:3000/users/johnny. Because of doing fragment caching on posts and comments, John's comments will point to John's wrong URL unless the fragment is expired. It can be possible to manually touch or expire all posts that contain comments by John in this example, but in a complex application this would require very complex queries and seems very error-prone.
What's the best practice here? Should I use non-SEO-friendly URLs such as /users/13 instead of /users/john ? Or maybe keep a list of old URLs until the cache is expired? No solution looks good to me.
EDIT: Please note this is just a simplified example -- it's definitely very simple to query posts and touch them in this case. But a complex app implies many relationships among objects which makes it hard to track every object that has a reference to a user. I've researched a bit on this -- Facebook only allows setting your username once, so this problem doesn't exist.
I don't see it would be complex to expire cached posts. Set up a sweeper:
class UserSweeper < ActionController::Caching::Sweeper
observe User
def after_save(user)
user.comments.collect(&:post).each do |post|
expire_fragment post
end
end
I'd use a before_save filter for example
class User
has_many :posts
before_save :touch_posts
private
def touch_posts
Post.update_all({:updated_at => Time.now}, {:user_id => self.id}) if self.login_changed?
true
end
end
One query to update every user's post. Not really complex.
精彩评论