ActiveJob
provides first class support for retrying or discarding jobs based on certain exceptions. This works great if you have a static list of exceptions that you want to retry your jobs on and this would probably serve 99% use-cases for retry/discard. If you are on this boat, read the details on the official guide.
But if you want to dynamically retry the job based on a configuration, for example, from the database, this post is for you. For the purpose of this post, let’s assume we have a database model with the fields retry_max_attempts
, retry_on_exceptions
and retry_wait_seconds
.
For this, we will use rescue_from
but with StandardError
. A disclaimer, if you are new to Rails, you probably shouldn’t do this unless it is for specific use-cases like the one in this post.
Here is how our rescue_on
block should look like:
rescue_from(StandardError) do |exception|
model = arguments.first
raise exception if model.retry_max_attempts <= executions
caught = model.retry_on_exceptions
.map(&:safe_constantize).compact
.any? { |klass| exception.is_a?(klass) }
raise exception unless caught
retry_job(:wait => model.retry_wait_seconds)
end
It is pretty self explanatory, but here’s the gist.
- We re-throw the exception if we have exceeded the max attempts (we are using the undocumented
executions
, so it is probable that this might break in the future, so you should have a good test coverage around this). - We check if the
retry_on_exceptions
is the one we caught. - If it is, we use
retry_job
with the correct wait.
One additional comment, if you are using Sidekiq, you would want to disable Sidekiq’s retry functionality by using the following in your job.
sidekiq_options :retry => 0