Skip to content

Instantly share code, notes, and snippets.

@mikeyhew
Created July 8, 2016 01:38
Show Gist options
  • Save mikeyhew/79dfd1189f14811425dad97e7f6545ca to your computer and use it in GitHub Desktop.
Save mikeyhew/79dfd1189f14811425dad97e7f6545ca to your computer and use it in GitHub Desktop.
Custom ShopifyAPI connection class with 429-retrial and throttling
class ApiConnection < ShopifyAPI::Connection
cattr_accessor :logger
self.logger = Logger.new(STDOUT) # for now
class_attribute :leak_rate
self.leak_rate = 2 # 2 per second
class_attribute :call_limit_header
self.call_limit_header = 'X-Shopify-Shop-Api-Call-Limit'
class_attribute :min_call_buffer
self.min_call_buffer = 2
def handle_response(response)
super.tap do
num_calls, limit = response.header[call_limit_header].split('/').map(&:to_i)
if limit - num_calls < min_call_buffer
self.wait_until = Time.now + ((num_calls + min_call_buffer - limit).to_f/leak_rate).seconds
end
logger.debug("API Calls used: #{num_calls}/#{limit}")
end
end
def request(*)
if wait_until
sleep_time = wait_until - Time.now
if sleep_time > 0
logger.debug "waiting #{sleep_time} seconds to allow bucket to drain"
sleep(sleep_time)
end
end
with_retrial do
super
end
end
private
attr_accessor :wait_until
def with_retrial
num_retries = 5
min_wait = 2
retries_left = num_retries
loop do
begin
return yield
rescue ActiveResource::ClientError => e
if e.response.code.to_i == 429 && retries_left > 0
retries_left -= 1
wait_time = e.response['Retry-After'.freeze].to_i
wait_time = min_wait if wait_time < min_wait
logger.info "Got a 429, will retry in #{wait_time} seconds"
sleep(wait_time)
next
else
raise
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment