强制ActiveRelation使用rails缓存

时间:2013-09-23 07:16:01

标签: ruby-on-rails caching activerecord ruby-on-rails-3.2 dalli

我有一张languages表,几乎没有任何变化。我试图避免在这个表上进行初始缓存的数据库查询。

class Language < ActiveRecord::Base
  attr_accessible :code, :name, :native_name

  def self.find_by_name_in_cache(name)
    get_all_cached.find {|l| l.name == name}
  end

  def self.find_by_code_in_cache(code)
    get_all_cached.find {|l| l.code == code}
  end

  def self.find_by_id_in_cache(id)
    get_all_cached.find {|l| l.id == id}
  end

  def self.get_all_cached
    Rails.cache.fetch('all_languages') {Language.all}
  end
end

只要我使用我定义的find_in_cache方法之一,一切顺利。

我的问题是,如何强制ActiveRelation也使用缓存。

例如,请考虑以下用户模型:

class User < ActiveRecord::Base
  belongs_to :native_language, :class_name => :Language, :foreign_key => :native_language_id
end

当我访问@user.native_language时,它会从数据库中查询language。我非常感谢任何阻止这种情况的想法。

我知道我可以做到以下几点:

class User < ActiveRecord::Base
  belongs_to :native_language, :class_name => :Language, :foreign_key => :native_language_id

  def native_language_cached
    Language.find_by_id_in_cache(self.native_language_id)
  end
end

然而,我希望有一个更透明的解决方案,因为我的很多表引用了languages表,并且将cached方法添加到所有这些模型中会非常混乱。

1 个答案:

答案 0 :(得分:1)

要在单个请求期间缓存查询,除了确保使用ActiveRecord::QueryCache中间件之外,您不必执行任何操作。如果你打两次电话:

2.times{ user.native_language.to_s }

您会在日志中看到类似的内容:

Language Load (0.2ms)   SELECT `languages`.* FROM `languages` WHERE ...
CACHE (0.0ms)  SELECT `languages`.* FROM `languages` WHERE ...

跨请求缓存需要手动缓存。 identity_cache gem可能对您尝试执行的操作很有用(缓存关联)。

最快的方法可能只是将expires_in选项添加到您拥有的代码中。您可以编写如下所示的通用cached_find方法:

def self.cached_find(id)
  key = model_name.cache_key + '/' + id
  Rails.cache.fetch(key) do
    find(id).tap do |model|
      Rails.cache.write(key, model, expires_in: cache_period)
    end
  end
end

def self.cache_period
  3.days
end

您可以将模块mixin设置为根据需要使用尽可能多的模型。它确实意味着您必须编写自己的关联查找。您也可以使用回调来执行此操作:

after_commit do
  Rails.cache.write(cache_key, self, expires_in: self.class.cache_period)
end