ruby-on-railscachingmemcacheddalli

Rails Cache Key generated as ActiveRecord::Relation


I am attempting to generate a fragment cache (using a Dalli/Memcached store) however the key is being generated with "#" as part of the key, so Rails doesn't seem to be recognizing that there is a cache value and is hitting the database.

My cache key in the view looks like this:

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

The controller has:

@jobs = @current_tenant.active_jobs

With the actual Active Record query like this:

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

Looking at the rails server, I see the cache read, but the SQL Query still runs:

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

Any ideas as to what I might be doing wrong? I'm sure it has to do w/ the key generation and ActiveRecord::Relation, but i'm not sure how.


Solution

  • I have had similar problems, I have not been able to successfully pass relations to the cache function and your @jobs variable is a relation.

    I coded up a solution for cache keys that deals with this issue along with some others that I was having. It basically involves generating a cache key by iterating through the relation.

    A full write up is on my site here.

    http://mark.stratmann.me/content_items/rails-caching-strategy-using-key-based-approach

    In summary I added a get_cache_keys function to 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)
    

    I can now create cache fragments by doing

    cache(@model.get_cache_key(['textlabel'])) do