Rails是否有用于使缓存过期的标签?

时间:2022-05-15 04:23:53

Our app uses Rails.cache in the controller to cache some items outside the scope of the view (like meta tags), then uses fragment_caching on the bulk of the view.

我们的应用程序在控制器中使用Rails.cache来缓存视图范围之外的一些项目(如元标记),然后在视图的大部分上使用fragment_caching。

The view caches one main model, but we have used data from 5 other models (not connected by an Association) inside that main cache. It's easy to expire the fragment with a sweeper on the main model, but those additional models also change and need to trigger this page to expire.

该视图缓存了一个主要模型,但我们在该主缓存中使用了来自其他5个模型(未通过关联连接)的数据。使用主模型上的清扫程序很容易使片段过期,但这些附加模型也会更改并需要触发此页面过期。

We can't use a regexp route for deleting the cache keys because we have to reference this cache entry by only the main model -- the other models are determined by an expensive query that we perform inside the cache block in the controller.

我们不能使用正则表达式路由来删除缓存键,因为我们必须仅通过主模型引用此缓存条目 - 其他模型由我们在控制器的缓存块内执行的昂贵查询确定。

Does Rails 3 have a way to essentially use tags to mark a cache entry, so we can trash it when any of the 6 models on the page change, but we can still find the cache entry from only the main model's key?

Rails 3是否有办法基本上使用标记来标记缓存条目,所以我们可以在页面上的6个模型中的任何一个更改时删除它,但我们仍然可以仅从主模型的密钥中找到缓存条目?


Here's some dummy code to express the idea:

这里有一些虚拟代码来表达这个想法:

In the controller

在控制器中

@cache_key = "/page/#{params[:name]}/#{params[:id]}"
unless fragment_exist? ( { :slug => @cache_key })
  # run our processes here that will be needed in the view, 
  # then cache the data that is used outside the view
  Rails.cache.write(@cache_key, { (data goes here) } )
  # run our expensive query here:
  @similar_pages = Page.pricey_query!.limit(5).all
else 
  cached = Rails.cache.read(@cache_key)
end

In the view

在视图中

- cache( {:slug => @cache_key} ) do
 - @similar_pages.each do |page|
  = image_tag page.photos.first.image.url
  -# more pretty stuff here

My goal:

我的目标:

  • Me: "Oh, page @cache_key has changed, let's expire it!"
  • 我:“哦,页面@cache_key已经改变了,让我们过期吧!”
  • Rails: Okay, easy!
  • Rails:好的,简单!
  • Me: "One of the similar pages changed their first photo, what do I do?"
  • 我:“其中一个类似的页面改变了他们的第一张照片,我该怎么办?”
  • Rails: Umm... #(*$^*@ .. does ... not ... compute.
  • Rails:嗯......#(* $ ^ * @ ..确实......不...计算。

3 个解决方案

#1


2  

Just as tadman states in the comments of the question, I had to invent my own solution, since Rails doesn't technically allow tags in the sense that I needed them. Here's a generalized solution for those interested in doing something similar:

正如tadman在问题的评论中所述,我必须创建自己的解决方案,因为Rails在技术上不允许我需要它们的标签。对于那些有兴趣做类似事情的人来说,这是一个通用的解决方案:

I created a new table called SimilarPages:

我创建了一个名为SimilarPages的新表:

create_table :similar_pages, {:id => false} do |t|
  t.integer :page_id, :similar_page_id
  # you could also do `t.string :tag_name` or similar
end
add_index :similar_pages, :page_id
add_index :similar_pages, :similar_page_id

Technically, I could do a self-referential has_many relationship on Pages, but I decided not to since I don't ever need to reference it that way. I just created a simple SimilarPage model:

从技术上讲,我可以在Pages上做一个自我引用的has_many关系,但我决定不这样做,因为我不需要那样引用它。我刚刚创建了一个简单的SimilarPage模型:

class SimilarPage < ActiveRecord::Base
  belongs_to :page
  belongs_to :similar_page, :class_name => 'Page'
end

