summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSean McGivern <sean@mcgivern.me.uk>2017-10-24 09:10:42 +0000
committerSean McGivern <sean@mcgivern.me.uk>2017-10-24 09:10:42 +0000
commit7fbefa3d0452a6f2301da5d7b5ca5bce6d108644 (patch)
treea009dd72d58d3af64d24eba431b2301e45a2b8ea /lib
parentcc17067085cc61c99322eb5934b73bc30b3e1caf (diff)
parent705c15d7af013f8ac9c132888dfe2e613e424dd4 (diff)
downloadgitlab-ce-7fbefa3d0452a6f2301da5d7b5ca5bce6d108644.tar.gz
Merge branch 'bvl-circuitbreaker-backoff' into 'master'
Circuitbreaker backoff and retries Closes #37383 and #38231 See merge request gitlab-org/gitlab-ce!14933
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/git/storage.rb1
-rw-r--r--lib/gitlab/git/storage/circuit_breaker.rb41
-rw-r--r--lib/gitlab/git/storage/circuit_breaker_settings.rb8
-rw-r--r--lib/gitlab/git/storage/forked_storage_check.rb13
-rw-r--r--lib/gitlab/git/storage/null_circuit_breaker.rb4
5 files changed, 54 insertions, 13 deletions
diff --git a/lib/gitlab/git/storage.rb b/lib/gitlab/git/storage.rb
index 08e6c29abad..99518c9b1e4 100644
--- a/lib/gitlab/git/storage.rb
+++ b/lib/gitlab/git/storage.rb
@@ -12,6 +12,7 @@ module Gitlab
CircuitOpen = Class.new(Inaccessible)
Misconfiguration = Class.new(Inaccessible)
+ Failing = Class.new(Inaccessible)
REDIS_KEY_PREFIX = 'storage_accessible:'.freeze
diff --git a/lib/gitlab/git/storage/circuit_breaker.rb b/lib/gitlab/git/storage/circuit_breaker.rb
index 0456ad9a1f3..be7598ef011 100644
--- a/lib/gitlab/git/storage/circuit_breaker.rb
+++ b/lib/gitlab/git/storage/circuit_breaker.rb
@@ -54,7 +54,7 @@ module Gitlab
end
def perform
- return yield unless Feature.enabled?('git_storage_circuit_breaker')
+ return yield unless enabled?
check_storage_accessible!
@@ -64,10 +64,27 @@ module Gitlab
def circuit_broken?
return false if no_failures?
+ failure_count > failure_count_threshold
+ end
+
+ def backing_off?
+ return false if no_failures?
+
recent_failure = last_failure > failure_wait_time.seconds.ago
- too_many_failures = failure_count > failure_count_threshold
+ too_many_failures = failure_count > backoff_threshold
- recent_failure || too_many_failures
+ recent_failure && too_many_failures
+ end
+
+ private
+
+ # The circuitbreaker can be enabled for the entire fleet using a Feature
+ # flag.
+ #
+ # Enabling it for a single host can be done setting the
+ # `GIT_STORAGE_CIRCUIT_BREAKER` environment variable.
+ def enabled?
+ ENV['GIT_STORAGE_CIRCUIT_BREAKER'].present? || Feature.enabled?('git_storage_circuit_breaker')
end
def failure_info
@@ -83,7 +100,7 @@ module Gitlab
return @storage_available if @storage_available
if @storage_available = Gitlab::Git::Storage::ForkedStorageCheck
- .storage_available?(storage_path, storage_timeout)
+ .storage_available?(storage_path, storage_timeout, access_retries)
track_storage_accessible
else
track_storage_inaccessible
@@ -94,7 +111,11 @@ module Gitlab
def check_storage_accessible!
if circuit_broken?
- raise Gitlab::Git::Storage::CircuitOpen.new("Circuit for #{storage} is broken", failure_wait_time)
+ raise Gitlab::Git::Storage::CircuitOpen.new("Circuit for #{storage} is broken", failure_reset_time)
+ end
+
+ if backing_off?
+ raise Gitlab::Git::Storage::Failing.new("Backing off access to #{storage}", failure_wait_time)
end
unless storage_available?
@@ -131,12 +152,6 @@ module Gitlab
end
end
- def cache_key
- @cache_key ||= "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage}:#{hostname}"
- end
-
- private
-
def get_failure_info
last_failure, failure_count = Gitlab::Git::Storage.redis.with do |redis|
redis.hmget(cache_key, :last_failure, :failure_count)
@@ -146,6 +161,10 @@ module Gitlab
FailureInfo.new(last_failure, failure_count.to_i)
end
+
+ def cache_key
+ @cache_key ||= "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage}:#{hostname}"
+ end
end
end
end
diff --git a/lib/gitlab/git/storage/circuit_breaker_settings.rb b/lib/gitlab/git/storage/circuit_breaker_settings.rb
index d2313fe7c1b..257fe8cd8f0 100644
--- a/lib/gitlab/git/storage/circuit_breaker_settings.rb
+++ b/lib/gitlab/git/storage/circuit_breaker_settings.rb
@@ -18,6 +18,14 @@ module Gitlab
application_settings.circuitbreaker_storage_timeout
end
+ def access_retries
+ application_settings.circuitbreaker_access_retries
+ end
+
+ def backoff_threshold
+ application_settings.circuitbreaker_backoff_threshold
+ end
+
private
def application_settings
diff --git a/lib/gitlab/git/storage/forked_storage_check.rb b/lib/gitlab/git/storage/forked_storage_check.rb
index 91d8241f17b..1307f400700 100644
--- a/lib/gitlab/git/storage/forked_storage_check.rb
+++ b/lib/gitlab/git/storage/forked_storage_check.rb
@@ -4,8 +4,17 @@ module Gitlab
module ForkedStorageCheck
extend self
- def storage_available?(path, timeout_seconds = 5)
- status = timeout_check(path, timeout_seconds)
+ def storage_available?(path, timeout_seconds = 5, retries = 1)
+ partial_timeout = timeout_seconds / retries
+ status = timeout_check(path, partial_timeout)
+
+ # If the status check did not succeed the first time, we retry a few
+ # more times to avoid one-off failures
+ current_attempts = 1
+ while current_attempts < retries && !status.success?
+ status = timeout_check(path, partial_timeout)
+ current_attempts += 1
+ end
status.success?
end
diff --git a/lib/gitlab/git/storage/null_circuit_breaker.rb b/lib/gitlab/git/storage/null_circuit_breaker.rb
index 60c6791a7e4..a12d52d295f 100644
--- a/lib/gitlab/git/storage/null_circuit_breaker.rb
+++ b/lib/gitlab/git/storage/null_circuit_breaker.rb
@@ -25,6 +25,10 @@ module Gitlab
!!@error
end
+ def backing_off?
+ false
+ end
+
def last_failure
circuit_broken? ? Time.now : nil
end