87 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
module Gitlab
 | 
						|
  class AvatarCache
 | 
						|
    class << self
 | 
						|
      # Increment this if a breaking change requires
 | 
						|
      # immediate cache expiry of all avatar caches.
 | 
						|
      #
 | 
						|
      # @return [Integer]
 | 
						|
      VERSION = 1
 | 
						|
 | 
						|
      # @return [Symbol]
 | 
						|
      BASE_KEY = :avatar_cache
 | 
						|
 | 
						|
      # @return [ActiveSupport::Duration]
 | 
						|
      DEFAULT_EXPIRY = 7.days
 | 
						|
 | 
						|
      # Look up cached avatar data by email address.
 | 
						|
      # This accepts a block to provide the value to be
 | 
						|
      # cached in the event nothing is found.
 | 
						|
      #
 | 
						|
      # Multiple calls in the same request will be served from the
 | 
						|
      # request store.
 | 
						|
      #
 | 
						|
      # @param email [String]
 | 
						|
      # @param additional_keys [*Object] all must respond to `#to_s`
 | 
						|
      # @param expires_in [ActiveSupport::Duration, Integer]
 | 
						|
      # @yield [email, *additional_keys] yields the supplied params back to the block
 | 
						|
      # @return [String]
 | 
						|
      def by_email(email, *additional_keys, expires_in: DEFAULT_EXPIRY)
 | 
						|
        key = email_key(email)
 | 
						|
        subkey = additional_keys.join(":")
 | 
						|
 | 
						|
        Gitlab::SafeRequestStore.fetch([key, subkey]) do
 | 
						|
          with do |redis|
 | 
						|
            # Look for existing cache value
 | 
						|
            cached = redis.hget(key, subkey)
 | 
						|
 | 
						|
            # Return the cached entry if set
 | 
						|
            break cached unless cached.nil?
 | 
						|
 | 
						|
            # Otherwise, call the block to get the value
 | 
						|
            to_cache = yield(email, *additional_keys).to_s
 | 
						|
 | 
						|
            # Set it in the cache
 | 
						|
            redis.hset(key, subkey, to_cache)
 | 
						|
 | 
						|
            # Update the expiry time
 | 
						|
            redis.expire(key, expires_in)
 | 
						|
 | 
						|
            # Return this new value
 | 
						|
            break to_cache
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # Remove one or more emails from the cache
 | 
						|
      #
 | 
						|
      # @param emails [String] one or more emails to delete
 | 
						|
      # @return [Integer] the number of keys deleted
 | 
						|
      def delete_by_email(*emails)
 | 
						|
        return 0 if emails.empty?
 | 
						|
 | 
						|
        with do |redis|
 | 
						|
          keys = emails.map { |email| email_key(email) }
 | 
						|
 | 
						|
          Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
 | 
						|
            redis.unlink(*keys)
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      private
 | 
						|
 | 
						|
      # @param email [String]
 | 
						|
      # @return [String]
 | 
						|
      def email_key(email)
 | 
						|
        "#{BASE_KEY}:v#{VERSION}:#{email}"
 | 
						|
      end
 | 
						|
 | 
						|
      def with(&blk)
 | 
						|
        Gitlab::Redis::Cache.with(&blk) # rubocop:disable CodeReuse/ActiveRecord
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |