Class: HumanTone::Retry

Inherits:
Object
  • Object
show all
Defined in:
lib/humantone/retry.rb

Constant Summary collapse

DEFAULT_MAX_RETRIES =
2
EXPONENTIAL_BASE_MS =
500
RATE_LIMIT_BASE_MS =
1000
JITTER_MS_MIN =
-200
JITTER_MS_MAX =
200
SERVER_ERROR_RANGE =
(500..599)

Instance Method Summary collapse

Constructor Details

#initialize(max_retries: DEFAULT_MAX_RETRIES, retry_on_post: false, logger: nil) ⇒ Retry

Returns a new instance of Retry.



17
18
19
20
21
# File 'lib/humantone/retry.rb', line 17

def initialize(max_retries: DEFAULT_MAX_RETRIES, retry_on_post: false, logger: nil)
  @max_retries = max_retries.to_i
  @retry_on_post = retry_on_post
  @logger = logger || Logger.new(IO::NULL)
end

Instance Method Details

#backoff_seconds(error:, attempt:) ⇒ Object



57
58
59
60
61
62
# File 'lib/humantone/retry.rb', line 57

def backoff_seconds(error:, attempt:)
  base_ms = base_backoff_ms(error: error, attempt: attempt)
  jitter_ms = rand(JITTER_MS_MIN..JITTER_MS_MAX).to_i
  seconds = (base_ms + jitter_ms).to_f / 1000.0
  seconds.negative? ? 0.0 : seconds
end

#call(method:, endpoint:) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/humantone/retry.rb', line 23

def call(method:, endpoint:)
  attempt = 0
  begin
    yield
  rescue Errors::Error => e
    if attempt < @max_retries && should_retry?(e, method: method, endpoint: endpoint)
      delay = backoff_seconds(error: e, attempt: attempt)
      @logger.info('HumanTone') do
        "retry: #{method.upcase} /v1/#{endpoint} attempt #{attempt + 1}/#{@max_retries} " \
          "after #{format('%.3f', delay)}s due to #{e.class.name.split('::').last}: #{e.message}"
      end
      sleep_for(delay)
      attempt += 1
      retry
    end
    raise
  end
end

#should_retry?(error, method:, endpoint:) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/humantone/retry.rb', line 42

def should_retry?(error, method:, endpoint:)
  case error
  when Errors::RateLimitError
    true
  when Errors::TimeoutError
    false
  when Errors::NetworkError
    method == :get || @retry_on_post
  when Errors::APIError
    decide_for_api_error(error, method: method, endpoint: endpoint)
  else
    false
  end
end