4

(Dalli / Memcachedストアを使用して)フラグメントキャッシュを生成しようとしていますが、キーの一部として「#」を使用してキーが生成されているため、Railsはキャッシュ値があることを認識していないようで、ヒットしていますデータベース。

ビューのキャッシュキーは次のようになります。

cache([@jobs, "index"]) do

コントローラには次のものがあります。

@jobs = @current_tenant.active_jobs

次のような実際のActiveRecordクエリを使用します。

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::Relationshipを使用する必要があると確信していますが、その方法はわかりません。

4

9 に答える 9

8

バックグラウンド:

問題は、コードが実行されるたびにリレーションの文字列表現が異なることです。

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

したがって、毎回異なるキャッシュキーを取得します。

その上、データベースクエリを完全に取り除くことはできません。(あなた自身の答えが最善です)

解決:

これの代わりに、有効なキーを生成するには

cache([@jobs, "index"])

これを行う:

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

これにより、データベースが照会され、モデルの配列が作成され、そこからcache_keyが取得されます。

PS:以前のバージョンのRailsで機能していたリレーションを使用して誓うことができました...

于 2013-04-18T12:24:41.130 に答える
3

私たちはあなたが生産で言及していることを正確に約1年間行ってきました。私はそれを数ヶ月前に宝石に抽出しました:

https://github.com/cmer/scope_cache_key

基本的に、スコープをキャッシュキーの一部として使用できます。スコープ内の各要素をループしてキャッシュを個別に取得するのではなく、単一のキャッシュ要素に複数のレコードを含むページをキャッシュできるようになったため、これを行うことでパフォーマンスが大幅に向上します。これを標準の「ロシア人形キャッシング」の原則と組み合わせるのが最適だと思います。

于 2013-06-22T11:08:01.087 に答える
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
于 2013-03-04T16:54:59.383 に答える
2

私はHopsoftのようなことをしましたが、それはRailsガイドのメソッドをテンプレートとして使用します。MD5ダイジェストを使用してリレーションを区別し(したがって、と区別User.active.cache_keyできますUser.deactivated.cache_key)、カウントと最大を使用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
于 2015-06-13T20:49:38.620 に答える
1

@ mark-stratmannの応答を正しいものとしてマークしましたが、実際には実装を単純化することでこれを解決しました。私はタッチを追加しました:私のモデル関係宣言に当てはまります:

belongs_to :tenant, touch: true

次に、テナントに基づいてキャッシュキーを設定します(必要なクエリパラメータも使用します)。

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

そうすれば、新しいジョブが追加された場合、それはテナントキャッシュにも影響を与えます。これが最適なルートかどうかはわかりませんが、機能し、非常に単純なようです。

于 2013-03-05T19:36:05.617 に答える
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
于 2013-09-19T17:33:27.240 に答える
0

多分これは https://github.com/casiodk/class_cacherを助けることができます、それはモデル自体からcache_keyを生成します、しかし多分あなたはコードベースのいくつかの原則を使うことができます

于 2013-07-10T01:16:06.697 に答える
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でtouchを使用しない限り、countとmaxupdated_atで構成されるキャッシュキーで十分であると想定して問題ありません。

于 2015-07-08T16:25:49.693 に答える
-1

ActiveRecord :: Relationshipで簡単なパッチを使用して、リレーションのキャッシュキーを生成しています。

require "digest/md5"

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

ActiveRecord::Relation.send :include, RelationCacheKey
于 2013-12-10T22:18:25.917 に答える