gitlab-org--gitlab-foss/lib/gitlab/avatar_cache.rb

86 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