Isn't there the possibility to handle errors with delayed_job gem? I would like to find a way (runtime) to send an email to a given account while the errors occur and after it performs delete them.
Is this what you want?
def do_something
# some code here
begin
# something that could go wrong here
rescue Exception => e
YourMailer.delay.your_method(e.to_s)
# don't know what you want to delete, but you can do it here
end
end
Found the solution here : http://blog.salsify.com/engineering/delayed-jobs-callbacks-and-hooks-in-rails
# config/initizalizers/delayed_job.rb
class ExceptionMailerPlugin < Delayed::Plugin
callbacks do |lifecycle|
lifecycle.around(:invoke_job) do |job, *args, &block|
begin
# Forward the call to the next callback in the callback chain
block.call(job, *args)
rescue Exception => error
ErrorMailer.exception_mail(error)
# Make sure we propagate the failure!
raise error
end
end
end
end
Delayed::Worker.plugins << ExceptionMailerPlugin
Not that code above doesn't report all the problems .. We recently got a lot issues on worker dynos and we were not even aware about them because we were listening for errors only ...
DelayedJob knows 2 type of errors - errors and failures. In our case - failures were triggered on lower level because we were using Octopus gem. Failure message was: undefined method[]' for nil:NilClass`
class DelayedJobExceptionPlugin < Delayed::Plugin
def self.report_error(exception, job, type: 'error')
Airbrake.notify(
exception,
error_message: "#{exception.class.name}: #{exception.message}",
backtrace: exception.backtrace,
component: 'DelayedJob Worker',
type: type,
parameters: {
failed_job: job.inspect
},
)
end
callbacks do |lifecycle|
lifecycle.around(:invoke_job) do |job, *args, &block|
begin
# Forward the call to the next callback in the callback chain
block.call(job, *args)
rescue Exception => exception
DelayedJobExceptionPlugin.report_error(exception, job)
raise error
end
end
lifecycle.after(:failure) do |job| # Note - some jobs did not trigger Airbrake.notify if we do not use failure!
exception = ::RuntimeError.new('DelayedJob Failure')
DelayedJobExceptionPlugin.report_error(exception, job, type: 'failure')
raise exception if Rails.env.test?
end
end
end
Delayed::Worker.plugins << DelayedJobExceptionPlugin
Related
Is it possible to specify which find_by! exception is raised in the following example (I want the second one to be raised, not the first):
def self.test
Instance.stubs(:find_by!).raises(ActiveRecord::RecordNotFound)
begin
function_one
rescue ActiveRecord::RecordNotFound
puts 'Failure'
end
begin
function_two
rescue ActiveRecord::RecordNotFound
puts 'Success'
end
end
def self.function_one
Model.find_by!(id: 1)
end
def self.function_two
Model.find_by!(id: 1)
end
*Assume id: 1 does not exist. As in the example, also assume these will be static class functions, but please mention any differences in the case that add any_instance will not be enough for instance methods.
How about this?
Instance.stubs(:find_by!).returns('result').then.raises(ActiveRecord::RecordNotFound)
I'm using ActiveJob, and I have some questions about the methods discard_on and retry_on that capture exceptions.
Does the ActiveJob define their order and execution order? (I guess it's related, but I'm not sure.)
Assuming relevant, I hope that retry_on only catches custom exceptions, and that other exceptions are caught by discard_on.
I see discard_on and retry_on in the source code is to use rescue_from, because after rescue_from defined statement to perform first so here is the way I own assumptions defined, hope you can help me to point out that whether it is right, of course, if you have a better way to achieve the same functionality would you please tell me, thank you very much.
class RemoteServiceJob < ActiveJob::Base
discard_on StandardError # second catch other exceptions
retry_on MyCustomException, wait: 5.seconds, attempts: 3 # first catch custom exceptions
end
In my tests, the order of execution is exactly the same as I expected, because rescue_from executes in the opposite order of declaration, and the order of exception declaration needs to be the same.
def discard_on(exception)
rescue_from exception do |error|
if block_given?
yield self, error
else
logger.error "Discarded #{self.class} due to a #{exception}. The original exception was #{error.cause.inspect}."
end
end
end
def retry_on(exception, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
rescue_from exception do |error|
if executions < attempts
logger.error "Retrying #{self.class} in #{wait} seconds, due to a #{exception}. The original exception was #{error.cause.inspect}."
retry_job wait: determine_delay(wait), queue: queue, priority: priority
else
if block_given?
yield self, error
else
logger.error "Stopped retrying #{self.class} due to a #{exception}, which reoccurred on #{executions} attempts. The original exception was #{error.cause.inspect}."
raise error
end
end
end
end
Some background: I have some Ruby code in a large codebase (Rails) that raises an exception under certain conditions. The exception however does not "occur" as expected, it is silently discarded. I assume that some other code (a gem) rescues the exception, maybe accidentally.
How can I determine where that exception is being rescued?
I do have full control over the exception. So maybe there's a way for an exception to know when or that it is being rescued?
Contrived example:
# code outside my control
def foo
yield
rescue
end
def black_box(&block)
foo(&block)
end
# my code
black_box do
puts 'about to raise'
raise
puts 'never gets here'
end
Output:
about to raise
So the exception was rescued. How can I identify (from within "my code") that it was rescued in foo?
The only way I can think of (right now) is manual debugging/inspection.
When you're about to raise that exception you'd like to track, inspect current caller. This gives you a call stack. Now visit each line/method in your editor and look for rescues that are too much greedy.
As for more "automatic" ways, I don't see any. Ruby exceptions don't have on_rescue callbacks or anything like that, so they can't know they're being rescued.
[after 3 years, I'm answering my own question here]
At least in MRI, rescue is handled just like when in a case statement!
For example,
begin
# ...
rescue A, B
# ...
rescue C
# ...
rescue
# ...
end
is evaluated like:
case exception
when A, B
# ...
when C
# ...
when StandardError
# ...
end
Under the hood, both of the above execute: (until a match is found)
A === exception
B === exception
C === exception
StandardError === Exception
with === being Module#=== because A, B, C, and StandardError are classes.
That === call can be used to track down a potential rescue, e.g. by overriding Module#=== in Exception: (where it becomes a class method)
# foo.rb
def foo
yield
rescue
end
def black_box(&block)
foo(&block)
end
class Exception
def self.===(other)
cl = caller_locations[0]
puts "#{cl.path}:#{cl.lineno} - #{$!.inspect}"
super
end
end
require_relative 'foo.rb'
black_box do
puts 'about to raise'
raise 'error message'
puts 'never gets here'
end
Output:
about to raise
foo.rb:5 - #<RuntimeError: error message>
TracePoint could also be used to intercept the method call:
TracePoint.trace(:c_call) do |tp|
if tp.defined_class == Module && tp.method_id == :=== && tp.self <= Exception
puts "#{tp.path}:#{tp.lineno} - #{$!.inspect}"
end
end
I am wondering on how do you access ActiveJob perform parameters in the resue block, such as
def perform object
end
rescue_from Exception do |e|
if e.class != ActiveRecord::RecordNotFound
**job.arguments.first**
# do something
end
end
Thank You !!
It is possible with arguments inside the rescue_from block:
rescue_from(StandardError) do |exception|
user = arguments[0]
post = arguments[1]
# ...
end
def perform(user, post)
# ...
end
This also works for callbacks as well (e.g. inside after_perform).
I had no idea on that as well, then simply decided to try and use self inside the rescue_from block, and it worked.
rescue_from(StandardError) do |ex|
puts ex.inspect
puts self.job_id
# etc.
end
As a side note -- NEVER ever rescue Exception:
Why is it a bad style to `rescue Exception => e` in Ruby?
You can access all Bindings by ex.bindings. To make sure you get the correct binding for your job you should check the receiver like this1:
method_binding = ex.bindings.find { |b| b.receiver.is_a?(self.class) }
Then you can get all local variables with .local_variable_get. Since method arguments are also local variables, you can at least explicitly fetch them:
user = method_binding.local_variable_get(:user)
post = method_binding.local_variable_get(:post)
So for you example:
def perform object
end
rescue_from Exception do |e|
if e.class != ActiveRecord::RecordNotFound
method_binding = ex.bindings.find { |b| b.receiver.is_a?(self.class) }
object = method_binding.local_variable_get(:object)
# do something
end
end
1. It is still possible that this binding is not the one of perform if you call other instance methods in your job's perform method and the error happens there. This can also be taken in account but is left out for brevity.
I am using Memcached as an Object Store with my Rails application where I store search results which are User objects in memcached
Now when I fetch the data out I get the Memcached Undefined Class/Module Error. I found a solution for this problem in this blog
http://www.philsergi.com/2007/06/rails-memcached-undefinded-classmodule.html
before_filter :preload_models
def preload_models
Model1
Model2
end
which recommends pre-loading the models before hand. I would like to know if there is a more elegant solution to this problem and are there any drawbacks in using the preloading technique.
Thanks in advance
I had this problem as well and I think i came up with a nice solution.
You can overwrite the fetch method and rescue the error and load the right constants.
module ActiveSupport
module Cache
class MemCacheStore
# Fetching the entry from memcached
# For some reason sometimes the classes are undefined
# First rescue: trying to constantize the class and try again.
# Second rescue, reload all the models
# Else raise the exception
def fetch(key, options = {})
retries = 2
begin
super
rescue ArgumentError, NameError => exc
if retries == 2
if exc.message.match /undefined class\/module (.+)$/
$1.constantize
end
retries -= 1
retry
elsif retries == 1
retries -= 1
preload_models
retry
else
raise exc
end
end
end
private
# There are errors sometimes like: undefined class module ClassName.
# With this method we re-load every model
def preload_models
#we need to reference the classes here so if coming from cache Marshal.load will find them
ActiveRecord::Base.connection.tables.each do |model|
begin
"#{model.classify}".constantize
rescue Exception
end
end
end
end
end
end
Ran across this today, managed to come up with a more terse solution that should work for all classes.
Rails.cache.instance_eval do
def fetch(key, options = {}, rescue_and_require=true)
super(key, options)
rescue ArgumentError => ex
if rescue_and_require && /^undefined class\/module (.+?)$/ =~ ex.message
self.class.const_missing($1)
fetch(key, options, false)
else
raise ex
end
end
end
Not sure why [MemCacheStore] is not having is [MemCacheStore.const_missing] method called and everything getting called in the normal “Rails-y” way. But, this should emulate that!
Cheers,
Chris