Search code examples
ruby-on-railsmemcacheddalli

Dalli: You are trying to cache a Ruby object which cannot be serialized to memcached


I'm getting weird behavior when I try to save an object in memory to the database and then cache that object with Dalli.

class Infraction << ActiveRecord::Base
  has_many :infraction_locations
  has_many :tracked_points, through: :infraction_locations
end

class TrackedPoint << ActiveRecord::Base
  has_many :infraction_locations
  has_many :infractions, through: :infraction_locations
end

class  InfractionLocation << ActiveRecord::Base
  belongs_to :infraction
  belongs_to :tracked_point
  belongs_to :rule
end

This works:

i = Infraction.create
i.tracked_points << TrackedPoint.create(location_id: 1)
i.save
Rails.cache.write "my_key", i

This also works:

i = Infraction.new
i.tracked_points << TrackedPoint.create(location_id: 1)
i.save
Rails.cache.write "my_key", i

Notice that the objects (in the second case just the TrackedPoint), is saved to the database implicitly by the call to create.

I've also found that reloading i allows me to write the object to the cache. So this works:

i = Infraction.new
i.tracked_points << TrackedPoint.new(location_id: 1)
i.save
i.reload
Rails.cache.write "my_key", i

This fails:

i = Infraction.new
i.tracked_points << TrackedPoint.new(location_id: 1)
i.save
Rails.cache.write "my_key", i

However, if I do some weird duping, I can get the failing example to work:

i = Infraction.new
i.tracked_points << TrackedPoint.new(location_id: 1)
i.save
copy = i.dup
copy.tracked_points = i.tracked_points.to_a
Rails.cache.write "my_key", copy

In my failing example, I can cache the infraction (i) before I save it to the database, like this:

i = Infraction.new
i.tracked_points << TrackedPoint.new(location_id: 1)
Rails.cache.write "what", i

Per Dave's idea, I tried build instead of << for the TrackedPoint as well as adding a accepts_nested_attributes_for :tracked_points to Infraction, but neither of those worked.

I am getting the marshalling/serializer error in the log:

You are trying to cache a Ruby object which cannot be serialized to memcached.

I am running Rails 3.2.13 and Dalli 2.7.0

EDIT

See also: Cacheing an ActiveRecord Object that has_many through:


Solution

  • Turns out it was a problem with squeel.

    There is something called an AliasTracker that is not being Marshalled correctly. A monkey patch that appears to fix this issue is:

    module ActiveRecord
      module Associations
        class AliasTracker
          def marshal_dump(*)
            nil
          end
    
          def marshal_load(*)
            nil
          end
        end
      end
    end
    

    More discussion and answer from here: https://github.com/activerecord-hackery/squeel/issues/232