2017-05-17 12:17:15 -04:00
|
|
|
module Gitlab
|
|
|
|
module Git
|
|
|
|
module Storage
|
|
|
|
class CircuitBreaker
|
2017-10-12 08:13:59 -04:00
|
|
|
include CircuitBreakerSettings
|
|
|
|
|
2017-05-17 12:17:15 -04:00
|
|
|
attr_reader :storage,
|
2017-11-13 10:52:07 -05:00
|
|
|
:hostname
|
2017-05-17 12:17:15 -04:00
|
|
|
|
2017-11-13 10:52:07 -05:00
|
|
|
delegate :last_failure, :failure_count, :no_failures?,
|
|
|
|
to: :failure_info
|
2017-05-17 12:17:15 -04:00
|
|
|
|
|
|
|
def self.for_storage(storage)
|
|
|
|
cached_circuitbreakers = RequestStore.fetch(:circuitbreaker_cache) do
|
|
|
|
Hash.new do |hash, storage_name|
|
2017-09-21 09:55:08 -04:00
|
|
|
hash[storage_name] = build(storage_name)
|
2017-05-17 12:17:15 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
cached_circuitbreakers[storage]
|
|
|
|
end
|
|
|
|
|
2017-09-21 09:55:08 -04:00
|
|
|
def self.build(storage, hostname = Gitlab::Environment.hostname)
|
|
|
|
config = Gitlab.config.repositories.storages[storage]
|
|
|
|
|
|
|
|
if !config.present?
|
|
|
|
NullCircuitBreaker.new(storage, hostname, error: Misconfiguration.new("Storage '#{storage}' is not configured"))
|
|
|
|
elsif !config['path'].present?
|
|
|
|
NullCircuitBreaker.new(storage, hostname, error: Misconfiguration.new("Path for storage '#{storage}' is not configured"))
|
|
|
|
else
|
|
|
|
new(storage, hostname)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(storage, hostname)
|
2017-05-17 12:17:15 -04:00
|
|
|
@storage = storage
|
|
|
|
@hostname = hostname
|
|
|
|
end
|
|
|
|
|
|
|
|
def perform
|
2017-10-19 03:30:04 -04:00
|
|
|
return yield unless enabled?
|
2017-05-17 12:17:15 -04:00
|
|
|
|
|
|
|
check_storage_accessible!
|
|
|
|
|
|
|
|
yield
|
|
|
|
end
|
|
|
|
|
|
|
|
def circuit_broken?
|
|
|
|
return false if no_failures?
|
|
|
|
|
2017-10-19 02:32:55 -04:00
|
|
|
failure_count > failure_count_threshold
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2017-10-19 03:30:04 -04:00
|
|
|
# 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
|
|
|
|
|
2017-09-21 09:55:08 -04:00
|
|
|
def failure_info
|
2017-11-13 10:52:07 -05:00
|
|
|
@failure_info ||= FailureInfo.load(cache_key)
|
2017-08-03 09:24:43 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def check_storage_accessible!
|
|
|
|
if circuit_broken?
|
2017-10-19 02:32:55 -04:00
|
|
|
raise Gitlab::Git::Storage::CircuitOpen.new("Circuit for #{storage} is broken", failure_reset_time)
|
|
|
|
end
|
|
|
|
end
|
2017-05-17 12:17:15 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|