Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 16:49:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 16:49:51 +0300
commit71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e (patch)
tree6a2d93ef3fb2d353bb7739e4b57e6541f51cdd71 /lib/gitlab/redis
parenta7253423e3403b8c08f8a161e5937e1488f5f407 (diff)
Add latest changes from gitlab-org/gitlab@15-9-stable-eev15.9.0-rc42
Diffstat (limited to 'lib/gitlab/redis')
-rw-r--r--lib/gitlab/redis/cache.rb17
-rw-r--r--lib/gitlab/redis/cluster_rate_limiting.rb11
-rw-r--r--lib/gitlab/redis/db_load_balancing.rb23
-rw-r--r--lib/gitlab/redis/duplicate_jobs.rb32
-rw-r--r--lib/gitlab/redis/multi_store.rb64
-rw-r--r--lib/gitlab/redis/rate_limiting.rb27
-rw-r--r--lib/gitlab/redis/repository_cache.rb13
-rw-r--r--lib/gitlab/redis/wrapper.rb17
8 files changed, 107 insertions, 97 deletions
diff --git a/lib/gitlab/redis/cache.rb b/lib/gitlab/redis/cache.rb
index 043f14630d5..647573e59fe 100644
--- a/lib/gitlab/redis/cache.rb
+++ b/lib/gitlab/redis/cache.rb
@@ -2,6 +2,16 @@
module Gitlab
module Redis
+ # Match signature in
+ # https://github.com/rails/rails/blob/v6.1.7.2/activesupport/lib/active_support/cache/redis_cache_store.rb#L59
+ ERROR_HANDLER = ->(method:, returning:, exception:) do
+ Gitlab::ErrorTracking.log_exception(
+ exception,
+ method: method,
+ returning: returning.inspect
+ )
+ end
+
class Cache < ::Gitlab::Redis::Wrapper
CACHE_NAMESPACE = 'cache:gitlab'
@@ -12,9 +22,14 @@ module Gitlab
redis: pool,
compress: Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_REDIS_CACHE_COMPRESSION', '1')),
namespace: CACHE_NAMESPACE,
- expires_in: ENV.fetch('GITLAB_RAILS_CACHE_DEFAULT_TTL_SECONDS', 8.hours).to_i # Cache should not grow forever
+ expires_in: default_ttl_seconds,
+ error_handler: ::Gitlab::Redis::ERROR_HANDLER
}
end
+
+ def self.default_ttl_seconds
+ ENV.fetch('GITLAB_RAILS_CACHE_DEFAULT_TTL_SECONDS', 8.hours).to_i
+ end
end
end
end
diff --git a/lib/gitlab/redis/cluster_rate_limiting.rb b/lib/gitlab/redis/cluster_rate_limiting.rb
new file mode 100644
index 00000000000..e9d1e4f0c3f
--- /dev/null
+++ b/lib/gitlab/redis/cluster_rate_limiting.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Redis
+ class ClusterRateLimiting < ::Gitlab::Redis::Wrapper
+ def self.config_fallback
+ Cache
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/redis/db_load_balancing.rb b/lib/gitlab/redis/db_load_balancing.rb
new file mode 100644
index 00000000000..01276445611
--- /dev/null
+++ b/lib/gitlab/redis/db_load_balancing.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Redis
+ class DbLoadBalancing < ::Gitlab::Redis::Wrapper
+ class << self
+ # The data we store on DbLoadBalancing used to be stored on SharedState.
+ def config_fallback
+ SharedState
+ end
+
+ private
+
+ def redis
+ primary_store = ::Redis.new(params)
+ secondary_store = ::Redis.new(config_fallback.params)
+
+ MultiStore.new(primary_store, secondary_store, store_name)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/redis/duplicate_jobs.rb b/lib/gitlab/redis/duplicate_jobs.rb
deleted file mode 100644
index c76d298da18..00000000000
--- a/lib/gitlab/redis/duplicate_jobs.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Redis
- # Pseudo-store to transition `Gitlab::SidekiqMiddleware::DuplicateJobs` from
- # using `Sidekiq.redis` to using the `SharedState` redis store.
- class DuplicateJobs < ::Gitlab::Redis::Wrapper
- class << self
- def store_name
- 'SharedState'
- end
-
- private
-
- def redis
- primary_store = ::Redis.new(Gitlab::Redis::SharedState.params)
-
- # `Sidekiq.redis` is a namespaced redis connection. This means keys are actually being stored under
- # "resque:gitlab:resque:gitlab:duplicate:". For backwards compatibility, we make the secondary store
- # namespaced in the same way, but omit it from the primary so keys have proper format there.
- # rubocop:disable Cop/RedisQueueUsage
- secondary_store = ::Redis::Namespace.new(
- Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE, redis: ::Redis.new(Gitlab::Redis::Queues.params)
- )
- # rubocop:enable Cop/RedisQueueUsage
-
- MultiStore.new(primary_store, secondary_store, name.demodulize)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/redis/multi_store.rb b/lib/gitlab/redis/multi_store.rb
index aa8f390ac10..a102267d52b 100644
--- a/lib/gitlab/redis/multi_store.rb
+++ b/lib/gitlab/redis/multi_store.rb
@@ -46,13 +46,6 @@ module Gitlab
#
# Ref: https://www.rubydoc.info/github/redis/redis-rb/Redis/Commands
#
- ENUMERATOR_CACHE_HIT_VALIDATOR = {
- scan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? },
- hscan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? },
- sscan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? },
- zscan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? }
- }.freeze
-
READ_CACHE_HIT_VALIDATOR = {
exists: ->(val) { val != 0 },
exists?: ->(val) { val },
@@ -62,13 +55,17 @@ module Gitlab
hgetall: ->(val) { val.is_a?(Hash) && !val.empty? },
hlen: ->(val) { val != 0 },
hmget: ->(val) { val.is_a?(Array) && !val.compact.empty? },
+ hscan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? },
mapped_hmget: ->(val) { val.is_a?(Hash) && !val.compact.empty? },
mget: ->(val) { val.is_a?(Array) && !val.compact.empty? },
+ scan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? },
scard: ->(val) { val != 0 },
sismember: ->(val) { val },
smembers: ->(val) { val.is_a?(Array) && !val.empty? },
sscan: ->(val) { val != ['0', []] },
- ttl: ->(val) { val != 0 && val != -2 }
+ sscan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? },
+ ttl: ->(val) { val != 0 && val != -2 }, # ttl returns -2 if the key does not exist. See https://redis.io/commands/ttl/
+ zscan_each: ->(val) { val.is_a?(Enumerator) && !val.first.nil? }
}.freeze
WRITE_COMMANDS = %i[
@@ -134,20 +131,6 @@ module Gitlab
end
end
- ENUMERATOR_CACHE_HIT_VALIDATOR.each_key do |name|
- define_method(name) do |*args, **kwargs, &block|
- enumerator = if use_primary_and_secondary_stores?
- read_command(name, *args, **kwargs)
- else
- default_store.send(name, *args, **kwargs)
- end
-
- return enumerator if block.nil?
-
- enumerator.each(&block)
- end
- end
-
PIPELINED_COMMANDS.each do |name|
define_method(name) do |*args, **kwargs, &block|
if use_primary_and_secondary_stores?
@@ -186,11 +169,15 @@ module Gitlab
end
def use_primary_and_secondary_stores?
- feature_enabled?("use_primary_and_secondary_stores_for")
+ feature_table_exists? &&
+ Feature.enabled?("use_primary_and_secondary_stores_for_#{instance_name.underscore}") && # rubocop:disable Cop/FeatureFlagUsage
+ !same_redis_store?
end
def use_primary_store_as_default?
- feature_enabled?("use_primary_store_as_default_for")
+ feature_table_exists? &&
+ Feature.enabled?("use_primary_store_as_default_for_#{instance_name.underscore}") && # rubocop:disable Cop/FeatureFlagUsage
+ !same_redis_store?
end
def increment_pipelined_command_error_count(command_name)
@@ -217,6 +204,14 @@ module Gitlab
extra.merge(command_name: command_name, instance_name: instance_name))
end
+ def default_store
+ use_primary_store_as_default? ? primary_store : secondary_store
+ end
+
+ def fallback_store
+ use_primary_store_as_default? ? secondary_store : primary_store
+ end
+
def ping(message = nil)
if use_primary_and_secondary_stores?
# Both stores have to response success for the ping to be considered success.
@@ -231,23 +226,14 @@ module Gitlab
private
# @return [Boolean]
- def feature_enabled?(prefix)
- feature_table_exists? &&
- Feature.enabled?("#{prefix}_#{instance_name.underscore}") && # rubocop:disable Cop/FeatureFlagUsage
- !same_redis_store?
- end
-
- # @return [Boolean]
def feature_table_exists?
+ # Use table_exists? (which uses ActiveRecord's schema cache) instead of Feature.feature_flags_available?
+ # as the latter runs a ';' SQL query which causes a connection to be checked out.
Feature::FlipperFeature.table_exists?
rescue StandardError
false
end
- def default_store
- use_primary_store_as_default? ? primary_store : secondary_store
- end
-
def log_method_missing(command_name, *_args)
return if SKIP_LOG_METHOD_MISSING_FOR_COMMANDS.include?(command_name)
@@ -275,26 +261,26 @@ module Gitlab
def read_one_with_fallback(command_name, *args, **kwargs, &block)
begin
- value = send_command(primary_store, command_name, *args, **kwargs, &block)
+ value = send_command(default_store, command_name, *args, **kwargs, &block)
rescue StandardError => e
log_error(e, command_name,
multi_store_error_message: FAILED_TO_READ_ERROR_MESSAGE)
end
- return value if cache_hit?(command_name, value)
+ return value if block.nil? && cache_hit?(command_name, value)
fallback_read(command_name, *args, **kwargs, &block)
end
def cache_hit?(command, value)
- validator = READ_CACHE_HIT_VALIDATOR[command] || ENUMERATOR_CACHE_HIT_VALIDATOR[command]
+ validator = READ_CACHE_HIT_VALIDATOR[command]
return false unless validator
!value.nil? && validator.call(value)
end
def fallback_read(command_name, *args, **kwargs, &block)
- value = send_command(secondary_store, command_name, *args, **kwargs, &block)
+ value = send_command(fallback_store, command_name, *args, **kwargs, &block)
if value
log_error(ReadFromPrimaryError.new, command_name)
diff --git a/lib/gitlab/redis/rate_limiting.rb b/lib/gitlab/redis/rate_limiting.rb
index 4ae1d55e4ce..12710bafbea 100644
--- a/lib/gitlab/redis/rate_limiting.rb
+++ b/lib/gitlab/redis/rate_limiting.rb
@@ -3,13 +3,28 @@
module Gitlab
module Redis
class RateLimiting < ::Gitlab::Redis::Wrapper
- # The data we store on RateLimiting used to be stored on Cache.
- def self.config_fallback
- Cache
- end
+ class << self
+ # The data we store on RateLimiting used to be stored on Cache.
+ def config_fallback
+ Cache
+ end
+
+ def cache_store
+ @cache_store ||= ActiveSupport::Cache::RedisCacheStore.new(
+ redis: pool,
+ namespace: Cache::CACHE_NAMESPACE,
+ error_handler: ::Gitlab::Redis::ERROR_HANDLER
+ )
+ end
+
+ private
+
+ def redis
+ primary_store = ::Redis.new(::Gitlab::Redis::ClusterRateLimiting.params)
+ secondary_store = ::Redis.new(params)
- def self.cache_store
- @cache_store ||= ActiveSupport::Cache::RedisCacheStore.new(redis: pool, namespace: Cache::CACHE_NAMESPACE)
+ MultiStore.new(primary_store, secondary_store, name.demodulize)
+ end
end
end
end
diff --git a/lib/gitlab/redis/repository_cache.rb b/lib/gitlab/redis/repository_cache.rb
index 8bfbfcfea60..6c7bc8c41d5 100644
--- a/lib/gitlab/redis/repository_cache.rb
+++ b/lib/gitlab/redis/repository_cache.rb
@@ -14,19 +14,10 @@ module Gitlab
redis: pool,
compress: Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_REDIS_CACHE_COMPRESSION', '1')),
namespace: Cache::CACHE_NAMESPACE,
- # Cache should not grow forever
- expires_in: ENV.fetch('GITLAB_RAILS_CACHE_DEFAULT_TTL_SECONDS', 8.hours).to_i
+ expires_in: Cache.default_ttl_seconds,
+ error_handler: ::Gitlab::Redis::ERROR_HANDLER
)
end
-
- private
-
- def redis
- primary_store = ::Redis.new(params)
- secondary_store = ::Redis.new(config_fallback.params)
-
- MultiStore.new(primary_store, secondary_store, store_name)
- end
end
end
end
diff --git a/lib/gitlab/redis/wrapper.rb b/lib/gitlab/redis/wrapper.rb
index e5e1e1d4165..c990655769c 100644
--- a/lib/gitlab/redis/wrapper.rb
+++ b/lib/gitlab/redis/wrapper.rb
@@ -59,16 +59,11 @@ module Gitlab
config_file_path("redis.#{store_name.underscore}.yml"),
# The current Redis instance may have been split off from another one
- # (e.g. TraceChunks was split off from SharedState). There are
- # installations out there where the lowest priority config source
- # (resque.yml) contains bogus values. In those cases, config_file_name
- # should resolve to the instance we originated from (the
- # "config_fallback") rather than resque.yml.
+ # (e.g. TraceChunks was split off from SharedState).
config_fallback&.config_file_name,
# Global config sources:
- ENV['GITLAB_REDIS_CONFIG_FILE'],
- config_file_path('resque.yml')
+ ENV['GITLAB_REDIS_CONFIG_FILE']
].compact.first
end
@@ -199,11 +194,17 @@ module Gitlab
def fetch_config
redis_yml = read_yaml(self.class.redis_yml_path).fetch(@rails_env, {})
instance_config_yml = read_yaml(self.class.config_file_name)[@rails_env]
+ resque_yml = read_yaml(self.class.config_file_path('resque.yml'))[@rails_env]
[
redis_yml[self.class.store_name.underscore],
+ # There are installations out there where the lowest priority config source (resque.yml) contains bogus
+ # values. In those cases, the configuration should be read for the instance we originated from (the
+ # "config_fallback"), either from its specific config file or from redis.yml, before falling back to
+ # resque.yml.
instance_config_yml,
- self.class.config_fallback && redis_yml[self.class.config_fallback.store_name.underscore]
+ self.class.config_fallback && redis_yml[self.class.config_fallback.store_name.underscore],
+ resque_yml
].compact.first
end