978647c6cb
Loading `ApplicationSetting` from Redis was responsible for at least 50% of the CPU load of the Redis cluster on GitLab.com. Since these values generally don't change very much, we can load this from the database and cache it in memory, skipping Redis altogther. We use `ActiveSupport::Cache::MemoryStore` as a drop-in replacement for `RedisCacheStore` even though we probably don't need synchronized access within `Thread.current`. Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/63977
81 lines
1.9 KiB
Ruby
81 lines
1.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module CacheableAttributes
|
|
extend ActiveSupport::Concern
|
|
|
|
included do
|
|
after_commit { self.class.expire }
|
|
end
|
|
|
|
class_methods do
|
|
def cache_key
|
|
"#{name}:#{Gitlab::VERSION}:#{Rails.version}".freeze
|
|
end
|
|
|
|
# Can be overridden
|
|
def current_without_cache
|
|
last
|
|
end
|
|
|
|
# Can be overridden
|
|
def defaults
|
|
{}
|
|
end
|
|
|
|
def build_from_defaults(attributes = {})
|
|
final_attributes = defaults
|
|
.merge(attributes)
|
|
.stringify_keys
|
|
.slice(*column_names)
|
|
|
|
new(final_attributes)
|
|
end
|
|
|
|
def cached
|
|
Gitlab::SafeRequestStore[:"#{name}_cached_attributes"] ||= retrieve_from_cache
|
|
end
|
|
|
|
def retrieve_from_cache
|
|
record = cache_backend.read(cache_key)
|
|
ensure_cache_setup if record.present?
|
|
|
|
record
|
|
end
|
|
|
|
def current
|
|
cached_record = cached
|
|
return cached_record if cached_record.present?
|
|
|
|
current_without_cache.tap { |current_record| current_record&.cache! }
|
|
rescue => e
|
|
if Rails.env.production?
|
|
Rails.logger.warn("Cached record for #{name} couldn't be loaded, falling back to uncached record: #{e}")
|
|
else
|
|
raise e
|
|
end
|
|
# Fall back to an uncached value if there are any problems (e.g. Redis down)
|
|
current_without_cache
|
|
end
|
|
|
|
def expire
|
|
cache_backend.delete(cache_key)
|
|
rescue
|
|
# Gracefully handle when Redis is not available. For example,
|
|
# omnibus may fail here during gitlab:assets:compile.
|
|
end
|
|
|
|
def ensure_cache_setup
|
|
# This is a workaround for a Rails bug that causes attribute methods not
|
|
# to be loaded when read from cache: https://github.com/rails/rails/issues/27348
|
|
define_attribute_methods
|
|
end
|
|
|
|
def cache_backend
|
|
Rails.cache
|
|
end
|
|
end
|
|
|
|
def cache!
|
|
self.class.cache_backend.write(self.class.cache_key, self, expires_in: 1.minute)
|
|
end
|
|
end
|