# 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 class ProjectPipelineStatus attr_accessor :sha, :status, :ref, :project, :loaded delegate :commit, to: :project def self.load_for_project(project) new(project).tap do |status| status.load_status end end 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) result = Gitlab::Redis.with do |redis| 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 def self.update_for_pipeline(pipeline) pipeline_info = { sha: pipeline.sha, status: pipeline.status, ref: pipeline.ref } new(pipeline.project, pipeline_info: pipeline_info). store_in_cache_if_needed end def initialize(project, pipeline_info: {}, loaded_from_cache: nil) @project = project @sha = pipeline_info[:sha] @ref = pipeline_info[:ref] @status = pipeline_info[:status] @loaded = loaded_from_cache end def has_status? loaded? && sha.present? && status.present? end def load_status return if loaded? if has_cache? load_from_cache else load_from_project store_in_cache end self.loaded = true end def load_from_project return unless commit self.sha = commit.sha self.status = commit.status self.ref = project.default_branch 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 return unless sha return unless ref if commit.sha == sha && project.default_branch == ref store_in_cache end end def load_from_cache Gitlab::Redis.with do |redis| self.sha, self.status, self.ref = redis.hmget(cache_key, :sha, :status, :ref) end end def store_in_cache Gitlab::Redis.with do |redis| redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref }) end end def delete_from_cache Gitlab::Redis.with do |redis| redis.del(cache_key) end end def has_cache? return self.loaded unless self.loaded.nil? Gitlab::Redis.with do |redis| redis.exists(cache_key) end end def loaded? self.loaded end def cache_key self.class.cache_key_for_project(project) end end end end end