diff --git a/app/services/ci/cancel_pipeline_service.rb b/app/services/ci/cancel_pipeline_service.rb index 92eead3fdd14c46dfa576abc4406e4621b7af3be..68e893c8a6dfd4593216eb24846a854334a683a6 100644 --- a/app/services/ci/cancel_pipeline_service.rb +++ b/app/services/ci/cancel_pipeline_service.rb @@ -43,8 +43,7 @@ def force_execute end log_pipeline_being_canceled - - pipeline.update_column(:auto_canceled_by_id, @auto_canceled_by_pipeline.id) if @auto_canceled_by_pipeline + update_auto_canceled_pipeline_attributes if @safe_cancellation # Only build and bridge (trigger) jobs can be interruptible. @@ -61,7 +60,7 @@ def force_execute private - attr_reader :pipeline, :current_user + attr_reader :pipeline, :current_user, :auto_canceled_by_pipeline def log_pipeline_being_canceled Gitlab::AppJsonLogger.info( @@ -74,6 +73,15 @@ def log_pipeline_being_canceled ) end + def update_auto_canceled_pipeline_attributes + return unless auto_canceled_by_pipeline + + pipeline.update_columns( + auto_canceled_by_id: auto_canceled_by_pipeline.id, + auto_canceled_by_partition_id: auto_canceled_by_pipeline.partition_id + ) + end + def cascade_to_children? @cascade_to_children end diff --git a/db/docs/batched_background_migrations/queue_backfill_autocancel_partition_id_on_ci_pipelines.yml b/db/docs/batched_background_migrations/queue_backfill_autocancel_partition_id_on_ci_pipelines.yml new file mode 100644 index 0000000000000000000000000000000000000000..49e97b5d0263e39ce73d090f2a6222d8383b3317 --- /dev/null +++ b/db/docs/batched_background_migrations/queue_backfill_autocancel_partition_id_on_ci_pipelines.yml @@ -0,0 +1,10 @@ +--- +migration_job_name: QueueBackfillAutocancelPartitionIdOnCiPipelines +description: It backfills the autocancel_by_partition_id to prepare for composite primary key on ci_pipelines +feature_category: ci_scaling +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157619 +milestone: '17.2' +queued_migration_version: 20240627122810 +# Replace with the approximate date you think it's best to ensure the completion of this BBM. +finalize_after: '2024-07-22' +finalized_by: # version of the migration that finalized this BBM diff --git a/db/migrate/20240627121919_add_auto_canceled_by_partition_id_to_ci_pipelines.rb b/db/migrate/20240627121919_add_auto_canceled_by_partition_id_to_ci_pipelines.rb new file mode 100644 index 0000000000000000000000000000000000000000..768ed463cc45b974a39f09960dbc6aa6e1fe79d2 --- /dev/null +++ b/db/migrate/20240627121919_add_auto_canceled_by_partition_id_to_ci_pipelines.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddAutoCanceledByPartitionIdToCiPipelines < Gitlab::Database::Migration[2.2] + milestone '17.2' + + def change + add_column :ci_pipelines, :auto_canceled_by_partition_id, :bigint + end +end diff --git a/db/post_migrate/20240627122810_queue_queue_backfill_autocancel_partition_id_on_ci_pipelines.rb b/db/post_migrate/20240627122810_queue_queue_backfill_autocancel_partition_id_on_ci_pipelines.rb new file mode 100644 index 0000000000000000000000000000000000000000..9e707400eec97c4ab59771f73c6f18e779f4f594 --- /dev/null +++ b/db/post_migrate/20240627122810_queue_queue_backfill_autocancel_partition_id_on_ci_pipelines.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class QueueQueueBackfillAutocancelPartitionIdOnCiPipelines < Gitlab::Database::Migration[2.2] + milestone '17.2' + restrict_gitlab_migration gitlab_schema: :gitlab_ci + + MIGRATION = "QueueBackfillAutocancelPartitionIdOnCiPipelines" + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 5000 + SUB_BATCH_SIZE = 250 + GITLAB_OPTIMIZED_BATCH_SIZE = 75_000 + GITLAB_OPTIMIZED_SUB_BATCH_SIZE = 750 + + def up + queue_batched_background_migration( + MIGRATION, + :ci_pipelines, + :id, + job_interval: DELAY_INTERVAL, + **batch_sizes + ) + end + + def down + delete_batched_background_migration(MIGRATION, :ci_pipelines, :id, []) + end + + private + + def batch_sizes + if Gitlab.com_except_jh? + { + batch_size: GITLAB_OPTIMIZED_BATCH_SIZE, + sub_batch_size: GITLAB_OPTIMIZED_SUB_BATCH_SIZE + } + else + { + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + } + end + end +end diff --git a/db/schema_migrations/20240627121919 b/db/schema_migrations/20240627121919 new file mode 100644 index 0000000000000000000000000000000000000000..439705b35319260aa659a53f1629527d255ccac4 --- /dev/null +++ b/db/schema_migrations/20240627121919 @@ -0,0 +1 @@ +80de104335055d247a08dcf0094fdaf85b573cd5161e572429141dea7b353720 \ No newline at end of file diff --git a/db/schema_migrations/20240627122810 b/db/schema_migrations/20240627122810 new file mode 100644 index 0000000000000000000000000000000000000000..a74ae76915fe3ad34d59bbd535629cacc1225fd3 --- /dev/null +++ b/db/schema_migrations/20240627122810 @@ -0,0 +1 @@ +8970d19294a69126387be4c728025c55d50866f31173935e7b0f2db772fe13c1 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index a393f6b9209b173a75d4a76d9e38427bddb9f866..fd3da17e46406559d43ce6ffdf554fa90a8523cd 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -7997,6 +7997,7 @@ CREATE TABLE ci_pipelines ( partition_id bigint NOT NULL, id bigint NOT NULL, auto_canceled_by_id bigint, + auto_canceled_by_partition_id bigint, CONSTRAINT check_d7e99a025e CHECK ((lock_version IS NOT NULL)) ); diff --git a/lib/gitlab/background_migration/queue_backfill_autocancel_partition_id_on_ci_pipelines.rb b/lib/gitlab/background_migration/queue_backfill_autocancel_partition_id_on_ci_pipelines.rb new file mode 100644 index 0000000000000000000000000000000000000000..4c19f3767fcdb5a47b13690789bc42b4f66e88e7 --- /dev/null +++ b/lib/gitlab/background_migration/queue_backfill_autocancel_partition_id_on_ci_pipelines.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + class QueueBackfillAutocancelPartitionIdOnCiPipelines < BatchedMigrationJob + operation_name :update_all + feature_category :ci_scaling + + # rubocop:disable Layout/LineLength -- Improve readability + def perform + each_sub_batch do |sub_batch| + sub_batch + .where.not(auto_canceled_by_id: nil) + .where('ci_pipelines.auto_canceled_by_id = canceling_pipelines.id') + .update_all('auto_canceled_by_partition_id = canceling_pipelines.partition_id FROM ci_pipelines as canceling_pipelines') + end + end + # rubocop:enable Layout/LineLength + end + end +end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index bbc1b5184400d343dee42e888bef61b07dfb5b01..be831ee8c11f307379a9cfd1406aac0506f0b843 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -65,7 +65,7 @@ ci_pipeline_messages: %w[partition_id], ci_pipeline_metadata: %w[partition_id], ci_pipeline_variables: %w[partition_id], - ci_pipelines: %w[partition_id], + ci_pipelines: %w[partition_id auto_canceled_by_partition_id], ci_runner_projects: %w[runner_id], ci_sources_pipelines: %w[partition_id source_partition_id source_job_id], ci_sources_projects: %w[partition_id], diff --git a/spec/lib/gitlab/background_migration/queue_backfill_autocancel_partition_id_on_ci_pipelines_spec.rb b/spec/lib/gitlab/background_migration/queue_backfill_autocancel_partition_id_on_ci_pipelines_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..27581b315a78049962d109b12edeca727d81489e --- /dev/null +++ b/spec/lib/gitlab/background_migration/queue_backfill_autocancel_partition_id_on_ci_pipelines_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::QueueBackfillAutocancelPartitionIdOnCiPipelines, feature_category: :ci_scaling do + let(:ci_pipelines_table) { table(:ci_pipelines, database: :ci) } + + let!(:pipeline_1) { ci_pipelines_table.create!(partition_id: 100) } + let!(:pipeline_3) { ci_pipelines_table.create!(partition_id: 101) } + let!(:pipeline_2) { ci_pipelines_table.create!(partition_id: 100, auto_canceled_by_id: pipeline_3.id) } + let!(:pipeline_4) { ci_pipelines_table.create!(partition_id: 101, auto_canceled_by_id: pipeline_2.id) } + + let(:migration_attrs) do + { + start_id: ci_pipelines_table.minimum(:id), + end_id: ci_pipelines_table.maximum(:id), + batch_table: :ci_pipelines, + batch_column: :id, + sub_batch_size: 2, + pause_ms: 0, + connection: Ci::ApplicationRecord.connection + } + end + + let!(:migration) { described_class.new(**migration_attrs) } + + describe '#perform' do + it 'backfills auto_canceled_by_partition_id' do + expect { migration.perform } + .to not_change { pipeline_1.reload.auto_canceled_by_partition_id } + .and not_change { pipeline_3.reload.auto_canceled_by_partition_id } + .and change { pipeline_2.reload.auto_canceled_by_partition_id }.from(nil).to(101) + .and change { pipeline_4.reload.auto_canceled_by_partition_id }.from(nil).to(100) + end + end +end diff --git a/spec/migrations/20240627122810_queue_queue_backfill_autocancel_partition_id_on_ci_pipelines_spec.rb b/spec/migrations/20240627122810_queue_queue_backfill_autocancel_partition_id_on_ci_pipelines_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..0255cce06438192a66090e81dcf8da0bd2b5aa6f --- /dev/null +++ b/spec/migrations/20240627122810_queue_queue_backfill_autocancel_partition_id_on_ci_pipelines_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueQueueBackfillAutocancelPartitionIdOnCiPipelines, migration: :gitlab_ci, feature_category: :ci_scaling do + let!(:batched_migration) { described_class::MIGRATION } + + it 'schedules a new batched migration' do + reversible_migration do |migration| + migration.before -> { + expect(batched_migration).not_to have_scheduled_batched_migration + } + + migration.after -> { + expect(batched_migration).to have_scheduled_batched_migration( + gitlab_schema: :gitlab_ci, + table_name: :ci_pipelines, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + } + end + end + + context 'when executed on .com' do + before do + allow(Gitlab).to receive(:com_except_jh?).and_return(true) + end + + it 'schedules a new batched migration' do + reversible_migration do |migration| + migration.before -> { + expect(batched_migration).not_to have_scheduled_batched_migration + } + + migration.after -> { + expect(batched_migration).to have_scheduled_batched_migration( + gitlab_schema: :gitlab_ci, + table_name: :ci_pipelines, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::GITLAB_OPTIMIZED_BATCH_SIZE, + sub_batch_size: described_class::GITLAB_OPTIMIZED_SUB_BATCH_SIZE + ) + } + end + end + end +end diff --git a/spec/services/ci/cancel_pipeline_service_spec.rb b/spec/services/ci/cancel_pipeline_service_spec.rb index 6051485c4df8c88cab7182cc0bb054a56f5a36a3..d6312aa7d2fa8de0ce460aa2f3f8530ce16ac0d4 100644 --- a/spec/services/ci/cancel_pipeline_service_spec.rb +++ b/spec/services/ci/cancel_pipeline_service_spec.rb @@ -80,6 +80,7 @@ subject expect(pipeline.auto_canceled_by_id).to eq(auto_canceled_by_pipeline.id) + expect(pipeline.auto_canceled_by_partition_id).to eq(auto_canceled_by_pipeline.partition_id) expect(pipeline.all_jobs.canceled.pluck(:auto_canceled_by_id).uniq) .to eq([auto_canceled_by_pipeline.id])