diff options
author | Alejandro RodrÃguez <alejorro70@gmail.com> | 2018-03-06 21:12:29 -0300 |
---|---|---|
committer | Alejandro RodrÃguez <alejorro70@gmail.com> | 2018-03-06 21:12:29 -0300 |
commit | 5171e2f3d4fdc681a58e11f9615afa968324a278 (patch) | |
tree | 256a12ed77a7a6aabff58bc267a70d476cc4dfa7 /lib | |
parent | 35f6efaee05835b75e605e1f269e57a8d6daf3fa (diff) | |
download | gitlab-ce-5171e2f3d4fdc681a58e11f9615afa968324a278.tar.gz |
Refactor RepositoryCache to make it usable in other classes
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/repository_cache.rb | 32 | ||||
-rw-r--r-- | lib/gitlab/repository_cache_adapter.rb | 84 | ||||
-rw-r--r-- | lib/repository_cache.rb | 30 |
3 files changed, 116 insertions, 30 deletions
diff --git a/lib/gitlab/repository_cache.rb b/lib/gitlab/repository_cache.rb new file mode 100644 index 00000000000..b7650f6ed81 --- /dev/null +++ b/lib/gitlab/repository_cache.rb @@ -0,0 +1,32 @@ +# Interface to the Redis-backed cache store +module Gitlab + class RepositoryCache + attr_reader :repository, :namespace, :backend + + def initialize(repository, backend = Rails.cache) + @repository = repository + @namespace = "#{repository.full_path}:#{repository.project.id}" + @backend = backend + end + + def cache_key(type) + "#{type}:#{namespace}" + end + + def expire(key) + backend.delete(cache_key(key)) + end + + def fetch(key, &block) + backend.fetch(cache_key(key), &block) + end + + def exist?(key) + backend.exist?(cache_key(key)) + end + + def read(key) + backend.read(cache_key(key)) + end + end +end diff --git a/lib/gitlab/repository_cache_adapter.rb b/lib/gitlab/repository_cache_adapter.rb new file mode 100644 index 00000000000..7f64a8c9e46 --- /dev/null +++ b/lib/gitlab/repository_cache_adapter.rb @@ -0,0 +1,84 @@ +module Gitlab + module RepositoryCacheAdapter + extend ActiveSupport::Concern + + class_methods do + # Wraps around the given method and caches its output in Redis and an instance + # variable. + # + # This only works for methods that do not take any arguments. + def cache_method(name, fallback: nil, memoize_only: false) + original = :"_uncached_#{name}" + + alias_method(original, name) + + define_method(name) do + cache_method_output(name, fallback: fallback, memoize_only: memoize_only) do + __send__(original) # rubocop:disable GitlabSecurity/PublicSend + end + end + end + end + + # RepositoryCache to be used. Should be overridden by the including class + def cache + raise NotImplementedError + end + + # Caches the supplied block both in a cache and in an instance variable. + # + # The cache key and instance variable are named the same way as the value of + # the `key` argument. + # + # This method will return `nil` if the corresponding instance variable is also + # set to `nil`. This ensures we don't keep yielding the block when it returns + # `nil`. + # + # key - The name of the key to cache the data in. + # fallback - A value to fall back to in the event of a Git error. + def cache_method_output(key, fallback: nil, memoize_only: false, &block) + ivar = cache_instance_variable_name(key) + + if instance_variable_defined?(ivar) + instance_variable_get(ivar) + else + # If the repository doesn't exist and a fallback was specified we return + # that value inmediately. This saves us Rugged/gRPC invocations. + return fallback unless fallback.nil? || cache.repository.exists? + + begin + value = + if memoize_only + yield + else + cache.fetch(key, &block) + end + + instance_variable_set(ivar, value) + rescue Gitlab::Git::Repository::NoRepository + # Even if the above `#exists?` check passes these errors might still + # occur (for example because of a non-existing HEAD). We want to + # gracefully handle this and not cache anything + fallback + end + end + end + + # Expires the caches of a specific set of methods + def expire_method_caches(methods) + methods.each do |key| + cache.expire(key) + + ivar = cache_instance_variable_name(key) + + remove_instance_variable(ivar) if instance_variable_defined?(ivar) + end + end + + private + + def cache_instance_variable_name(key) + :"@#{key.to_s.tr('?!', '')}" + end + end +end diff --git a/lib/repository_cache.rb b/lib/repository_cache.rb deleted file mode 100644 index 068a95790c0..00000000000 --- a/lib/repository_cache.rb +++ /dev/null @@ -1,30 +0,0 @@ -# Interface to the Redis-backed cache store used by the Repository model -class RepositoryCache - attr_reader :namespace, :backend, :project_id - - def initialize(namespace, project_id, backend = Rails.cache) - @namespace = namespace - @backend = backend - @project_id = project_id - end - - def cache_key(type) - "#{type}:#{namespace}:#{project_id}" - end - - def expire(key) - backend.delete(cache_key(key)) - end - - def fetch(key, &block) - backend.fetch(cache_key(key), &block) - end - - def exist?(key) - backend.exist?(cache_key(key)) - end - - def read(key) - backend.read(cache_key(key)) - end -end |