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)
endRead 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::RetryErrortype.
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)