2013-03-04 62 views
4

我正在嘗試生成一個片段緩存(使用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 ::關係,但我不知道如何。

回答

2

我有類似的問題,我還沒有能夠成功地將關係傳遞給緩存函數,您的@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 
+3

該鏈接返回一個404 ... – 2014-02-16 13:29:33

1

創建緩存片段雖然我標誌着@標記施特拉特曼我的答覆是正確的,我實際上是通過簡化實現來解決這個問題的。我加觸摸:忠於我的模型關係聲明:

belongs_to :tenant, touch: true 

,然後設置緩存鍵根據承租人(有需要的查詢參數以及):

<% cache([@current_tenant, params[:query], "#{@current_tenant.id}-index"]) do %> 

這樣,如果新作業已添加,它也觸及Tenant緩存。不知道這是不是最好的路線,但它的工作原理似乎很簡單。

8

背景:

問題是關係的字符串表示是不同的每個代碼運行時間:

        |This changes| 
views/#<ActiveRecord::Relation:0x007fbabef9cd58>/... 

所以你每次不同的緩存鍵。

此外,它是而不是可能完全擺脫數據庫查詢。(您own answer是最好的,可以做)

解決方案:

生成有效的關鍵,而不是此

cache([@jobs, "index"]) 

做到這一點:

cache([@jobs.to_a, "index"]) 

此查詢數據庫並構建一個模型數組,從中檢索cache_key

PS:我可以發誓使用關係Rails中的早期版本中的工作......

+0

我不太確定如果模型中的某個字段是十進制的,那麼這將起作用 – bcackerman 2014-02-27 16:12:54

+0

@bcackerman:這沒什麼區別。默認情況下,Rails使用'updated_at'和'id' * only *來生成緩存鍵。 – 2015-06-15 10:48:21

3

我們一直在做你正在生產提什麼了大約一年。我是解壓到一個寶石在幾個月前:

https://github.com/cmer/scope_cache_key

基本上,它可以讓你用一個範圍作爲緩存鍵的一部分。這樣做有顯着的性能優勢,因爲現在可以將包含多個記錄的頁面緩存在單個緩存元素中,而不是遍歷該作用域中的每個元素並分別檢索緩存。我覺得把它與標準的「俄羅斯娃娃緩存」原則結合起來是最佳的。

1

林:

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 
-1

我在ActiveRecord :: Relation上使用一個簡單的補丁來爲關係生成緩存鍵。

require "digest/md5" 

module RelationCacheKey 
    def cache_key 
    Digest::MD5.hexdigest to_sql.downcase 
    end 
end 

ActiveRecord::Relation.send :include, RelationCacheKey 
+0

由於SQL本身被用作緩存鍵,所以如果更新任何模型,這將永不過期。 – fivedigit 2014-07-13 20:05:39

1

我已經做了類似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 
0

以此爲起點,你可以嘗試這樣的事:

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組成的緩存鍵就足夠了,那麼應該沒問題。