解决 Rails 片段缓存中 memcached 缺乏通配符过期的问题
我正在致力于将片段缓存添加到具有登录用户和匿名用户的 Rails 3 站点,并且需要根据整个站点显示的内容更新时间来控制部分页面的过期时间。在此过程中,我发现 memcached 不支持 expire_fragment 的正则表达式,所以现在我正在寻找解决此问题的方法。
对于匿名用户来说,这并不是一个大问题。我创建了诸如“frontpage-new-shows”或“schedule-weekly-forecast-10/24/10”之类的缓存键,并在 Show 模型中创建新条目或下周播出的节目时使它们过期通过清理器修改。效果很好。
我的问题是登录的用户。他们每个人都有自己的自定义时间表,因此我的密钥看起来像“schedule-user-jschuur-10/10/24”,其中 jschuur 是帐户的用户名。我知道,使用基于日期的约定,它们会自然过期,但我也想在当天(或一周)的日程表中的某些内容发生显示相关更改时,显式地使缓存日程表的所有片段过期。
事实证明,memcached 无法执行我需要的通配符过期类型(schedule-user-.*-10/10/24)。这意味着我需要某种解决方案来存储在 memcached 中的中央查找密钥中发出的所有密钥,或者让 Rails/Ruby 以某种方式根据我发送到 memcached 的密钥在内部执行此操作。
我认为前者是更好的解决方案。我的问题是,如何以最有效的方式做到这一点,这样我就不会浪费所有节省下来的时间而不再访问数据库。我可以在 memcached 中存储一个数组或散列,检索整个内容,循环遍历 if 匹配,删除它们并将散列存储回来。听起来它可能对几百甚至一千个用户来说效果很好,但这真的是最好的方法吗?
有人已经解决了这个问题并发布了解决方案吗?
需要考虑的一件事是,目前我的几乎所有缓存都是通过视图中的语句完成的,面向尚未触发的 ActiveRelations 准备好的查询,例如:
<% if current_user %>
<% if current_user.saved_shows_count %>
<% cache "schedule-user-#{current_user.username}-#{(Time.now + 3.hours).to_date.end_of_week.strftime('%D')}" do %>
<% if @shows.any? %>
<%= render :partial => "schedule/schedule", :locals => { :shows => @shows } %>
<% end %>
<% end %>
...
该网站托管在 Heroku 上,我正在使用 dalli gem作为客户。
I'm working on adding fragment caching to a Rails 3 site that has both logged in and anonymous users, and need to control when parts of the page expire based on when content displayed throughout the site is updated. Midway into this, I discovered that memcached doesn't support regexps for expire_fragment, so now I'm looking for ways around this.
For anonymous users, this is not a huge problem. I create cache keys like 'frontpage-new-shows' or 'schedule-weekly-forecast-10/24/10' and expire them when a new entry in the Show model is made, or when a show that airs within the next week is modified via sweepers. That works just fine.
My problem is with users who are logged in. They each have their own customized schedule, so my keys look like 'schedule-user-jschuur-10/10/24' with jschuur being the username to the account. I understand that with the date based convention, they expire naturally, but I also want to explicitly expire all fragments for cached schedules during the day when show related changes occur for something in your schedule for that day(or week).
As it turns out, memcached can't do the kind of wildcard expiration that I need here (schedule-user-.*-10/10/24). This means I need some kind of solution that stores all the keys issued in a central lookup key in memcached, or have Rails/Ruby somehow do this internally based on the keys I've sent to memcached for me.
I'm assuming the former is the better solution. My question is, how do I do this in the most efficient way, so I don't blow all the time saved not hitting the DB any more. I could just store an array or hash in memcached, retrieve the whole thing, loop over if for matches, remove them and store the hash back. That sounds like it might work great for a few hundred or even a thousand user, but is this really the best way to go?
Has someone already tackled this problem and released a solution?
One thing to consider is that almost all of my caching is currently done with statements in the view, geared towards ActiveRelations prepared queries that haven't fired yet, like this one:
<% if current_user %>
<% if current_user.saved_shows_count %>
<% cache "schedule-user-#{current_user.username}-#{(Time.now + 3.hours).to_date.end_of_week.strftime('%D')}" do %>
<% if @shows.any? %>
<%= render :partial => "schedule/schedule", :locals => { :shows => @shows } %>
<% end %>
<% end %>
...
The site is hosted on Heroku and I'm using the dalli gem as a client.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
编辑:用 dalli gem 编写的代码存在一些问题,对我来说,执行读取会引发“编组数据太短”或“内存缓存中的无效编组数据”。不确定它是否也适合你。
虽然有点 hackish,但您可以使用命名空间,然后增加命名空间,从而使使用它创建的所有密钥无效。为此,首先使用
Rails.cache.write("frontpage-new-shows-namespace", "1", {:raw => true}) 在初始化程序(或任何位置)中设置命名空间.设置每个缓存时,将
Rails.cache.read("frontpage-new-shows-namespace")
添加到哈希键中您想要的任何位置(位置并不重要)。使命名空间中创建的所有缓存键过期就像Rails.cache.increment("frontpage-new-shows-namespace", 1)
一样简单。您可以将 Rails.cache.read("frontpage-new-shows-namespace") 添加为全局变量,从而消除用于创建缓存键的 memcache 搜索,然后随时更新此变量缓存命名空间增量。祝你好运,我希望这会有所帮助。
EDIT: There are some issues with this as written with the dalli gem, doing a read raises "marshal data too short" or "invalid marshalled data in memcache" for me. Not sure if it will for you as well.
Although a little hackish, you could use a namespace and then increase the namespace, thus invalidating all keys created using it. To do this, first set the namespace in an initializer (or wherever) with
Rails.cache.write("frontpage-new-shows-namespace", "1", {:raw => true})
. When you set each cache, addRails.cache.read("frontpage-new-shows-namespace")
into the hash key wherever you would like (it doesn't really matter where). Expiring all cache keys created in a namespace is as easy asRails.cache.increment("frontpage-new-shows-namespace", 1)
. You might be able to addRails.cache.read("frontpage-new-shows-namespace")
as a global variable, eliminating memcache searches for creating cache keys, and then update this variable whenever you do a cache namespace increment.Good luck, and I hope this helps.