mla*_*erg 23 ruby caching ruby-on-rails fragment-caching russian-doll-caching
在研究了DHH和其他关于基于密钥的缓存过期和俄罗斯娃娃缓存的博客文章之后,我仍然不确定如何处理一种关系类型.具体来说,是一种has_many关系.
我将分享我对示例应用程序的研究结果.这是一个故事讲述,所以坚持下去.假设我们有以下ActiveRecord模型.我们所关心的只是模型的正确改变cache_key,对吗?
class Article < ActiveRecord::Base
attr_accessible :author_id, :body, :title
has_many :comments
belongs_to :author
end
class Comment < ActiveRecord::Base
attr_accessible :article_id, :author_id, :body
belongs_to :author
belongs_to :article, touch: true
end
class Author < ActiveRecord::Base
attr_accessible :name
has_many :articles
has_many :comments
end
Run Code Online (Sandbox Code Playgroud)
我们已经有一篇文章,只有一条评论.两者都是由不同的作者.目标是cache_key在以下情况下对文章进行更改:
因此,默认情况下,我们对案例1和案例2都有好处.
1.9.3-p194 :034 > article.cache_key
=> "articles/1-20130412185804"
1.9.3-p194 :035 > article.comments.first.update_attribute('body', 'First Post!')
1.9.3-p194 :038 > article.cache_key
=> "articles/1-20130412185913"
Run Code Online (Sandbox Code Playgroud)
但不是案例3.
1.9.3-p194 :040 > article.author.update_attribute('name', 'Adam A.')
1.9.3-p194 :041 > article.cache_key
=> "articles/1-20130412185913"
Run Code Online (Sandbox Code Playgroud)
让我们定义一个复合cache_key方法Article.
class Article < ActiveRecord::Base
attr_accessible :author_id, :body, :title
has_many :comments
belongs_to :author
def cache_key
[super, author.cache_key].join('/')
end
end
1.9.3-p194 :007 > article.cache_key
=> "articles/1-20130412185913/authors/1-20130412190438"
1.9.3-p194 :008 > article.author.update_attribute('name', 'Adam B.')
1.9.3-p194 :009 > article.cache_key
=> "articles/1-20130412185913/authors/1-20130412190849"
Run Code Online (Sandbox Code Playgroud)
赢得!但当然这不适用于案例4.
1.9.3-p194 :012 > article.comments.first.author.update_attribute('name', 'Bernard A.')
1.9.3-p194 :013 > article.cache_key
=> "articles/1-20130412185913/authors/1-20130412190849"
Run Code Online (Sandbox Code Playgroud)
那剩下什么选择呢?我们可以对has_many关联做一些事情Author,但has_many不采取{touch: true}选择,可能是有原因的.我想它可以在以下几行中实现.
class Author < ActiveRecord::Base
attr_accessible :name
has_many :articles
has_many :comments
before_save do
articles.each { |record| record.touch }
comments.each { |record| record.touch }
end
end
article.comments.first.author.update_attribute('name', 'Bernard B.')
article.cache_key
=> "articles/1-20130412192036"
Run Code Online (Sandbox Code Playgroud)
虽然这确实有效.它通过加载,实例化和更新每个文章以及由其他文章逐一进行评论而产生巨大的性能影响.我不相信这是一个合适的解决方案,但它是什么?
当然37signals用例/示例可能会有所不同:project -> todolist -> todo.但我想象一个属于用户的todo项目.
如何解决这个缓存问题?
我偶然发现的一种方法是通过缓存键来处理这个问题.has_many_through为文章添加评论者关系:
class Article < ActiveRecord::Base
attr_accessible :author_id, :body, :title
has_many :comments
has_many :commenters, through: :comments, source: :author
belongs_to :author
end
Run Code Online (Sandbox Code Playgroud)
然后在article/show中我们将构造如下的缓存键:
<% cache [@article, @article.commenters, @article.author] do %>
<h2><%= @article.title %></h2>
<p>Posted By: <%= @article.author.name %></p>
<p><%= @article.body %></p>
<ul><%= render @article.comments %></ul>
<% end %>
Run Code Online (Sandbox Code Playgroud)
诀窍在于,commenters无论何时添加,删除或更新注释,从关联生成的缓存密钥都将更改.虽然这确实需要额外的SQL查询来生成缓存键,但它可以很好地与Rails的低级缓存一起使用,并且添加像identity_cache gem这样的东西可以轻松地帮助它.
我想看看其他人是否有更清洁的解决方案.