如何使用Rails.cache.fetch&存储nil;内存缓存?

时间:2015-05-21 20:25:37

标签: ruby-on-rails ruby-on-rails-3 caching null memcached

我有一个现有的Model singleton方法触发了我想要为缓存建模的昂贵的数据库查询。为此,我围绕相关方法进行了Rails.cache.fetch()调用:

# app/models/specialist.rb
class Specialist < ActiveRecord::Base

  def city
    Rails.cache.fetch([self.class.name, self.id, "city"], expires_in: 23.hours) do

      # expensive legacy query:
      if responded?
        o = offices.first
        return nil if o.blank?
        return o.city
      elsif hospital_or_clinic_only?
        (hospitals.map{ |h| h.city } + clinics.map{ |c| c.cities }).flatten.reject{ |i| i == nil }.uniq.first
      elsif hospital_or_clinic_referrals_only?
        (offices.map{ |o| o.city } + hospitals.map{ |h| h.city } + clinics.map{ |c| c.cities }).flatten.reject{ |c| c.blank? }.uniq.first
      else
        nil
      end

    end
  end

end

对过去需要16秒的所有记录执行.city;使用此Rails.cache.fetch块时,它只会下降到7秒,因为有一半的记录仍在触发数据库调用。

当我调查时,我发现当city方法返回nil时,Rails.cache不会将结果写入memcache - 这意味着我的一半专家记录仍会触发昂贵的数据库查找,尽管它是“缓存的” “

如何在使用memcache时强制Rails.cache.fetch存储nil的值,以便不会触发另一个数据库查找以再次找到nil?

1 个答案:

答案 0 :(得分:1)

一种解决方案是使用NullObject,如下所示:

class Specialist
  NullData = Struct.new(nil)

  def city
    result = Rails.cache.fetch([self.class.name, self.id, "city"], expires_in: 23.hours) do
      if responded?
        ..
      else
        NullData.new()
      end
     result.is_a?(NullData) ? nil : result 
   end
end

这样就可以创建一个可以缓存的空对象。然后当你返回时检查它是否是一个已存储的空对象,在这种情况下你返回nil(为了确保你不会破坏依赖于你的方法返回nil的旧代码),否则你返回缓存的内容。