140 lines
3.9 KiB
Ruby
140 lines
3.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# 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::Cache.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)
|
|
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:projects/#{project.id}/pipeline_status/#{project.commit&.sha}"
|
|
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, self.status, self.ref = commit.sha, commit.status, 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::Cache.with do |redis|
|
|
self.sha, self.status, self.ref = redis.hmget(cache_key, :sha, :status, :ref)
|
|
|
|
self.status = nil if self.status.empty?
|
|
end
|
|
end
|
|
|
|
def store_in_cache
|
|
Gitlab::Redis::Cache.with do |redis|
|
|
redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref })
|
|
end
|
|
end
|
|
|
|
def delete_from_cache
|
|
Gitlab::Redis::Cache.with do |redis|
|
|
redis.del(cache_key)
|
|
end
|
|
end
|
|
|
|
def has_cache?
|
|
return self.loaded unless self.loaded.nil?
|
|
|
|
Gitlab::Redis::Cache.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
|