Navigation Menu

Skip to content

Commit

Permalink
rescue DeserializationError and mark job as failed
Browse files Browse the repository at this point in the history
  • Loading branch information
yetanothernguyen authored and betamatt committed May 28, 2011
1 parent 674c655 commit acefab8
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 18 deletions.
14 changes: 10 additions & 4 deletions lib/delayed/backend/base.rb
@@ -1,8 +1,5 @@
module Delayed
module Backend
class DeserializationError < StandardError
end

module Base
def self.included(base)
base.extend ClassMethods
Expand Down Expand Up @@ -79,6 +76,15 @@ def unlock
self.locked_at = nil
self.locked_by = nil
end

def hook(name, *args)
if payload_object.respond_to?(name)
method = payload_object.method(name)
method.arity == 0 ? method.call : method.call(self, *args)
end
rescue DeserializationError
# do nothing
end

def reschedule_at
payload_object.respond_to?(:reschedule_at) ?
Expand Down Expand Up @@ -107,7 +113,7 @@ def deserialize(source)

raise DeserializationError,
'Job failed to load: Unknown handler. Try to manually require the appropriate file.'
rescue TypeError, LoadError, NameError => e
rescue TypeError, LoadError, NameError, ArgumentError => e
raise DeserializationError,
"Job failed to load: #{e.message}. Try to manually require the required file."
end
Expand Down
4 changes: 4 additions & 0 deletions lib/delayed/deserialization_error.rb
@@ -0,0 +1,4 @@
module Delayed
class DeserializationError < StandardError
end
end
21 changes: 12 additions & 9 deletions lib/delayed/worker.rb
Expand Up @@ -124,8 +124,11 @@ def run(job)
end
say "#{job.name} completed after %.4f" % runtime
return true # did work
rescue Exception => e
handle_failed_job(job, e)
rescue DeserializationError => error
job.last_error = "{#{error.message}\n#{error.backtrace.join('\n')}"
failed(job)
rescue Exception => error
handle_failed_job(job, error)
return false # work failed
end

Expand All @@ -138,16 +141,16 @@ def reschedule(job, time = nil)
job.save!
else
say "PERMANENTLY removing #{job.name} because of #{job.attempts} consecutive failures.", Logger::INFO

if job.payload_object.respond_to? :on_permanent_failure
say "Running on_permanent_failure hook"
job.payload_object.on_permanent_failure
end

self.class.destroy_failed_jobs ? job.destroy : job.update_attributes(:failed_at => Delayed::Job.db_time_now)
failed(job)
end
end

def failed(job)
job.hook(:on_permanent_failure)

self.class.destroy_failed_jobs ? job.destroy : job.update_attributes(:failed_at => Delayed::Job.db_time_now)
end

def say(text, level = Logger::INFO)
text = "[Worker(#{name})] #{text}"
puts text unless @quiet
Expand Down
1 change: 1 addition & 0 deletions lib/delayed_job.rb
Expand Up @@ -4,6 +4,7 @@
require File.dirname(__FILE__) + '/delayed/performable_method'
require File.dirname(__FILE__) + '/delayed/backend/base'
require File.dirname(__FILE__) + '/delayed/worker'
require File.dirname(__FILE__) + '/delayed/deserialization_error'
require File.dirname(__FILE__) + '/delayed/railtie' if defined?(::Rails::Railtie)

Object.send(:include, Delayed::MessageSending)
Expand Down
34 changes: 29 additions & 5 deletions spec/backend/shared_backend_spec.rb
Expand Up @@ -52,31 +52,36 @@ def create_job(opts = {})

it "should raise an DeserializationError when the job class is totally unknown" do
job = @backend.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"
lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
end

it "should raise an DeserializationError when the job is badly encoded" do
job = @backend.new :handler => "--- !ruby/object:SimpleJob {"
lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
end

it "should try to load the class when it is unknown at the time of the deserialization" do
job = @backend.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"
job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
end

it "should try include the namespace when loading unknown objects" do
job = @backend.new :handler => "--- !ruby/object:Delayed::JobThatDoesNotExist {}"
job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
end

it "should also try to load structs when they are unknown (raises TypeError)" do
job = @backend.new :handler => "--- !ruby/struct:JobThatDoesNotExist {}"
job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
end

it "should try include the namespace when loading unknown structs" do
job = @backend.new :handler => "--- !ruby/struct:Delayed::JobThatDoesNotExist {}"
job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
end

describe "find_available" do
Expand Down Expand Up @@ -314,4 +319,23 @@ def create_job(opts = {})
@job.max_attempts.should == 99
end
end

describe "worker integration" do
before do
@worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true)
end

describe "running a job" do

context "when the job raises a deserialization error" do
it "should mark the job as failed" do
Delayed::Worker.destroy_failed_jobs = false
job = described_class.create! :handler => "--- !ruby/object:JobThatDoesNotExist {}"
@worker.work_off
job.reload
job.failed_at.should_not be_nil
end
end
end
end
end

0 comments on commit acefab8

Please sign in to comment.