sidekiq won't send exception notifier emails - ruby-on-rails

How can I enable emails to be sent from sidekiq when it fails? Currently I know exception notifier is working, though when a sidekiq job fails, it doesn't do anything.

class DelayedWorker
include Sidekiq::Worker
# Utils include watchdog, which will email on failures
include Sidekiq::Util
def perform(type, args)
watchdog('DelayedWorker failed') do
raise 'Doh!'
puts "Doing hard work #{type} #{args.to_json}"
end
end
end
Also make sure you have the newer version of exception notifier, which takes in 2 arguments into it's handle_background_exception method

Related

How to find an event in sentry by job arguments?

I have a rails app, sidekiq and sentry.
I want to find event in sentry by job arguments.
Sample:
I have SomeJob which executed with arguments [{some_arg: 'Arg1'}]
Job failed with error and send event to sentry.
How I can find event by job arguments?
I try full-text search, but it doesn't work
Search in sentry is limited by what they allow you to search by.
From reading their Search docs briefly you can either use:
sentry tags
messages
Either way, you would want to enrich your sentry events.
For example, let's assume you will rescue from the error raised in your job
class SomeJob
include Sidekiq::Worker
def perform(args)
# do stuff with args
rescue StandardError
SentryError.new(args: args)
end
end
SentryJobError is really just a PORO that would be called by your job classes.
class SentryJobError
def initialize(args:)
return if Rails.env.development?
Sentry.configure_scope do |scope|
scope.set_context('job_args', { args: args })
scope.set_context('message', 'job ${args[:some_arg]} failed')
end
end
end

Manually fail a Sidekiq job

I am calling an interactor inside a worker and I want the Sidekiq job to fail if the interactor fails.
def perform(id)
result = RandomInteractor.call(id: id)
# catch and respond to result.failure?
end
Right now, this will display the job as completed rather than failed.
I haven't used interactor gem before but based on your question, this should work:
def perform(id)
result = RandomInteractor.call(id: id)
raise StandardError if result.failure?
end
Since you have setup to not retry failed jobs, this should be marked as Failed as soon as the exception is raised.

why doesn't my delayed job work more than once when being triggered from a rails server?

given the delayed job worker,
class UserCommentsListWorker
attr_accessor :opts
def initialize opts = {}
#opts = opts
end
def perform
UserCommentsList.new(#opts)
end
def before job
p 'before hook', job
end
def after job
p 'after hook', job
end
def success job
p 'success hook', job
end
def error job, exception
p '4', exception
end
def failure job
p '5', job
end
def enqueue job
p '-1', job
end
end
When I run Delayed::Job.enqueue UserCommentsListWorker.new(client: client) from a rails console, I can get repeated sequences of print statements and a proper delayed job lifecyle even hooks to print including the feedback from the worker that the job was a success.
Including the same call to run the worker via a standard rails controller endpoint like;
include OctoHelper
include QueryHelper
include ObjHelper
include StructuralHelper
class CommentsController < ApplicationController
before_filter :authenticate_user!
def index
if params['updateCache'] == 'true'
client = build_octoclient current_user.octo_token
Delayed::Job.enqueue UserCommentsListWorker.new(client: client)
end
end
end
I'm noticing that the worker will run and created the delayed job, but none of the hooks get called and the worker nevers logs the job as completed.
Notice the screenshot,
Jobs 73,75,76 were all triggered via a roundtrip to the above referenced endpoint while job 74 was triggered via the rails console, what is wrong with my setup and/or what am I failing to notice in this process? I will stress that the first time the webserver hits the above controller endpoint, the job queues and runs properly but all subsequent instances where the job should run properly appear to be doing nothing and giving me no feedback in the process.
i would also highlight that i'm never seeing the failure, error or enqueue hooks run.
thanks :)
The long and the short of the answer to this problem was that if you notice, i was attempting to store a client object in the delayed job notification which was causing problems, so therefore, don't store complex objects in the job, just work with basic data ids 1 or strings foo or booleans true etc. capisce?

Test ExceptionNotification middleware in rails unit test

I have a service class that I am testing. The service class makes an API call to an external resource. If the resource comes back empty, I would like to raise an exception that sends an email using the ExceptionNotification gem.
This gem operates as middleware and is usually not enabled in the test environment. However, in my service class test, I would like to test that the exception notification goes out as it's very important we are notified that the API request failed.
My understanding of middleware is that they are usually in the context of a request and an app. However, if I enable the ExceptionNotification gem in config/environments/test.rb and run my unit test, the exception notification email is sent out.
So, my question is how can I temporarily turn on and off this middleware for just this test without having an "app" to add it into, so I figure this must be possible without being in a feature spec.
Ok, here's the code I'm working with and started:
class MyService
ThingNotFound = Class.new(Exception)
def self.doit(params)
the_thing = ApiResource.get_the_thing(params)
raise ThingNotFound unless the_thing.present?
return the_thing
rescue ThingNotFound => e
ExceptionNotifier.notify_exception(e, data: {params: params})
end
end
In config/environments/test.rb, here is the ExceptionNotifier middleware code:
config.before_initialize do
MyApp::Application.config.middleware.use ExceptionNotification::Rack
end
Here's a hacky way I came up with. Open to alternatives. Essentially, when you include it in the test.rb, it instantiates the middleware which sets up some class variables so you can use it outside the context of a Rack app, so we just need to reproduce that.
describe 'MyService' do
before { ExceptionNotification::Rack.new(Recognize::Application, email: {:email_prefix => "[PREFIX] ", :sender_address => "whatever#whatever.com", :exception_recipients => %w(noone#noone.com)}) }
after { ExceptionNotifier.class_variable_set("##notifiers", {}) }
it 'sends an exception email when no thing is found' do
ApiResource.stub(get_the_thing: nil)
expect{ MyService.doit({}) }.to change{ActionMailer::Base.deliveries.length}.by(1)
end
end

Sidekiq Error Handling and stop worker

To avoid running unnecessary, I'd like my Sidekiq worker to make a check at each stage for a certain condition. If that condition is not met, then Sidekiq should stop and report the error.
Currently I have:
class BotWorker
include Sidekiq::Worker
def perform(id)
user = User.find(id)
if user.nil?
# report the error? Thinking of using UserMailer
return false # stop the worker
end
# other processing here
end
This seems like a naive way to handle Sidekiq errors. The app needs to immediately notify admin if something breaks in the worker.
Am I missing something? What is a better way to handle errors in Sidekiq?
You can create your own error handler
Sidekiq.configure_server do |config|
config.error_handlers << Proc.new {|exception,context_hash| MyErrorService.notify(exception,context_hash) }
end

Resources