Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add request retry with backoff when encountring 429 errors #21511

Merged
merged 2 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion spaceship/lib/spaceship/connect_api/api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,13 @@ def initialize(msg)
end
end

def with_asc_retry(tries = 5, &_block)
class TooManyRequestsError < StandardError
def initialize(msg)
super
end
end

def with_asc_retry(tries = 5, backoff = 1, &_block)
response = yield

status = response.status if response
Expand All @@ -166,6 +172,10 @@ def with_asc_retry(tries = 5, &_block)
raise TimeoutRetryError, msg
end

if status == 429
raise TooManyRequestsError, "Too many requests, backing off #{backoff} seconds"
end

return response
rescue UnauthorizedAccessError => error
tries -= 1
Expand All @@ -186,6 +196,14 @@ def with_asc_retry(tries = 5, &_block)
else
retry
end
rescue TooManyRequestsError => error
if backoff > 3600
raise TooManyRequestsError, "Too many requests, giving up after backing off for > 3600 seconds."
end
puts(error) if Spaceship::Globals.verbose?
Kernel.sleep(backoff)
backoff = backoff * 2
retry
end

def handle_response(response)
Expand Down
25 changes: 25 additions & 0 deletions spaceship/spec/connect_api/api_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,31 @@ def stub_client_request(uri, status, body)
client.get('')
end.to raise_error(Spaceship::AccessForbiddenError)
end

describe 'with_retry' do
it 'sleeps on 429' do
stub_request(:get, client.hostname).
to_return(status: 429).then.
to_return(status: 200, body: "")

expect(Kernel).to receive(:sleep).once.with(1)
expect(client).to receive(:handle_response).once
expect(client).to receive(:request).twice.and_call_original
expect do
client.get('')
end.to_not raise_error
end

it 'sleeps until limit is reached on 429' do
body = JSON.generate({"errors": [{"title": "The request rate limit has been reached.", "details": "We've received too many requests for this API. Please wait and try again or slow down your request rate."}]})
stub_client_request(client.hostname, 429, body)

expect(Kernel).to receive(:sleep).exactly(12).times
expect do
client.get('')
end.to raise_error(Spaceship::ConnectAPI::APIClient::TooManyRequestsError, "Too many requests, giving up after backing off for > 3600 seconds.")
end
end
end

describe "#handle_error" do
Expand Down