gitlab-org--gitlab-foss/lib/gitlab/container_repository/tags/cache.rb

77 lines
2.4 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module ContainerRepository
module Tags
class Cache
def initialize(container_repository)
@container_repository = container_repository
@cached_tag_names = Set.new
end
def populate(tags)
return if tags.empty?
# This will load all tags in one Redis roundtrip
# the maximum number of tags is configurable and is set to 200 by default.
# https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/packages/container_registry/index.md#set-cleanup-limits-to-conserve-resources
keys = tags.map(&method(:cache_key))
cached_tags_count = 0
with_redis do |redis|
tags.zip(redis.mget(keys)).each do |tag, created_at|
next unless created_at
tag.created_at = DateTime.rfc3339(created_at)
@cached_tag_names << tag.name
cached_tags_count += 1
end
end
cached_tags_count
end
def insert(tags, max_ttl_in_seconds)
return unless max_ttl_in_seconds
return if tags.empty?
# tags with nil created_at are not cacheable
# tags already cached don't need to be cached again
cacheable_tags = tags.select do |tag|
tag.created_at.present? && !tag.name.in?(@cached_tag_names)
end
return if cacheable_tags.empty?
now = Time.zone.now
with_redis do |redis|
# we use a pipeline instead of a MSET because each tag has
# a specific ttl
redis.pipelined do |pipeline|
cacheable_tags.each do |tag|
created_at = tag.created_at
# ttl is the max_ttl_in_seconds reduced by the number
# of seconds that the tag has already existed
ttl = max_ttl_in_seconds - (now - created_at).seconds
ttl = ttl.to_i
pipeline.set(cache_key(tag), created_at.rfc3339, ex: ttl) if ttl > 0
end
end
end
end
private
def cache_key(tag)
"container_repository:{#{@container_repository.id}}:tag:#{tag.name}:created_at"
end
def with_redis(&block)
::Gitlab::Redis::Cache.with(&block) # rubocop:disable CodeReuse/ActiveRecord
end
end
end
end
end