作为ActiveRecord生成的Rails缓存键::关系
我正在尝试生成一个片段缓存(使用Dalli/Memcached存储),但是使用“#”作为键的一部分生成密钥,所以Rails似乎没有要认识到有一个缓存值并正在击中数据库。作为ActiveRecord生成的Rails缓存键::关系
在查看我的缓存键看起来是这样的:
cache([@jobs, "index"]) do
该控制器具有:
@jobs = @current_tenant.active_jobs
通过这样的实际活动记录查询:
def active_jobs
self.jobs.where("published = ? and expiration_date >= ?", true, Date.today).order("(featured and created_at > now() - interval '" + self.pinned_time_limit.to_s + " days') desc nulls last, created_at desc")
end
看着rails服务器,我看到缓存读取,但SQL查询仍在运行:
Cache read: views/#<ActiveRecord::Relation:0x007fbabef9cd58>/1-index
Read fragment views/#<ActiveRecord::Relation:0x007fbabef9cd58>/1-index (1.0ms)
(0.6ms) SELECT COUNT(*) FROM "jobs" WHERE "jobs"."tenant_id" = 1 AND (published = 't' and expiration_date >= '2013-03-03')
Job Load (1.2ms) SELECT "jobs".* FROM "jobs" WHERE "jobs"."tenant_id" = 1 AND (published = 't' and expiration_date >= '2013-03-03') ORDER BY (featured and created_at > now() - interval '7 days') desc nulls last, created_at desc
有关我可能会做什么错的任何想法?我相信它必须做的关键生成和ActiveRecord ::关系,但我不知道如何。
我有类似的问题,我还没有能够成功地将关系传递给缓存函数,您的@jobs变量是一个关系。
我编写了一个缓存键的解决方案,处理这个问题以及我正在处理的其他一些问题。它基本上涉及通过迭代关系来生成缓存键。
一个完整的写在我的网站在这里。
http://mark.stratmann.me/content_items/rails-caching-strategy-using-key-based-approach
总之我添加了一个get_cache_keys功能的ActiveRecord :: Base的
module CacheKeys
extend ActiveSupport::Concern
# Instance Methods
def get_cache_key(prefix=nil)
cache_key = []
cache_key << prefix if prefix
cache_key << self
self.class.get_cache_key_children.each do |child|
if child.macro == :has_many
self.send(child.name).all.each do |child_record|
cache_key << child_record.get_cache_key
end
end
if child.macro == :belongs_to
cache_key << self.send(child.name).get_cache_key
end
end
return cache_key.flatten
end
# Class Methods
module ClassMethods
def cache_key_children(*args)
@v_cache_key_children = []
# validate the children
args.each do |child|
#is it an association
association = reflect_on_association(child)
if association == nil
raise "#{child} is not an association!"
end
@v_cache_key_children << association
end
end
def get_cache_key_children
return @v_cache_key_children ||= []
end
end
end
# include the extension
ActiveRecord::Base.send(:include, CacheKeys)
我现在可以做
cache(@model.get_cache_key(['textlabel'])) do
创建缓存片段虽然我标志着@标记施特拉特曼我的答复是正确的,我实际上是通过简化实现来解决这个问题的。我加触摸:忠于我的模型关系声明:
belongs_to :tenant, touch: true
,然后设置缓存键根据承租人(有需要的查询参数以及):
<% cache([@current_tenant, params[:query], "#{@current_tenant.id}-index"]) do %>
这样,如果新作业已添加,它也触及Tenant缓存。不知道这是不是最好的路线,但它的工作原理似乎很简单。
背景:
问题是关系的字符串表示是不同的每个代码运行时间:
|This changes|
views/#<ActiveRecord::Relation:0x007fbabef9cd58>/...
所以你每次不同的缓存键。
此外,它是而不是可能完全摆脱数据库查询。(您own answer是最好的,可以做)
解决方案:
生成有效的关键,而不是此
cache([@jobs, "index"])
做到这一点:
cache([@jobs.to_a, "index"])
此查询数据库并构建一个模型数组,从中检索cache_key
。
PS:我可以发誓使用关系Rails中的早期版本中的工作......
我不太确定如果模型中的某个字段是十进制的,那么这将起作用 – bcackerman 2014-02-27 16:12:54
@bcackerman:这没什么区别。默认情况下,Rails使用'updated_at'和'id' * only *来生成缓存键。 – 2015-06-15 10:48:21
我们一直在做你正在生产提什么了大约一年。我是解压到一个宝石在几个月前:
https://github.com/cmer/scope_cache_key
基本上,它可以让你用一个范围作为缓存键的一部分。这样做有显着的性能优势,因为现在可以将包含多个记录的页面缓存在单个缓存元素中,而不是遍历该作用域中的每个元素并分别检索缓存。我觉得把它与标准的“俄罗斯娃娃缓存”原则结合起来是最佳的。
林:
class ActiveRecord::Base
def self.cache_key
pluck("concat_ws('/', '#{table_name}', group_concat(#{table_name}.id), date_format(max(#{table_name}.updated_at), '%Y%m%d%H%i%s'))").first
end
def self.updated_at
maximum(:updated_at)
end
end
我在ActiveRecord :: Relation上使用一个简单的补丁来为关系生成缓存键。
require "digest/md5"
module RelationCacheKey
def cache_key
Digest::MD5.hexdigest to_sql.downcase
end
end
ActiveRecord::Relation.send :include, RelationCacheKey
由于SQL本身被用作缓存键,所以如果更新任何模型,这将永不过期。 – fivedigit 2014-07-13 20:05:39
我已经做了类似Hopsoft,但它使用的Rails Guide方法作为模板。我使用MD5摘要来区分关系(因此User.active.cache_key
可以与User.deactivated.cache_key
区分开来),并使用count和max updated_at
在关系更新时自动使缓存过期。
require "digest/md5"
module RelationCacheKey
def cache_key
model_identifier = name.underscore.pluralize
relation_identifier = Digest::MD5.hexdigest(to_sql.downcase)
max_updated_at = maximum(:updated_at).try(:utc).try(:to_s, :number)
"#{model_identifier}/#{relation_identifier}-#{count}-#{max_updated_at}"
end
end
ActiveRecord::Relation.send :include, RelationCacheKey
以此为起点,你可以尝试这样的事:
def self.cache_key
["#{model_name.cache_key}-all",
"#{count}-#{updated_at.utc.to_s(cache_timestamp_format) rescue 'empty'}"
] * '/'
end
def self.updated_at
maximum :updated_at
end
我在这里多模型涉及到同其他型号的规范化数据库,想客户,位置等的所有通过street_id获得地址。
使用此解决方案,您可以基于范围生成cache_keys,例如,
cache [@client, @client.locations] do
# ...
end
cache [@client, @client.locations.active, 'active'] do
# ...
end
,我可以简单地从上述修改self.updated
也包括相关的对象(因为has_many
不支持“触摸”,所以如果我更新了街道,也不会被高速缓存,否则看到的):
belongs_to :street
def cache_key
[street.cache_key, super] * '/'
end
# ...
def self.updated_at
[maximum(:updated_at),
joins(:street).maximum('streets.updated_at')
].max
end
只要你没有“取消删除”记录并在belongs_to中使用触摸,假设由count和max updated_at组成的缓存键就足够了,那么应该没问题。
该链接返回一个404 ... – 2014-02-16 13:29:33