Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution to fix DeserializationErrors with old class references #1089

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .rake_tasks~
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
build
install
install:local
release[remote]
rubocop
rubocop:auto_correct
spec
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,22 @@ Cleaning up
===========
You can invoke `rake jobs:clear` to delete all jobs in the queue.

Upgrade old references
===========================
Maybe you get errors in your jobs like this.
```
ERROR -- : 2019-03-15T10:11:04+0000: [Worker(delayed_job host:xxx pid:776)] Job Delayed::PerformableMethod (id=1) FAILED permanently with Delayed::DeserializationError: Job failed to load: undefined class/module ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlDateTime
```

You have to search all conflictive references, find theirs new names and set to fix them.

```ruby
# config/initializers/delayed_job_config.rb
Psych.old_class_references = {
'ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlDateTime' => 'ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString'
}
```

Having problems?
================
Good places to get help are:
Expand Down
24 changes: 24 additions & 0 deletions lib/delayed/psych_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,23 @@ def encode_with(coder)
end

module Psych
# allow set references to serialize old dependencies by news
class << self; attr_accessor :old_class_references end
@old_class_references = {}

def self.load_dj(yaml)
result = parse(yaml)
result ? Delayed::PsychExt::ToRuby.create.accept(result) : result
end

class ClassLoader
private

def find(klassname)
klassname = ::Psych.old_class_references[klassname] || klassname
@cache[klassname] ||= resolve(klassname)
end
end
end

module Delayed
Expand Down Expand Up @@ -97,6 +110,17 @@ def resolve_class(klass_name)
rescue
super
end

private

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
20 changes: 20 additions & 0 deletions spec/psych_ext_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,24 @@
expect(deserialized.instance_variable_get(:@check)).to eq(12)
end
end

context 'with a old reference' do
class NewKlass; end

let(:klass) { NewKlass.name }
let(:invalid_reference) { 'OldKlass' }
let(:invalid_yaml) { "--- !ruby/class '#{invalid_reference}'\n" }

it 'raise an error when is not refered' do
expect { YAML.load(invalid_yaml) }.to raise_error ArgumentError
end

it 'deserializes correctly when is refered' do
expect do
Psych.old_class_references = {invalid_reference => klass}
expect(YAML.load(invalid_yaml)).to be Kernel.const_get(klass)
Psych.old_class_references = {}
end.not_to raise_error
end
end
end