summaryrefslogtreecommitdiff
path: root/app/services/projects/container_repository/cache_tags_created_at_service.rb
blob: 3a5346d7a2302203f411a26ca8a12c72189817e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# frozen_string_literal: true

module Projects
  module ContainerRepository
    class CacheTagsCreatedAtService
      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

        ::Gitlab::Redis::Cache.with 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

        ::Gitlab::Redis::Cache.with do |redis|
          # we use a pipeline instead of a MSET because each tag has
          # a specific ttl
          redis.pipelined do
            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
              redis.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
    end
  end
end