Skip to content

Commit

Permalink
Merge pull request #71 from bensheldon/on_thread_error
Browse files Browse the repository at this point in the history
Add a callable hook on thread errors
  • Loading branch information
bensheldon authored Aug 8, 2020
2 parents 189daff + e028a42 commit 501cc2d
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ By default, if a job raises an error while it is being performed, _and it bubble
GoodJob.reperform_jobs_on_standard_error = true # => default
```
To report errors that _do_ bubble up to the GoodJob backend, assign a callable to `GoodJob.on_thread_error`. For example:
```ruby
# config/initializers/good_job.rb
# With Sentry (or Bugsnag, Airbrake, Honeybadger, etc)
GoodJob.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
```
### Retrying jobs
ActiveJob can be configured to retry an infinite number of times, with an exponential backoff. Using ActiveJob's `retry_on` will ensure that errors do not bubble up to the GoodJob backend:
Expand Down
1 change: 1 addition & 0 deletions lib/good_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module GoodJob
cattr_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
mattr_accessor :preserve_job_records, default: false
mattr_accessor :reperform_jobs_on_standard_error, default: true
mattr_accessor :on_thread_error, default: nil

ActiveSupport.run_load_hooks(:good_job, self)
end
2 changes: 2 additions & 0 deletions lib/good_job/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ def create_thread
end

def timer_observer(time, executed_task, thread_error)
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
ActiveSupport::Notifications.instrument("finished_timer_task.good_job", { result: executed_task, error: thread_error, time: time })
end

def task_observer(time, output, thread_error)
GoodJob.on_thread_error.call(thread_error) if thread_error && GoodJob.on_thread_error.respond_to?(:call)
ActiveSupport::Notifications.instrument("finished_job_task.good_job", { result: output, error: thread_error, time: time })
create_thread if output
end
Expand Down
42 changes: 42 additions & 0 deletions spec/lib/good_job/scheduler_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,48 @@
expect { example.run }.to output.to_stdout # rubocop:disable RSpec/ExpectInHook
end

context 'when thread error' do
let(:error_proc) { double("Error Collector", call: nil) } # rubocop:disable RSpec/VerifiedDoubles

before do
allow(GoodJob).to receive(:on_thread_error).and_return(error_proc)
stub_const 'THREAD_HAS_RUN', Concurrent::AtomicBoolean.new(false)
end

context 'when on timer thread' do
it 'calls GoodJob.on_thread_error' do
allow_any_instance_of(described_class).to receive(:create_thread) do
THREAD_HAS_RUN.make_true
raise "Whoops"
end

scheduler = described_class.new(performer)

sleep_until { THREAD_HAS_RUN.true? }

expect(error_proc).to have_received(:call).with(an_instance_of(RuntimeError).and(having_attributes(message: 'Whoops')))

scheduler.shutdown
end
end

context 'when on task thread' do
it 'calls GoodJob.on_thread_error' do
allow(performer).to receive(:next) do
THREAD_HAS_RUN.make_true
raise "Whoops"
end

scheduler = described_class.new(performer)
sleep_until { THREAD_HAS_RUN.true? }

expect(error_proc).to have_received(:call).with(an_instance_of(RuntimeError).and(having_attributes(message: 'Whoops')))

scheduler.shutdown
end
end
end

describe '.instances' do
it 'contains all registered instances' do
scheduler = nil
Expand Down

0 comments on commit 501cc2d

Please sign in to comment.