I have a bug only in production. I can't reproduce the problem in development env. I have a main class that initializes several class for process data in a background job, but this is not going well in delayed_job, here are my two classes simplified.
Handler class
class Handler
def initialize(setting)
@setting = setting
end
def process
0.upto(11) do |index|
# data = some code in local method...
launch_process(data)
end
end
protected
def launch_process(data)
Process.new(
@setting,
data: data
).boot
end
end
Process class with called by Handler class
class Process
def initialize(setting, options = {})
@setting = setting
options.each { |k,v| instance_variable_set("@#{k}", v) }
end
def boot
self.delay.launch_retrieve
end
end
But when the process class is executed, I have a error by delayed_job : Delayed::DeserializationError: Job failed to load: undefined method has_key? for nil:NilClass
. I don't understand why initialize of class returned this error.
Any idea ? Thanks
Sometimes when we upgrade libs delayed jobs still keep old references.
Try to find the id of delayed_job in logs and play to parse its handler to ruby to find the wrong reference
j = DelayedJob.find(XXX)
data = YAML.load_dj(j.handler)
data.to_ruby
I made a pull request to help with this problem.
Meanwhile you can use this lines
# config/initializers/delayed_job.rb
# Monkey patch to use old class references
module Psych
class << self; attr_accessor :old_class_references end
@old_class_references = {}
class ClassLoader
private
def find klassname
klassname = ::Psych.old_class_references[klassname] || klassname
@cache[klassname] ||= resolve(klassname)
end
end
module Visitors
class ToRuby < Psych::Visitors::Visitor
def revive klass, node
if klass.is_a? String
klassname = ::Psych.old_class_references[klass] || klass
klass = Kernel.const_get(klassname) rescue klassname
end
s = register(node, klass.allocate)
init_with(s, revive_hash({}, node), node)
end
end
end
end
# Add all old dependencies (hash keys) pointing to new references (hash values)
Psych.old_class_references = {
'ActiveRecord::AttributeSet' => 'ActiveModel::AttributeSet'
# ...
}