Few months ago I've created a post about reentrancy: Ruby (Rails, Sinatra) background processing – Reentrancy for your workers is a must be!.
In this post, I will present a nice way to implement such feature for your Sidekiq workers.
Normally a Sidekiq worker looks similar to this one:
class ExampleWorker include Sidekiq::Worker def perform(*args) # Background logic here end end
and if something goes wrong, you should see it in your Sidekiq log (or a bugtracker like Errbit). However there is no reentrancy there. You could catch exceptions and handle reentrancy with this code:
class ExampleWorker include Sidekiq::Worker def perform(*args) # Background logic here rescue => exception # Do something on failure before reraising raise exception end end
but it is not too elegant and if you have multiple Sidekiq workers, than probably you will end-up with a lot of code duplication.
Making your reentrancy code more fancy
Instead of handling reentrancy in every single worker, you could just create a base worker class, that would provide such functionality for all the workers that would inherit from the base one:
class BaseWorker include Sidekiq::Worker def perform(*args) # you need to implement the execute method # execute method should contain code you want to execute in the background execute(*args) rescue => exception after_failure(*args) if respond_to?(:after_failure) raise exception end end
Now, instead of implementing a perform method in every worker, you need to name it (or rename) execute. Perform method will act as a wrapper that will try to execute your worker code and if it fails, will run the after_failure method (if it exists).
Note that the error will be reraised, but now we have a fallback to do for example some database status changes.
class KeywordsWorker < BaseWorker def execute(keyword_name) KeywordsService.new.remotely(keyword_name) end # Bring to an expire state if something goes wrong def after_failure(keyword_name) KeywordsService.new.expire(keyword_name) end end
Of course we might have workers, that won't require reentrancy at all. Then we just skip the after_failure method and thanks to the respond_to? method, everything will work normally.