1# frozen_string_literal: true 2 3module Ci 4 class ResourceGroup < Ci::ApplicationRecord 5 belongs_to :project, inverse_of: :resource_groups 6 7 has_many :resources, class_name: 'Ci::Resource', inverse_of: :resource_group 8 has_many :processables, class_name: 'Ci::Processable', inverse_of: :resource_group 9 10 validates :key, 11 length: { maximum: 255 }, 12 format: { with: Gitlab::Regex.environment_name_regex, 13 message: Gitlab::Regex.environment_name_regex_message } 14 15 before_create :ensure_resource 16 17 enum process_mode: { 18 unordered: 0, 19 oldest_first: 1, 20 newest_first: 2 21 } 22 23 ## 24 # NOTE: This is concurrency-safe method that the subquery in the `UPDATE` 25 # works as explicit locking. 26 def assign_resource_to(processable) 27 resources.free.limit(1).update_all(build_id: processable.id) > 0 28 end 29 30 def release_resource_from(processable) 31 resources.retained_by(processable).update_all(build_id: nil) > 0 32 end 33 34 def upcoming_processables 35 if unordered? 36 processables.waiting_for_resource 37 elsif oldest_first? 38 processables.waiting_for_resource_or_upcoming 39 .order(Arel.sql("commit_id ASC, #{sort_by_job_status}")) 40 elsif newest_first? 41 processables.waiting_for_resource_or_upcoming 42 .order(Arel.sql("commit_id DESC, #{sort_by_job_status}")) 43 else 44 Ci::Processable.none 45 end 46 end 47 48 private 49 50 # In order to avoid deadlock, we do NOT specify the job execution order in the same pipeline. 51 # The system processes wherever ready to transition to `pending` status from `waiting_for_resource`. 52 # See https://gitlab.com/gitlab-org/gitlab/-/issues/202186 for more information. 53 def sort_by_job_status 54 <<~SQL 55 CASE status 56 WHEN 'waiting_for_resource' THEN 0 57 ELSE 1 58 END ASC 59 SQL 60 end 61 62 def ensure_resource 63 # Currently we only support one resource per group, which means 64 # maximum one build can be set to the resource group, thus builds 65 # belong to the same resource group are executed once at time. 66 self.resources.build if self.resources.empty? 67 end 68 end 69end 70