我有一个现有的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?
答案 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的旧代码),否则你返回缓存的内容。