Then using ar-extensions (because I'm lazy, and also because I wanted to do this in one INSERT statement), I do this within the cache block:

然后使用ar-extensions(因为我很懒,也因为我想在一个INSERT语句中执行此操作),我在缓存块中执行此操作:

SimilarPage.delete_all("page_id = '#{@page_id}'")
SimilarPage.import [:page_id, :similar_page_id], @similar_pages.collect {|s| SimilarPage.new(:page_id=>@page_id,:similar_page_id=>s.id)}

In my expire_cache_for method of my Observer, I do this:

在我的观察者的expire_cache_for方法中,我这样做:

SimilarPage.where(:similar_page_id => expiring_page.id).all.each do |s|
  ActionController::Base.new.expire_fragment(/page_show__#{s.page_id}__.*/) 
  # the regexp is for different currencies being cached ^
  Rails.cache.delete("page_show_#{s.page_id}")
end

#2


0  

There is an excellent explanation of how Rails generates tags for content expiration in this video http://railslab.newrelic.com/2009/02/19/episode-8-memcached

在此视频中,Rails如何为内容过期生成标记有一个很好的解释http://railslab.newrelic.com/2009/02/19/episode-8-memcached

The short if it is if you pass your entire object into the cache it will generate a cache key that that uses the timestamp which will get updated when you change your object.

如果将整个对象传递到缓存中,它将生成一个缓存键,该缓存键使用时间戳,当您更改对象时,该时间戳将更新。

#3


0  

Cashier may be helpful. "Tag based caching"

收银员可能会有所帮助。 “基于标记的缓存”

# in your view
cache @some_record, :tag => 'some-component'

# later
Cashier.expire 'some-component'

https://github.com/twinturbo/cashier

https://github.com/twinturbo/cashier

#1


2  

Just as tadman states in the comments of the question, I had to invent my own solution, since Rails doesn't technically allow tags in the sense that I needed them. Here's a generalized solution for those interested in doing something similar:

正如tadman在问题的评论中所述,我必须创建自己的解决方案,因为Rails在技术上不允许我需要它们的标签。对于那些有兴趣做类似事情的人来说,这是一个通用的解决方案:

I created a new table called SimilarPages:

我创建了一个名为SimilarPages的新表:

create_table :similar_pages, {:id => false} do |t|
  t.integer :page_id, :similar_page_id
  # you could also do `t.string :tag_name` or similar
end
add_index :similar_pages, :page_id
add_index :similar_pages, :similar_page_id

Technically, I could do a self-referential has_many relationship on Pages, but I decided not to since I don't ever need to reference it that way. I just created a simple SimilarPage model:

从技术上讲,我可以在Pages上做一个自我引用的has_many关系,但我决定不这样做,因为我不需要那样引用它。我刚刚创建了一个简单的SimilarPage模型:

class SimilarPage < ActiveRecord::Base
  belongs_to :page
  belongs_to :similar_page, :class_name => 'Page'
end

Then using ar-extensions (because I'm lazy, and also because I wanted to do this in one INSERT statement), I do this within the cache block:

然后使用ar-extensions(因为我很懒,也因为我想在一个INSERT语句中执行此操作),我在缓存块中执行此操作:

SimilarPage.delete_all("page_id = '#{@page_id}'")
SimilarPage.import [:page_id, :similar_page_id], @similar_pages.collect {|s| SimilarPage.new(:page_id=>@page_id,:similar_page_id=>s.id)}

In my expire_cache_for method of my Observer, I do this:

在我的观察者的expire_cache_for方法中,我这样做:

SimilarPage.where(:similar_page_id => expiring_page.id).all.each do |s|
  ActionController::Base.new.expire_fragment(/page_show__#{s.page_id}__.*/) 
  # the regexp is for different currencies being cached ^
  Rails.cache.delete("page_show_#{s.page_id}")
end

#2


0  

There is an excellent explanation of how Rails generates tags for content expiration in this video http://railslab.newrelic.com/2009/02/19/episode-8-memcached

在此视频中,Rails如何为内容过期生成标记有一个很好的解释http://railslab.newrelic.com/2009/02/19/episode-8-memcached

The short if it is if you pass your entire object into the cache it will generate a cache key that that uses the timestamp which will get updated when you change your object.

如果将整个对象传递到缓存中,它将生成一个缓存键,该缓存键使用时间戳,当您更改对象时,该时间戳将更新。

#3


0  

Cashier may be helpful. "Tag based caching"

收银员可能会有所帮助。 “基于标记的缓存”

# in your view
cache @some_record, :tag => 'some-component'

# later
Cashier.expire 'some-component'

https://github.com/twinturbo/cashier

https://github.com/twinturbo/cashier