Sidekiq: retry once before erroring
Here's how you can silence/mute the initial Sidekiq error, but still report any errors after the first try.
This reduces noise in the error tracker and makes any errors more actionable.
def perform
# Do work
rescue SomeError => e
retry_once_before_raising_error(e)
end
Read on to implement retry_once_before_raising_error
yourself.
Step 1: Adding retry_count to your jobs
To implement our error silencing logic, we need to access the retry count inside our Sidekiq jobs. We'll start by modifying the BaseJob
class to include a retry_count
attribute. Add the following code to your BaseJob
class:
attr_writer :retry_count
def retry_count
@retry_count ||= 0
end
This allows us to set and retrieve the retry count within job instances.
Step 2: Sidekiq middleware
Next, we'll create a Sidekiq middleware that sets the retry count for each job. Create a new file called retry_count.rb
and add the following code:
module SidekiqMiddleware
class RetryCount
def call(worker, job, _queue)
if worker.respond_to?(:retry_count)
worker.retry_count = job.fetch("retry_count", 0)
end
yield
end
end
end
This middleware makes retry_count
available to you inside of the job.
Step 3: Configuring Sidekiq to use the middleware
To activate the Sidekiq middleware, you need to add it to your Sidekiq configuration. Open your Sidekiq configuration file (e.g., sidekiq.rb
) and add the following code:
config.server_middleware do |chain|
chain.add(SidekiqMiddleware::RetryCount)
end
Step 4: Handling retry Logic and error muting
Now that we have access to the retry count and the middleware set up, we can implement our retry logic and error muting. In the BaseJob
class add the following private method:
class RetryError < RuntimeError; end
def retry_once_before_raising_error(exception)
if retry_count < 1
raise RetryError, exception.message
else
raise exception
end
end
This method checks the retry_count
and raises a custom RetryError
if the count is less than 1 (indicating the first retry). If the count is 1 or higher, it raises the original exception.
Step 5: Excluding RetryErrors in Sentry configuration
To silence the RetryError
exceptions in Sentry, we need to configure Sentry to exclude them. Locate your Sentry error handling configuration and add the following line:
config.excluded_exceptions += ["BaseJob::RetryError"]
This configuration instructs Sentry to ignore any exceptions of the BaseJob::RetryError
type.
Step 6: Implementing retry logic in your job
Finally, you can apply the retry logic and error muting in your job code. In the perform
method of your Sidekiq job, add the following code:
def perform
# Your job code
rescue SomeNoisyError => exception
retry_once_before_raising_error(exception)
end
In this example, we rescue a specific SomeNoisyError
and call the retry_once_before_raising_error
method we defined earlier. This method handles the retry logic based on the retry count and raises the RetryError
when appropriate.
Additional Integration Examples:
This works for any bug tracking service, here are a couple of examples:
Bugsnag:
- Documentation: Bugsnag Error Handling
- Example configuration:
Bugsnag.notify(exception) unless exception.is_a?(BaseJob::RetryError)
Rollbar:
- Documentation: Rollbar Error Handling
- Example configuration:
Rollbar.error(exception) unless exception.is_a?(BaseJob::RetryError)