139 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # 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"
 | |
|         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::Cache.with do |redis|
 | |
|             self.sha, self.status, self.ref = redis.hmget(cache_key, :sha, :status, :ref)
 | |
|           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
 |