问题
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:
回答1:
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.
回答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
来源:https://stackoverflow.com/questions/21071299/dalli-you-are-trying-to-cache-a-ruby-object-which-cannot-be-serialized-to-memca