Merge pull request #434 from retentionscience/aw/exp-backoff

On OctoKit::Forbidden error: retry with exponential backoff
This commit is contained in:
Olle Jonsson 2016-10-20 21:52:29 +02:00 committed by GitHub
commit 9eb8c93019
4 changed files with 65 additions and 9 deletions

View File

@ -29,4 +29,5 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency("octokit", ["~> 4.0"])
spec.add_runtime_dependency("faraday-http-cache")
spec.add_runtime_dependency("activesupport")
spec.add_runtime_dependency("retriable", ["~> 2.1"])
end

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true
require "retriable"
module GitHubChangelogGenerator
# A Fetcher responsible for all requests to GitHub and all basic manipulation with related data
# (such as filtering, validating, e.t.c)
@ -8,6 +9,7 @@ module GitHubChangelogGenerator
class OctoFetcher
PER_PAGE_NUMBER = 100
MAX_THREAD_NUMBER = 25
MAX_FORBIDDEN_RETRIES = 100
CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN"
GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, change log may be " \
"missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument."
@ -280,17 +282,45 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
#
# @return [Object] returns exactly the same, what you put in the block, but wrap it with begin-rescue block
def check_github_response
begin
value = yield
rescue Octokit::Unauthorized => e
Helper.log.error e.message
abort "Error: wrong GitHub token"
Retriable.retriable(retry_options) do
yield
end
rescue Octokit::Forbidden => e
Helper.log.warn e.message
Helper.log.error("#{e.class}: #{e.message}")
sys_abort("Exceeded retry limit")
rescue Octokit::Unauthorized => e
Helper.log.error("#{e.class}: #{e.message}")
sys_abort("Error: wrong GitHub token")
end
# Exponential backoff
def retry_options
{
on: [Octokit::Forbidden],
tries: MAX_FORBIDDEN_RETRIES,
base_interval: sleep_base_interval,
multiplier: 1.0,
rand_factor: 0.0,
on_retry: retry_callback
}
end
def sleep_base_interval
1.0
end
def retry_callback
proc do |exception, try, elapsed_time, next_interval|
Helper.log.warn("RETRY - #{exception.class}: '#{exception.message}'")
Helper.log.warn("#{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try")
Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG
Helper.log.warn @client.rate_limit
end
value
end
def sys_abort(msg)
abort(msg)
end
# Print specified line on the same string

View File

@ -42,7 +42,7 @@ module GitHubChangelogGenerator
# setup parsing options
def self.setup_parser(options)
parser = OptionParser.new do |opts|
parser = OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
opts.banner = "Usage: github_changelog_generator [options]"
opts.on("-u", "--user [USER]", "Username of the owner of target GitHub repo") do |last|
options[:user] = last

View File

@ -12,6 +12,31 @@ describe GitHubChangelogGenerator::OctoFetcher do
let(:fetcher) { GitHubChangelogGenerator::OctoFetcher.new(options) }
describe "#check_github_response" do
context "when returns successfully" do
it "returns block value" do
expect(fetcher.send(:check_github_response) { 1 + 1 }).to eq(2)
end
end
context "when raises Octokit::Unauthorized" do
it "aborts" do
expect(fetcher).to receive(:sys_abort).with("Error: wrong GitHub token")
fetcher.send(:check_github_response) { raise(Octokit::Unauthorized) }
end
end
context "when raises Octokit::Forbidden" do
it "sleeps and retries and then aborts" do
retry_limit = GitHubChangelogGenerator::OctoFetcher::MAX_FORBIDDEN_RETRIES - 1
allow(fetcher).to receive(:sleep_base_interval).exactly(retry_limit).times.and_return(0)
expect(fetcher).to receive(:sys_abort).with("Exceeded retry limit")
fetcher.send(:check_github_response) { raise(Octokit::Forbidden) }
end
end
end
describe "#fetch_github_token" do
token = GitHubChangelogGenerator::OctoFetcher::CHANGELOG_GITHUB_TOKEN
context "when token in ENV exist" do