Class: Schwab::Middleware::RateLimit

Inherits:
Faraday::Middleware
  • Object
show all
Defined in:
lib/schwab/middleware/rate_limit.rb

Overview

Faraday middleware for handling rate limits with exponential backoff

Constant Summary collapse

DEFAULT_MAX_RETRIES =

Default maximum number of retries for rate-limited requests

3
DEFAULT_RETRY_DELAY =

Default initial retry delay in seconds

1
DEFAULT_BACKOFF_FACTOR =

Default exponential backoff factor for retries

2
RETRY_STATUSES =

Rate limited and Service Unavailable

[429, 503].freeze

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ RateLimit

Returns a new instance of RateLimit.



18
19
20
21
22
23
24
# File 'lib/schwab/middleware/rate_limit.rb', line 18

def initialize(app, options = {})
  super(app)
  @max_retries = options[:max_retries] || DEFAULT_MAX_RETRIES
  @retry_delay = options[:retry_delay] || DEFAULT_RETRY_DELAY
  @backoff_factor = options[:backoff_factor] || DEFAULT_BACKOFF_FACTOR
  @logger = options[:logger]
end

Instance Method Details

#call(env) ⇒ Faraday::Response

Process the request with rate limit handling

Parameters:

  • env (Faraday::Env)

    The request environment

Returns:

  • (Faraday::Response)

    The response



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/schwab/middleware/rate_limit.rb', line 29

def call(env)
  retries = 0
  delay = @retry_delay

  begin
    response = @app.call(env)

    # Check if we should retry this response
    if should_retry?(response) && retries < @max_retries
      retries += 1

      # Check for Retry-After header
      retry_after = response.headers["retry-after"]
      wait_time = retry_after ? parse_retry_after(retry_after) : delay

      log_retry(env, response, retries, wait_time)

      # Wait before retrying
      sleep(wait_time)

      # Exponential backoff for next retry
      delay *= @backoff_factor

      # Retry the request by raising a custom error
      raise Faraday::RetriableResponse.new(nil, response)
    end

    response
  rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e
    # Retry on network errors
    if retries < @max_retries
      retries += 1

      log_retry_error(env, e, retries, delay)

      sleep(delay)
      delay *= @backoff_factor

      retry
    else
      raise e
    end
  rescue Faraday::RetriableResponse
    # This is our custom retry signal
    retry
  end
end