diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/ci/build.rb | 7 | ||||
-rw-r--r-- | app/models/ci/build_metadata.rb | 11 | ||||
-rw-r--r-- | app/models/ci/catalog/resource.rb | 5 | ||||
-rw-r--r-- | app/models/ci/catalog/resources/version.rb | 9 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 48 | ||||
-rw-r--r-- | app/models/ci/stage.rb | 44 | ||||
-rw-r--r-- | app/models/commit_status.rb | 7 | ||||
-rw-r--r-- | app/models/concerns/ci/metadatable.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/ci/partitionable.rb | 4 | ||||
-rw-r--r-- | app/models/release.rb | 7 |
10 files changed, 113 insertions, 31 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 0bb93a68470..284495a1d97 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -19,7 +19,12 @@ module Ci belongs_to :runner belongs_to :trigger_request belongs_to :erased_by, class_name: 'User' - belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, inverse_of: :builds + belongs_to :pipeline, + ->(build) { in_partition(build) }, + class_name: 'Ci::Pipeline', + foreign_key: :commit_id, + partition_foreign_key: :partition_id, + inverse_of: :builds RUNNER_FEATURES = { upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? }, diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb index 555565ff621..4194be7c0af 100644 --- a/app/models/ci/build_metadata.rb +++ b/app/models/ci/build_metadata.rb @@ -14,9 +14,14 @@ module Ci self.table_name = 'p_ci_builds_metadata' self.primary_key = 'id' + query_constraints :id, :partition_id partitionable scope: :build, partitioned: true - belongs_to :build, class_name: 'CommitStatus' + belongs_to :build, # rubocop: disable Rails/InverseOf -- this relation is not present on CommitStatus + ->(metadata) { in_partition(metadata) }, + partition_foreign_key: :partition_id, + class_name: 'CommitStatus' + belongs_to :project before_create :set_build_project @@ -42,6 +47,10 @@ module Ci job_timeout_source: 4 } + def self.use_partition_id_filter? + Ci::Pipeline.use_partition_id_filter? + end + def update_timeout_state timeout = timeout_with_highest_precedence diff --git a/app/models/ci/catalog/resource.rb b/app/models/ci/catalog/resource.rb index 3ead852e834..8bfef225e05 100644 --- a/app/models/ci/catalog/resource.rb +++ b/app/models/ci/catalog/resource.rb @@ -50,6 +50,11 @@ module Ci save! end + # Triggered in Ci::Catalog::Resources::Version and Release model callbacks. + def update_latest_released_at! + update!(latest_released_at: versions.latest&.released_at) + end + private # These columns are denormalized from the `projects` table. We first sync these diff --git a/app/models/ci/catalog/resources/version.rb b/app/models/ci/catalog/resources/version.rb index bd0ebc77a6d..ab4d5c52526 100644 --- a/app/models/ci/catalog/resources/version.rb +++ b/app/models/ci/catalog/resources/version.rb @@ -28,6 +28,9 @@ module Ci delegate :name, :description, :tag, :sha, :released_at, :author_id, to: :release + after_destroy :update_catalog_resource + after_save :update_catalog_resource + class << self # In the future, we should support semantic versioning. # See https://gitlab.com/gitlab-org/gitlab/-/issues/427286 @@ -110,6 +113,12 @@ module Ci end end end + + private + + def update_catalog_resource + catalog_resource.update_latest_released_at! + end end end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 54aa3d78cf3..faa79f8f49c 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -80,31 +80,31 @@ module Ci # Ci:Job models. With that epic, we aim to replace `statuses` with `jobs`. # # DEPRECATED: - has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline - has_many :processables, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline - has_many :latest_statuses_ordered_by_stage, -> { latest.order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline - has_many :latest_statuses, -> { latest }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline - has_many :statuses_order_id_desc, -> { order_id_desc }, class_name: 'CommitStatus', foreign_key: :commit_id, - inverse_of: :pipeline - has_many :bridges, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline - has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline - has_many :generic_commit_statuses, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'GenericCommitStatus' + has_many :statuses, ->(pipeline) { in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :processables, ->(pipeline) { in_partition(pipeline) }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :latest_statuses_ordered_by_stage, -> (pipeline) { latest.in_partition(pipeline).order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :latest_statuses, ->(pipeline) { latest.in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :statuses_order_id_desc, ->(pipeline) { in_partition(pipeline).order_id_desc }, class_name: 'CommitStatus', foreign_key: :commit_id, + inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :bridges, ->(pipeline) { in_partition(pipeline) }, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :builds, ->(pipeline) { in_partition(pipeline) }, foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :generic_commit_statuses, ->(pipeline) { in_partition(pipeline) }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'GenericCommitStatus', partition_foreign_key: :partition_id # # NEW: - has_many :all_jobs, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline - has_many :current_jobs, -> { latest }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline - has_many :all_processable_jobs, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline - has_many :current_processable_jobs, -> { latest }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline + has_many :all_jobs, ->(pipeline) { in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :current_jobs, ->(pipeline) { latest.in_partition(pipeline) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :all_processable_jobs, ->(pipeline) { in_partition(pipeline) }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id + has_many :current_processable_jobs, ->(pipeline) { latest.in_partition(pipeline) }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline, partition_foreign_key: :partition_id has_many :job_artifacts, through: :builds has_many :build_trace_chunks, class_name: 'Ci::BuildTraceChunk', through: :builds, source: :trace_chunks has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id, inverse_of: :pipeline # rubocop:disable Cop/ActiveRecordDependent has_many :variables, class_name: 'Ci::PipelineVariable' - has_many :latest_builds, -> { latest.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build' + has_many :latest_builds, ->(pipeline) { in_partition(pipeline).latest.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build' has_many :downloadable_artifacts, -> do not_expired.or(where_exists(Ci::Pipeline.artifacts_locked.where("#{Ci::Pipeline.quoted_table_name}.id = #{Ci::Build.quoted_table_name}.commit_id"))).downloadable.with_job end, through: :latest_builds, source: :job_artifacts - has_many :latest_successful_jobs, -> { latest.success.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Processable' + has_many :latest_successful_jobs, ->(pipeline) { in_partition(pipeline).latest.success.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Processable' has_many :messages, class_name: 'Ci::PipelineMessage', inverse_of: :pipeline @@ -113,14 +113,14 @@ module Ci has_many :merge_requests_as_head_pipeline, foreign_key: :head_pipeline_id, class_name: 'MergeRequest', inverse_of: :head_pipeline - has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline - has_many :failed_builds, -> { latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build', + has_many :pending_builds, ->(pipeline) { in_partition(pipeline).pending }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline + has_many :failed_builds, ->(pipeline) { in_partition(pipeline).latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline - has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline - has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus', + has_many :retryable_builds, ->(pipeline) { in_partition(pipeline).latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline + has_many :cancelable_statuses, ->(pipeline) { in_partition(pipeline).cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus', inverse_of: :pipeline - has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Processable', inverse_of: :pipeline - has_many :scheduled_actions, -> { latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline + has_many :manual_actions, ->(pipeline) { in_partition(pipeline).latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Processable', inverse_of: :pipeline + has_many :scheduled_actions, ->(pipeline) { in_partition(pipeline).latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: :auto_canceled_by_id, inverse_of: :auto_canceled_by @@ -605,6 +605,12 @@ module Ci ::Gitlab::Ci::PipelineObjectHierarchy.new(relation, options: options) end + def self.use_partition_id_filter? + ::Gitlab::SafeRequestStore.fetch(:ci_builds_partition_id_query_filter) do + ::Feature.enabled?(:ci_builds_partition_id_query_filter) + end + end + def uses_needs? processables.where(scheduling_type: :dag).any? end diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 3d2df9a45ef..e413ed8a668 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -21,19 +21,45 @@ module Ci belongs_to :project belongs_to :pipeline - has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id, inverse_of: :ci_stage - has_many :latest_statuses, -> { ordered.latest }, + has_many :statuses, + ->(stage) { in_partition(stage) }, class_name: 'CommitStatus', foreign_key: :stage_id, + partition_foreign_key: :partition_id, inverse_of: :ci_stage - has_many :retried_statuses, -> { ordered.retried }, + has_many :latest_statuses, + ->(stage) { in_partition(stage).ordered.latest }, class_name: 'CommitStatus', foreign_key: :stage_id, + partition_foreign_key: :partition_id, + inverse_of: :ci_stage + has_many :retried_statuses, + ->(stage) { in_partition(stage).ordered.retried }, + class_name: 'CommitStatus', + foreign_key: :stage_id, + partition_foreign_key: :partition_id, + inverse_of: :ci_stage + has_many :processables, + ->(stage) { in_partition(stage) }, + class_name: 'Ci::Processable', + foreign_key: :stage_id, + partition_foreign_key: :partition_id, + inverse_of: :ci_stage + has_many :builds, + ->(stage) { in_partition(stage) }, + foreign_key: :stage_id, + partition_foreign_key: :partition_id, + inverse_of: :ci_stage + has_many :bridges, + ->(stage) { in_partition(stage) }, + foreign_key: :stage_id, + partition_foreign_key: :partition_id, + inverse_of: :ci_stage + has_many :generic_commit_statuses, + ->(stage) { in_partition(stage) }, + foreign_key: :stage_id, + partition_foreign_key: :partition_id, inverse_of: :ci_stage - has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id, inverse_of: :ci_stage - has_many :builds, foreign_key: :stage_id, inverse_of: :ci_stage - has_many :bridges, foreign_key: :stage_id, inverse_of: :ci_stage - has_many :generic_commit_statuses, foreign_key: :stage_id, inverse_of: :ci_stage scope :ordered, -> { order(position: :asc) } scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) } @@ -107,6 +133,10 @@ module Ci end end + def self.use_partition_id_filter? + Ci::Pipeline.use_partition_id_filter? + end + def set_status(new_status) retry_optimistic_lock(self, name: 'ci_stage_set_status') do case new_status diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 9f77bd8ebe2..a8e2615b327 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -25,11 +25,12 @@ class CommitStatus < Ci::ApplicationRecord self.sequence_name = :ci_builds_id_seq self.primary_key = :id + query_constraints :id, :partition_id partitionable scope: :pipeline, partitioned: true belongs_to :user belongs_to :project - belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, inverse_of: :statuses + belongs_to :pipeline, ->(build) { in_partition(build) }, class_name: 'Ci::Pipeline', foreign_key: :commit_id, inverse_of: :statuses, partition_foreign_key: :partition_id belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline', inverse_of: :auto_canceled_jobs belongs_to :ci_stage, class_name: 'Ci::Stage', foreign_key: :stage_id @@ -233,6 +234,10 @@ class CommitStatus < Ci::ApplicationRecord false end + def self.use_partition_id_filter? + Ci::Pipeline.use_partition_id_filter? + end + def locking_enabled? will_save_change_to_status? end diff --git a/app/models/concerns/ci/metadatable.rb b/app/models/concerns/ci/metadatable.rb index b785e39523d..5dcae7d594d 100644 --- a/app/models/concerns/ci/metadatable.rb +++ b/app/models/concerns/ci/metadatable.rb @@ -10,8 +10,10 @@ module Ci included do has_one :metadata, + ->(build) { where(partition_id: build.partition_id) }, class_name: 'Ci::BuildMetadata', foreign_key: :build_id, + partition_foreign_key: :partition_id, inverse_of: :build, autosave: true diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb index aaf07bfee3a..447603c1635 100644 --- a/app/models/concerns/ci/partitionable.rb +++ b/app/models/concerns/ci/partitionable.rb @@ -61,6 +61,10 @@ module Ci before_validation :set_partition_id, on: :create validates :partition_id, presence: true + scope :in_partition, ->(id) do + where(partition_id: (id.respond_to?(:partition_id) ? id.partition_id : id)) + end + def set_partition_id return if partition_id_changed? && partition_id.present? return unless partition_scope_value diff --git a/app/models/release.rb b/app/models/release.rb index 6830f6e8480..f8a7973e451 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -25,6 +25,9 @@ class Release < ApplicationRecord accepts_nested_attributes_for :links, allow_destroy: true before_create :set_released_at + # TODO: Remove this callback after catalog_resource.released_at is denormalized. See https://gitlab.com/gitlab-org/gitlab/-/issues/430117. + after_update :update_catalog_resource, if: -> { project.catalog_resource && saved_change_to_released_at? } + after_destroy :update_catalog_resource, if: -> { project.catalog_resource } validates :project, :tag, presence: true validates :author_id, presence: true, on: :create @@ -168,6 +171,10 @@ class Release < ApplicationRecord order_created_desc end end + + def update_catalog_resource + project.catalog_resource.update_latest_released_at! + end end Release.prepend_mod_with('Release') |