在学习了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
我们已经有一篇文章,一个评论,两者由不同的作者撰写。目标是在以下情况下更改文章的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"
但对于第三种情况则不是这样。
1.9.3-p194 :040 > article.author.update_attribute('name', 'Adam A.')
1.9.3-p194 :041 > article.cache_key
=> "articles/1-20130412185913"
让我们为Article
定义一个复合的cache_key
方法。
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"
胜利!但是对于第四种情况,这种方法无效。
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"
那么现在还有什么选择?我们可以考虑在 Author
模型上使用 has_many
关联,但是 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"
虽然这个方法确实可以生效,但会对性能产生巨大影响,因为需要一篇篇文章和评论的加载、实例化和更新,一个接一个地进行。我认为这不是一个合适的解决方案,那么有什么更好的方法呢?
当然,37signals的用例/示例可能不同:项目 -> 待办事项列表 -> 待办事项
。但我想象一个单独的待办事项也属于一个用户。
有什么方法可以解决这个缓存问题吗?
articles.update_all(updated_at: Time.now)
这样的操作,这将为文章(和评论)产生一个操作。 - numbers1311407update_all
只执行 SQL,不会执行回调函数,因此后续的touch
操作不会发生,并且内存中对象的cache_key
不会重新生成。 - Graham Conzetttouch
调用。如果用户更新了他的姓名并且我们在他的评论上调用update_all
,则评论中的belongs_to:article,touch:true
不会触发,并且文章的片段缓存将不会过期。至少这就是我看到的,请纠正我如果我错了。您可以手动过期用户有评论的所有文章,但随着树的增长,这将变得难以维护。不幸的是,我目前没有看到其他选择。 - Graham Conzett