BigW Consortium Gitlab

project_pipeline_status.rb 3.74 KB
Newer Older
1 2 3 4 5 6
# This class is not backed by a table in the main database.
# It loads the latest Pipeline for the HEAD of a repository, and caches that
# in Redis.
module Gitlab
  module Cache
    module Ci
7
      class ProjectPipelineStatus
8
        attr_accessor :sha, :status, :ref, :project, :loaded
9 10 11 12 13 14 15 16 17

        delegate :commit, to: :project

        def self.load_for_project(project)
          new(project).tap do |status|
            status.load_status
          end
        end

18 19 20 21 22 23 24 25
        def self.load_in_batch_for_projects(projects)
          cached_results_for_projects(projects).zip(projects).each do |result, project|
            project.pipeline_status = new(project, result)
            project.pipeline_status.load_status
          end
        end

        def self.cached_results_for_projects(projects)
26
          result = Gitlab::Redis::Cache.with do |redis|
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
            redis.multi do
              projects.each do |project|
                cache_key = cache_key_for_project(project)
                redis.exists(cache_key)
                redis.hmget(cache_key, :sha, :status, :ref)
              end
            end
          end

          result.each_slice(2).map do |(cache_key_exists, (sha, status, ref))|
            pipeline_info = { sha: sha, status: status, ref: ref }
            { loaded_from_cache: cache_key_exists, pipeline_info: pipeline_info }
          end
        end

        def self.cache_key_for_project(project)
          "projects/#{project.id}/pipeline_status"
        end

46
        def self.update_for_pipeline(pipeline)
47 48 49 50 51 52
          pipeline_info = {
            sha: pipeline.sha,
            status: pipeline.status,
            ref: pipeline.ref
          }

53 54
          new(pipeline.project, pipeline_info: pipeline_info)
            .store_in_cache_if_needed
55 56
        end

57
        def initialize(project, pipeline_info: {}, loaded_from_cache: nil)
58
          @project = project
59 60 61 62
          @sha = pipeline_info[:sha]
          @ref = pipeline_info[:ref]
          @status = pipeline_info[:status]
          @loaded = loaded_from_cache
63 64 65 66 67 68 69 70 71 72 73 74
        end

        def has_status?
          loaded? && sha.present? && status.present?
        end

        def load_status
          return if loaded?

          if has_cache?
            load_from_cache
          else
75
            load_from_project
76 77 78 79 80 81
            store_in_cache
          end

          self.loaded = true
        end

82
        def load_from_project
83 84 85 86
          return unless commit

          self.sha = commit.sha
          self.status = commit.status
87
          self.ref = project.default_branch
88 89 90 91 92 93
        end

        # We only cache the status for the HEAD commit of a project
        # This status is rendered in project lists
        def store_in_cache_if_needed
          return delete_from_cache unless commit
94 95 96 97 98 99
          return unless sha
          return unless ref

          if commit.sha == sha && project.default_branch == ref
            store_in_cache
          end
100 101 102
        end

        def load_from_cache
103
          Gitlab::Redis::Cache.with do |redis|
104
            self.sha, self.status, self.ref = redis.hmget(cache_key, :sha, :status, :ref)
105 106 107 108
          end
        end

        def store_in_cache
109
          Gitlab::Redis::Cache.with do |redis|
110
            redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref })
111 112 113 114
          end
        end

        def delete_from_cache
115
          Gitlab::Redis::Cache.with do |redis|
116 117 118 119 120
            redis.del(cache_key)
          end
        end

        def has_cache?
121 122
          return self.loaded unless self.loaded.nil?

123
          Gitlab::Redis::Cache.with do |redis|
124 125 126 127 128 129 130 131 132
            redis.exists(cache_key)
          end
        end

        def loaded?
          self.loaded
        end

        def cache_key
133
          self.class.cache_key_for_project(project)
134 135 136 137 138
        end
      end
    end
  end
end