mirror of
https://github.com/Homebrew/brew.git
synced 2026-08-12 22:29:27 +04:00
utils: extract rate limit sleep and exponential backoff helpers
Address review feedback: - Add GitHub::API.sleep_for_rate_limit and use it in dev-cmd/bump.rb and dev-cmd/contributions.rb. - Add Utils.exponential_backoff_sleep and use it in dev-cmd/bump.rb, retryable_download.rb and github_packages.rb.
This commit is contained in:
@@ -876,9 +876,7 @@ module Homebrew
|
||||
)
|
||||
consecutive_github_api_errors = 0
|
||||
rescue GitHub::API::RateLimitExceededError => e
|
||||
sleep_seconds = [e.reset - Time.now.to_i, 1].max
|
||||
opoo "GitHub rate limit exceeded, sleeping for #{sleep_seconds} seconds..."
|
||||
sleep sleep_seconds
|
||||
GitHub::API.sleep_for_rate_limit(e)
|
||||
retry
|
||||
rescue GitHub::API::AuthenticationFailedError
|
||||
# Retrying this for the remaining packages cannot succeed, so stop now.
|
||||
@@ -886,9 +884,9 @@ module Homebrew
|
||||
rescue GitHub::API::Error => e
|
||||
github_api_retries += 1
|
||||
if github_api_retries <= MAX_GITHUB_API_RETRIES
|
||||
wait = 2 ** github_api_retries
|
||||
onoe "#{name}: retrying in #{wait}s after a GitHub API error: #{e}"
|
||||
sleep wait
|
||||
Utils.exponential_backoff_sleep(github_api_retries) do |wait|
|
||||
onoe "#{name}: retrying in #{wait}s after a GitHub API error: #{e}"
|
||||
end
|
||||
retry
|
||||
end
|
||||
|
||||
|
||||
@@ -516,9 +516,7 @@ module Homebrew
|
||||
end
|
||||
results
|
||||
rescue GitHub::API::RateLimitExceededError => e
|
||||
sleep_seconds = [e.reset - Time.now.to_i, 1].max
|
||||
opoo "GitHub rate limit exceeded, sleeping for #{sleep_seconds} seconds..."
|
||||
sleep sleep_seconds
|
||||
GitHub::API.sleep_for_rate_limit(e)
|
||||
retry
|
||||
end
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ class GitHubPackages
|
||||
rescue ErrorDuringExecution
|
||||
retry_count += 1
|
||||
odie "Cannot perform an upload to registry after retrying multiple times!" if retry_count >= 10
|
||||
sleep 2 ** retry_count
|
||||
Utils.exponential_backoff_sleep(retry_count)
|
||||
retry
|
||||
end
|
||||
|
||||
|
||||
@@ -88,12 +88,12 @@ module Homebrew
|
||||
tries_remaining = @tries - @try
|
||||
raise if tries_remaining.zero?
|
||||
|
||||
wait = 2 ** @try
|
||||
unless quiet
|
||||
Utils.exponential_backoff_sleep(@try) do |wait|
|
||||
next if quiet
|
||||
|
||||
what = Utils.pluralize("try", tries_remaining)
|
||||
ohai "Retrying download in #{wait}s... (#{tries_remaining} #{what} left)"
|
||||
end
|
||||
sleep wait
|
||||
|
||||
# Preserve the partial `.incomplete` file on network errors so the next
|
||||
# attempt can resume via `--continue-at`. Clear the cache only when the
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "utils/github"
|
||||
|
||||
RSpec.describe GitHub::API do
|
||||
describe "::sleep_for_rate_limit" do
|
||||
it "sleeps for at least 1 second even if the rate limit has already reset" do
|
||||
exception = GitHub::API::RateLimitExceededError.new(
|
||||
"API rate limit exceeded", reset: Time.now.to_i - 10, resource: "core", limit: 5000
|
||||
)
|
||||
expect(described_class).to receive(:sleep).with(1)
|
||||
described_class.sleep_for_rate_limit(exception)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -157,6 +157,23 @@ RSpec.describe Utils do
|
||||
end
|
||||
end
|
||||
|
||||
describe ".exponential_backoff_sleep" do
|
||||
it "sleeps for 2**try seconds" do
|
||||
expect(described_class).to receive(:sleep).with(8)
|
||||
described_class.exponential_backoff_sleep(3)
|
||||
end
|
||||
|
||||
it "sleeps for base**try seconds when a base is given" do
|
||||
expect(described_class).to receive(:sleep).with(27)
|
||||
described_class.exponential_backoff_sleep(3, base: 3)
|
||||
end
|
||||
|
||||
it "yields the wait time before sleeping" do
|
||||
allow(described_class).to receive(:sleep)
|
||||
expect { |block| described_class.exponential_backoff_sleep(2, &block) }.to yield_with_args(4)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".underscore" do
|
||||
# commented out entries require acronyms inflections
|
||||
let(:words) do
|
||||
|
||||
@@ -112,6 +112,15 @@ module Utils
|
||||
"#{prefix}#{stem}#{suffix}"
|
||||
end
|
||||
|
||||
# Sleeps for an exponentially increasing wait (`base ** try` seconds), yielding
|
||||
# the wait time first so callers can print a message before sleeping.
|
||||
sig { params(try: Integer, base: Integer, _blk: T.nilable(T.proc.params(wait: Integer).void)).void }
|
||||
def self.exponential_backoff_sleep(try, base: 2, &_blk)
|
||||
wait = base.pow(try)
|
||||
yield wait if block_given?
|
||||
sleep wait
|
||||
end
|
||||
|
||||
sig { params(author: String).returns({ email: String, name: String }) }
|
||||
def self.parse_author!(author)
|
||||
match_data = /^(?<name>[^<]+?)[ \t]*<(?<email>[^>]+?)>$/.match(author)
|
||||
|
||||
@@ -169,6 +169,14 @@ module GitHub
|
||||
JSON::ParserError,
|
||||
].freeze
|
||||
|
||||
# Sleeps until the rate limit from the given exception has reset.
|
||||
sig { params(exception: RateLimitExceededError).void }
|
||||
def self.sleep_for_rate_limit(exception)
|
||||
sleep_seconds = [exception.reset - Time.now.to_i, 1].max
|
||||
opoo "GitHub rate limit exceeded, sleeping for #{sleep_seconds} seconds..."
|
||||
sleep sleep_seconds
|
||||
end
|
||||
|
||||
# Gets the token from the GitHub CLI for github.com.
|
||||
sig { returns(T.nilable(String)) }
|
||||
def self.github_cli_token
|
||||
|
||||
Reference in New Issue
Block a user