Class: MonoVM::Whois::Transport::Middleware::Retry

Inherits:
Middleware::Base
  • Object
show all
Defined in:
lib/monovm/whois/transport/middleware/retry.rb

Overview

Retries failures that are plausibly transient.

Only ConnectionError and MonoVM::Whois::TimeoutError qualify: a dropped TCP connection or a slow registry is worth one more attempt. A ServerRefusedError is not retried, and that restraint is the point — a refusal usually is a rate limit, so hammering it makes the block worse and delays every other lookup in the batch behind the backoff.

Constant Summary collapse

DEFAULT_ATTEMPTS =
2
DEFAULT_BACKOFF =
0.5
RETRIABLE =
[ConnectionError, TimeoutError].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, attempts: DEFAULT_ATTEMPTS, backoff: DEFAULT_BACKOFF, sleeper: nil) ⇒ Retry

Returns a new instance of Retry.

Parameters:

  • attempts (Integer) (defaults to: DEFAULT_ATTEMPTS)

    total tries, so 2 means one retry

  • backoff (Float) (defaults to: DEFAULT_BACKOFF)

    seconds before the first retry, doubling after

  • sleeper (#call) (defaults to: nil)

    injectable so specs do not actually wait



28
29
30
31
32
33
# File 'lib/monovm/whois/transport/middleware/retry.rb', line 28

def initialize(app, attempts: DEFAULT_ATTEMPTS, backoff: DEFAULT_BACKOFF, sleeper: nil)
  @attempts = [attempts.to_i, 1].max
  @backoff = backoff
  @sleeper = sleeper || ->(seconds) { sleep(seconds) }
  super(app)
end

Instance Attribute Details

#attemptsObject (readonly)

Returns the value of attribute attempts.



23
24
25
# File 'lib/monovm/whois/transport/middleware/retry.rb', line 23

def attempts
  @attempts
end

#backoffObject (readonly)

Returns the value of attribute backoff.



23
24
25
# File 'lib/monovm/whois/transport/middleware/retry.rb', line 23

def backoff
  @backoff
end

Instance Method Details

#fetch(query:, endpoint:) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/monovm/whois/transport/middleware/retry.rb', line 35

def fetch(query:, endpoint:)
  attempt = 0
  delay = backoff

  loop do
    attempt += 1

    begin
      return app.fetch(query: query, endpoint: endpoint)
    rescue *RETRIABLE
      raise if attempt >= attempts

      @sleeper.call(delay)
      delay *= 2
    end
  end
end