aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/services/retry_service.rb63
-rw-r--r--spec/services/retry_service_spec.rb137
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