Last active
August 29, 2015 14:17
-
-
Save mumoshu/a8c68d42b5f927dda640 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Reliable | |
def retries(method, up_to:, on:, delay: Tryer.delay, before_retry: Tryer.before_retry, after_call: Tryer.after_call) | |
original_method = :"#{method}_without_reliable" | |
alias_method original_method, method | |
tryer = Tryer.new(up_to: up_to, on: on, delay: delay, before_retry: before_retry, after_call: after_call) | |
define_method method do |*args| | |
tryer.reliably do | |
__send__ original_method, *args | |
end | |
end | |
end | |
class Tryer | |
class << self | |
def delay=(delay) | |
@delay = delay | |
end | |
def delay | |
@delay ||= -> x { x ** 2 } | |
end | |
def before_retry=(before_retry) | |
@before_retry = before_retry | |
end | |
def before_retry | |
@before_retry ||= -> e:, tries: { | |
warn "Retrying on error: #{e.class.name}: #{e}" | |
} | |
end | |
def after_call=(after_call) | |
@after_call = after_call | |
end | |
def after_call | |
@after_call ||= -> value:, error:, tries: {} | |
end | |
end | |
def initialize(up_to:, on:, delay: Tryer.delay, before_retry: Tryer.before_retry, after_call: Tryer.after_call) | |
@enumerable = up_to.respond_to?(:each) ? up_to : up_to.times | |
@before_retry = before_retry | |
@after_call = after_call | |
@delay = delay | |
@retried_errors = [on].flatten | |
end | |
def reliably(&block) | |
tries = 0 | |
last_value = nil | |
last_error = nil | |
@enumerable.each do | |
tries += 1 | |
begin | |
if tries > 1 | |
@before_retry[e: last_error, tries: tries] | |
sleep @delay[tries-1] | |
end | |
last_error = nil | |
last_value = block.call | |
return last_value | |
rescue *@retried_errors => e | |
last_error = e | |
ensure | |
@after_call[value: last_value, error: last_error, tries: tries] | |
end | |
end | |
raise last_error | |
end | |
end | |
end | |
Reliable::Tryer.after_call = -> value:, error:, tries: { | |
puts "#{tries}: value=#{value}, error=#{error.class.name}(#{error})" | |
} | |
class Test | |
extend Reliable | |
class FooError < StandardError; end | |
def foo | |
raise FooError, 'message' if rand < 0.8 | |
"successful result" | |
end | |
retries :foo, up_to: 3.times, on: FooError | |
end | |
# Test.new.foo | |
#1: value=, error=Test::FooError(message) | |
#Retrying on error: Test::FooError: message | |
#2: value=, error=Test::FooError(message) | |
#Retrying on error: Test::FooError: message | |
#3: value=successful result, error=NilClass() | |
#=> "successful result" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# @usage with_retries(3) { randomly_failing_method } | |
# @usage with_retries(3, delay: -> x { x ** 2 }) { randomly_failing_method } | |
def with_retries(retries=3, delay: -> x { x }, &block) | |
result = retries.times.lazy.map do |trial| | |
begin | |
if block.arity == 1 | |
block.call(trial) | |
else | |
block.call | |
end | |
rescue => e | |
trial < retries - 1 ? (sleep delay[trial]; nil) : e | |
end | |
end.find(&:itself) | |
if result.is_a? StandardError | |
raise result | |
else | |
result | |
end | |
end | |
# An imperative variant of with_retries | |
def with_retries2(retries=3, delay: -> x { x }, &block) | |
trial = 0 | |
while trial < retries | |
begin | |
result = | |
if block.arity == 1 | |
block.call(trial) | |
else | |
block.call | |
end | |
return result | |
rescue => e | |
raise e if trial >= retries - 1 | |
trial += 1 | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def with_retrying(tries: 1, on: StandardError, delay: -> x { x }, before_retry: -> e:, tries:, max_tries: { warn "Retrying on error: #{tries}/#{max_tries}: #{e.class.name}: #{e}" }, &block) | |
max_tries = tries | |
tries = 1 | |
retried_errors = [on].flatten | |
begin | |
yield | |
rescue *retried_errors => e | |
if tries < max_tries | |
before_retry[e: e, tries: tries, max_tries: max_tries] | |
tries += 1 | |
retry | |
end | |
raise e | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment