Skip to content
This repository was archived by the owner on Sep 19, 2018. It is now read-only.

Commit

Permalink
implement preemptive rate limiting mutex and 429 handling, closes #9
Browse files Browse the repository at this point in the history
  • Loading branch information
z64 committed Jul 26, 2017
1 parent 946e380 commit c61375a
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions lib/rls/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'rest-client'
require 'json'
require 'time'
require 'rls/objects/platform'
require 'rls/objects/player'
require 'rls/objects/season'
Expand Down Expand Up @@ -122,19 +123,61 @@ def seasons(renew = false)
end
end

# Performs a mutex-protected request to the API with rate limit handling.
# @param type [String, Symbol] HTTP verb
# @param endpoint [String, Symbol] The API endpoint
# @param attributes [Array<Hash>] Header and query parameters
# passed along with the request
# @return [Hash] The parsed JSON response
def request(type, endpoint, *attributes)
attributes << {} if attributes.empty?
response = raw_request(type, endpoint, attributes)
JSON.parse(response)
@mutex ||= Mutex.new

@mutex.synchronize do
sleep until_reset if will_be_rate_limited?

begin
@last_response = raw_request(type, endpoint, attributes)
rescue RestClient::TooManyRequests
sleep until_reset
retry
end

JSON.parse(@last_response)
end
end

private

# @return [String, nil] if it exists, the specific header from the last API request
def last_header(key)
@last_response&.headers&.dig(key)
end

# @return [Float] the amount of time until the rate limit resets
def until_reset
(last_header(:x_rate_limit_reset_remaining) || 0).to_i / 1000.0
end

# @return [Integer] number of requests until we're rate limited
def remaining_requests
(last_header(:x_rate_limit_remaining) || 0).to_i / 1000.0
end

# @return [Time, nil] if it exists, when the rate limit will be reset
def rate_limit_reset
str = last_header(:x_rate_limit_reset)
Time.parse(str) if str
end

# @return [true, false] if the next request will be rate limited
def will_be_rate_limited?
return false unless @last_response
return false if Time.now > rate_limit_reset
remaining_requests.zero?
end

# Performs a request to the API. Not protected by a rate-limit mutex!
# @param type [String, Symbol] HTTP verb
# @param endpoint [String, Symbol] The API endpoint
# @param attributes [Array<Hash>] Header and query parameters
Expand Down

0 comments on commit c61375a

Please sign in to comment.