diff --git a/github_changelog_generator.gemspec b/github_changelog_generator.gemspec index c114b9c..ec22619 100644 --- a/github_changelog_generator.gemspec +++ b/github_changelog_generator.gemspec @@ -29,4 +29,6 @@ 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 diff --git a/lib/github_changelog_generator/octo_fetcher.rb b/lib/github_changelog_generator/octo_fetcher.rb index ee82e07..a1f1f0f 100644 --- a/lib/github_changelog_generator/octo_fetcher.rb +++ b/lib/github_changelog_generator/octo_fetcher.rb @@ -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) @@ -281,47 +282,47 @@ 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 - attempt = 1 - begin - value = yield - rescue Octokit::Unauthorized => e - Helper.log.error e.message - sys_abort("Error: wrong GitHub token") - rescue Octokit::Forbidden => e - attempt += 1 - sleep_time = exp_backoff(attempt) - Helper.log.warn("sleeping #{sleep_time} second") - sys_sleep(sleep_time) - - Helper.log.warn e.message - Helper.log.warn GH_RATE_LIMIT_EXCEEDED_MSG - Helper.log.warn @client.rate_limit - - if attempt >= MAX_FORBIDDEN_RETRIES - sys_abort("Exceeded retry limit") - else - retry - end + Retriable.retriable(retry_options) do + yield end - value + + rescue Octokit::Forbidden => e + 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 - def sys_sleep(seconds) - sleep(seconds) + # 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.new 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 end def sys_abort(msg) abort(msg) end - # Returns the exponential backoff (seconds) for this attempt number - # - # @param [Integer] attempt the attempt number - # @return [Integer] Exponential backoff seconds - def exp_backoff(attempt) - (2**attempt - 1) / 2 - end - # Print specified line on the same string # # @param [String] log_string diff --git a/spec/unit/octo_fetcher_spec.rb b/spec/unit/octo_fetcher_spec.rb index 7a254b4..a4398b1 100644 --- a/spec/unit/octo_fetcher_spec.rb +++ b/spec/unit/octo_fetcher_spec.rb @@ -29,9 +29,9 @@ describe GitHubChangelogGenerator::OctoFetcher do 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).and_return(0) expect(fetcher).to receive(:sys_abort).with("Exceeded retry limit") - expect(fetcher).to receive(:sys_sleep).exactly(retry_limit).times fetcher.send(:check_github_response) { raise(Octokit::Forbidden) } end end