-
Notifications
You must be signed in to change notification settings - Fork 282
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
Failover handlers when async_handler fails #135
Changes from 5 commits
521efa1
d469545
427b576
7794918
77fdb3f
367a48d
f810954
e5f01ec
41f92d3
29dd3b0
c628d57
8f2846e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,8 @@ | |
require 'rollbar/active_record_extension' if defined?(ActiveRecord) | ||
require 'rollbar/util' | ||
require 'rollbar/railtie' if defined?(Rails) | ||
require 'rollbar/delay/girl_friday' | ||
require 'rollbar/delay/thread' | ||
|
||
unless ''.respond_to? :encode | ||
require 'iconv' | ||
|
@@ -28,6 +30,8 @@ class << self | |
attr_writer :configuration | ||
attr_accessor :last_report | ||
|
||
@file_semaphore = Mutex.new | ||
|
||
# Similar to configure below, but used only internally within the gem | ||
# to configure it without initializing any of the third party hooks | ||
def preconfigure | ||
|
@@ -46,9 +50,8 @@ def preconfigure | |
# end | ||
def configure | ||
# if configuration.enabled has not been set yet (is still 'nil'), set to true. | ||
if configuration.enabled.nil? | ||
configuration.enabled = true | ||
end | ||
configuration.enabled = true if configuration.enabled.nil? | ||
|
||
yield(configuration) | ||
|
||
require_hooks | ||
|
@@ -225,6 +228,7 @@ def log_warning(message) | |
puts "[Rollbar] #{message}" | ||
end | ||
end | ||
|
||
def log_debug(message) | ||
begin | ||
logger.debug message | ||
|
@@ -234,6 +238,12 @@ def log_debug(message) | |
end | ||
end | ||
|
||
def default_async_handler | ||
return Rollbar::Delay::GirlFriday if defined?(GirlFriday) | ||
|
||
Rollbar::Delay::Thread | ||
end | ||
|
||
private | ||
|
||
def attach_request_data(payload, request_data) | ||
|
@@ -425,29 +435,44 @@ def send_payload(payload) | |
end | ||
|
||
def schedule_payload(payload) | ||
if payload.nil? | ||
return | ||
end | ||
return if payload.nil? | ||
|
||
log_info '[Rollbar] Scheduling payload' | ||
|
||
if configuration.use_async | ||
unless configuration.async_handler | ||
configuration.async_handler = method(:default_async_handler) | ||
end | ||
|
||
if configuration.write_to_file | ||
unless @file_semaphore | ||
@file_semaphore = Mutex.new | ||
end | ||
end | ||
|
||
configuration.async_handler.call(payload) | ||
process_async_payload(payload) | ||
else | ||
process_payload(payload) | ||
end | ||
end | ||
|
||
def process_async_payload(payload) | ||
configuration.async_handler ||= default_async_handler | ||
configuration.async_handler.call(payload) | ||
rescue | ||
raise unless configuration.failover_handlers.any? | ||
|
||
async_failover(payload) | ||
end | ||
|
||
def async_failover(payload) | ||
index = 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a reason you used an index here instead of iterating over There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought it'd be easier to read, but you are right. I'll change it to make it more rubyst :-D. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brianr changed. I've removed the call to |
||
failover_handlers = configuration.failover_handlers | ||
|
||
begin | ||
handler = failover_handlers[index] | ||
handler.call(payload) | ||
rescue => e | ||
index += 1 | ||
|
||
if index >= failover_handlers.size | ||
report_internal_error(e) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't really an internal error (i.e. a bug in the gem); it's just that all of the handlers failed. I think we should do the same thing in this case as for line 453 above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should re-raise the exception here, what do you think? And finally execute https://github.com/jondeandres/rollbar-gem/blob/failover/lib/rollbar.rb#L112. |
||
else | ||
retry | ||
end | ||
end | ||
end | ||
|
||
def build_payload(data) | ||
payload = { | ||
'access_token' => configuration.access_token, | ||
|
@@ -534,21 +559,6 @@ def server_data | |
data | ||
end | ||
|
||
def default_async_handler(payload) | ||
if defined?(GirlFriday) | ||
unless @queue | ||
@queue = GirlFriday::WorkQueue.new(nil, :size => 5) do |payload| | ||
process_payload(payload) | ||
end | ||
end | ||
|
||
@queue.push(payload) | ||
else | ||
log_warning '[Rollbar] girl_friday not found to handle async call, falling back to Thread' | ||
Thread.new { process_payload(payload) } | ||
end | ||
end | ||
|
||
# Reports an internal error in the Rollbar library. This will be reported within the configured | ||
# Rollbar project. We'll first attempt to provide a report including the exception traceback. | ||
# If that fails, we'll fall back to a more static failsafe response. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
module Rollbar | ||
module Delay | ||
class GirlFriday | ||
|
||
class << self | ||
attr_accessor :queue | ||
|
||
def call(payload) | ||
new.call(payload) | ||
end | ||
end | ||
|
||
def queue_class | ||
::GirlFriday::WorkQueue | ||
end | ||
|
||
def call(payload) | ||
self.class.queue = queue_class.new(nil, :size => 5) do |payload| | ||
Rollbar.process_payload(payload) | ||
end | ||
|
||
self.class.queue.push(payload) | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
require 'resque' | ||
|
||
module Rollbar | ||
module Delay | ||
class Resque | ||
def self.call(payload) | ||
new.call(payload) | ||
end | ||
|
||
def call(payload) | ||
::Resque.enqueue(Job, payload) | ||
end | ||
|
||
class Job | ||
def self.queue; 'default'; end | ||
|
||
def self.perform(payload) | ||
new.perform(payload) | ||
end | ||
|
||
def perform(payload) | ||
Rollbar.process_payload(payload) | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module Rollbar | ||
module Delay | ||
class Thread | ||
def self.call(payload) | ||
new.call(payload) | ||
end | ||
|
||
def call(payload) | ||
::Thread.new { Rollbar.process_payload(payload) } | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
require 'spec_helper' | ||
|
||
# require girl_friday in the test instead in the implementation | ||
# just to let the user decide to load it or not | ||
require 'girl_friday' | ||
require 'rollbar/delay/girl_friday' | ||
|
||
describe Rollbar::Delay::GirlFriday do | ||
describe '.call' do | ||
let(:payload) do | ||
{ :key => 'value' } | ||
end | ||
|
||
it 'push the payload into the queue' do | ||
expect_any_instance_of(::GirlFriday::WorkQueue).to receive(:push).with(payload) | ||
described_class.call(payload) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
require 'spec_helper' | ||
require 'rollbar/delay/resque' | ||
|
||
describe Rollbar::Delay::Resque do | ||
describe '.call' do | ||
let(:payload) do | ||
{ :key => 'value' } | ||
end | ||
|
||
before do | ||
allow(Resque).to receive(:inline?).and_return(true) | ||
end | ||
|
||
it 'process the payload' do | ||
loaded_hash = MultiJson.load(MultiJson.dump(payload)) | ||
|
||
expect(Rollbar).to receive(:process_payload).with(loaded_hash) | ||
described_class.call(payload) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
require 'spec_helper' | ||
|
||
describe Rollbar::Delay::Thread do | ||
describe '.call' do | ||
let(:payload) { { :key => 'value' } } | ||
|
||
it 'process the payload in a new thread' do | ||
expect(Rollbar).to receive(:process_payload).with(payload) | ||
|
||
th = described_class.call(payload) | ||
th.join | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this exception going to go?
Asking because I want to make sure that:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I'm only reraising the exception in case that the handler failed to process the payload. Really this is the actual behaviour. If you don't have any failover handler, then it just crashes, isn't it?
Well, it will not crash cause it's rescued in https://github.com/jondeandres/rollbar-gem/blob/failover/lib/rollbar.rb#L111.
This will be logged in https://github.com/jondeandres/rollbar-gem/blob/failover/lib/rollbar.rb#L566 finally