diff options
| -rw-r--r-- | app/services/retry_service.rb | 63 | ||||
| -rw-r--r-- | spec/services/retry_service_spec.rb | 137 |
2 files changed, 0 insertions, 200 deletions
diff --git a/app/services/retry_service.rb b/app/services/retry_service.rb deleted file mode 100644 index 245dd32df..000000000 --- a/app/services/retry_service.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'result' - -class RetryService - - Retry = Class.new(RuntimeError) - - # @param@ delays: - # An array of delays that are used to retry after a sleep of the indicated - # value in case of failed exceutions. - # Once this array is exhausted the executen fails permanently - # - # @param@ rescue_from: - # During execution all the excpetions from this array +plus RetryService::Retry+ are rescued from and - # trigger just another retry after a `sleep` as indicated above. - # - # @param@ block: - # This optional code is excuted before each retry, it is passed the result of the failed attempt, thus - # an `Exception` and the number of execution already tried. - def initialize( delays: [], rescue_from: [], logger: nil, &blk ) - @intervals = delays - @logger = logger - @registered_exceptions = Array(rescue_from) << Retry - @failure_callback = blk - end - - # @param@ blk: - # The code to be executed it will be retried goverened by the `delay` passed into the initializer - # as described there in case it fails with one of the predefined exceptions or `RetryService::Retry` - # - # Eventually it will return a `Result` object. - def execute &blk - result = execute_protected blk - return result if result.ok? - @intervals.each_with_index do | interval, retry_count | - warn "retry #{retry_count + 1 }; sleeping #{interval}; cause: #{result.value.inspect}" - sleep interval - @failure_callback.try(:call, result.value, retry_count + 1) - result = execute_protected blk - return result if result.ok? - end - result - end - - - private - - def execute_protected blk - result = blk.() - return result if Result === result - Result.ok(result) - rescue Exception => e - if @registered_exceptions.any?{ |re| e.is_a? re } - Result.error(e) - else - raise - end - end - - def warn message - return unless @logger - @logger.try :warn, message - end -end diff --git a/spec/services/retry_service_spec.rb b/spec/services/retry_service_spec.rb deleted file mode 100644 index bb3416373..000000000 --- a/spec/services/retry_service_spec.rb +++ /dev/null @@ -1,137 +0,0 @@ -RSpec.describe RetryService do - subject { described_class.new delays: [2, 3], rescue_from: [NameError, ArgumentError] } - - context 'no retry necessary' do - before do - expect( subject ).not_to receive(:sleep) - end - - it 'returns an ok result' do - expect( subject.execute { 42 } ).to eq(Result.ok(42)) - end - it 'does not fail on nil' do - expect( subject.execute { nil } ).to eq(Result.ok(nil)) - end - - it 'fails wihout retries if raising un unregistered exception' do - expect{ subject.execute{ raise KeyError } }.to raise_error(KeyError) - end - - end - - context 'all retries fail' do - before do - expect( subject ).to receive(:sleep).with(2) - expect( subject ).to receive(:sleep).with(3) - end - it 'fails after raising a registered exception n times' do - result = subject.execute{ raise ArgumentError } - expect( result.status ).to eq(:error) - expect( result.value ).to be_kind_of(ArgumentError) - end - it 'fails with an explicit try again (automatically registered exception)' do - result = subject.execute{ raise RetryService::Retry } - expect( result.status ).to eq(:error) - expect( result.value ).to be_kind_of(RetryService::Retry) - end - end - - context "if at first you don't succeed" do - before do - @count = 0 - expect( subject ).to receive(:sleep).with(2) - end - - it 'succeeds the second time' do - expect( subject.execute{ succeed_later(ArgumentError){ 42 } } ).to eq(Result.ok(42)) - end - - it 'succeeds the second time with try again (automatically registered exception)' do - expect( subject.execute{ succeed_later(RetryService::Retry){ 42 } } ).to eq(Result.ok(42)) - end - end - - context 'last chance' do - before do - @count = 0 - expect( subject ).to receive(:sleep).with(2) - expect( subject ).to receive(:sleep).with(3) - end - it 'succeeds the third time with try again (automatically registered exception)' do - result = subject.execute{ succeed_later(RetryService::Retry, count: 2){ 42 } } - expect( result ).to eq( Result.ok(42) ) - end - end - - context 'failure callback once' do - subject do - described_class.new delays: [2, 3], rescue_from: [NameError, ArgumentError] do |reason, count| - @reason=reason - @callback_count=count - @failures += 1 - end - end - - before do - @failures = 0 - @count = 0 - expect( subject ).to receive(:sleep).with(2) - end - - it 'succeeds the second time and calls the failure_callback once' do - subject.execute{ succeed_later(RetryService::Retry){ 42 } } - expect( @failures ).to eq(1) - end - it '... and the failure is passed into the callback' do - subject.execute{ succeed_later(RetryService::Retry){ 42 } } - expect( @reason ).to be_a(RetryService::Retry) - expect( @callback_count ).to eq(1) - end - end - - context 'failure callback twice' do - subject do - described_class.new delays: [2, 3], rescue_from: [NameError, ArgumentError] do |_reason, _count| - @failures += 1 - end - end - - before do - @failures = 0 - @count = 0 - expect( subject ).to receive(:sleep).with(2) - expect( subject ).to receive(:sleep).with(3) - end - - it 'succeeds the third time and calls the failure_callback twice' do - subject.execute{ succeed_later(NameError, count: 2){ 42 } } - expect( @failures ).to eq(2) - end - end - - context 'failure callback in constructor' do - subject do - described_class.new(delays: [1, 2], &method(:add2failures)) - end - before do - @failures = [] - @count = 0 - expect( subject ).to receive(:sleep).with(1) - expect( subject ).to receive(:sleep).with(2) - end - it 'succeeds the second time and calls the failure_callback once' do - subject.execute{ succeed_later(RetryService::Retry, count: 2){ 42 } } - expect( @failures ).to eq([1,2]) - end - end - - def add2failures( e, c) - @failures << c - end - - def succeed_later error, count: 1, &blk - return blk.() unless @count < count - @count += 1 - raise error, 'error' - end -end |
