summaryrefslogtreecommitdiff
path: root/lib/gitlab/container_repository/tags/cache.rb
blob: ff457fb9219faeace92cdc653004d25e7cd271fc (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
71
72
# 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

          ::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
end