Sidekiq retry on specific failed exception
Unfortunatelly sidekiq/job_retry.rb doesn't support anything like ActiveJob's retry_on therefore you need to write your own:
# app/workers/sync_worker.rb class SyncWorker include Sidekiq::Worker sidekiq_options queue: :foo def perform(model_id, next_param = nil) model = Model.find(model_id) PullDataService.new(model, next_param).call rescue PullDataService::TooManyRequests self.class.perform_in(rand(4..20).minutes, group_id, next_param, retry_count + 1) end end
Now please note that this version will create infinite loop in case service keep on raising PullDataService::TooManyRequests (and no Sidekiq's retry: limits don't apply here)
If you want a version that would stop retry on several attempts:
# app/workers/sync_worker.rb class SyncWorker include Sidekiq::Worker sidekiq_options queue: :foo def perform(model_id, next_param = nil, retry_count = 0) model = Model.find(model_id) PullDataService.new(model, next_param).call rescue PullDataService::TooManyRequests => e if retry_count < 10 self.class.perform_in(rand(4..20).minutes, group_id, next_param, retry_count + 1) else raise e end end end
# spec/workers/sync_worker_spec.rb require "rails_helper" describe SyncWorker do subject(:worker) { described_class.new } let(:model) { create(:model) } let(:service_double) { instance_double(PullDataService) } before { expect(PullDataService).to receive(:new).with(model, nil).and_return(service_double) } it "job perform calls service that succeed should not reschedule anything" do expect(service_double).to receive(:call).with(no_args) expect(described_class).not_to receive(:perform_in) worker.perform(model.id) end it "job perform calls service that failed TooManyRequests should reschedule" do expect(service_double).to receive(:call).with(no_args).and_raise(PullDataService::TooManyRequests) expect(described_class).to receive(:perform_in).with(be_kind_of(Integer), model.id, nil, 1) worker.perform(group.id) end it "rescheduled job beyond retry limit perform calls service that failed TooManyRequests should not reschedule" do expect(service_double).to receive(:call).with(no_args).and_raise(PullDataService::TooManyRequests) expect(described_class).not_to receive(:perform_in) expect { worker.perform(model.id, nil, 10) }.to raise_error(PullDataService::TooManyRequests) end # ...