Question

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:

Was it helpful?

Solution 2

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

OTHER TIPS

My best guess, just by looking at the code differences.

In the first two examples you're creating the associated object with TrackedPoint.create which immediately persists it in the database. So assigning the association via "<<" works as there is an id for that object.

In the third you use TrackedPoint.new and then assign the object. This would leverage a nested creation. So you need "accepts_nested_attributes_for" in the model. The proper way IIRC is to use "build" to properly instantiate the association of a new object. My guess is you're seeing some weird case when you dup it where rails is creating the TrackedPoint object so its no longer a nested attributes case, its just direct assignment of an existing object to the association.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top