From b42f9a1e167f103a2cd118f678df685677da66d7 Mon Sep 17 00:00:00 2001
From: Marius Bobin <mbobin@gitlab.com>
Date: Tue, 2 Jul 2024 15:28:13 +0300
Subject: [PATCH] Add partitioned FK from ci_pipelines_config to ci_pipelines

Changelog: other
---
 ..._config_on_partition_id_and_pipeline_id.rb | 38 +++++++++++++++++++
 ...nes_config_partition_id_and_pipeline_id.rb | 17 +++++++++
 db/schema_migrations/20240702120756           |  1 +
 db/schema_migrations/20240702120757           |  1 +
 db/structure.sql                              |  3 ++
 spec/db/schema_spec.rb                        |  1 +
 ...ll_partition_id_ci_pipeline_config_spec.rb | 27 ++++++++++---
 7 files changed, 82 insertions(+), 6 deletions(-)
 create mode 100644 db/post_migrate/20240702120756_fk_to_ci_pipelines_from_ci_pipelines_config_on_partition_id_and_pipeline_id.rb
 create mode 100644 db/post_migrate/20240702120757_validate_async_fk_on_ci_pipelines_config_partition_id_and_pipeline_id.rb
 create mode 100644 db/schema_migrations/20240702120756
 create mode 100644 db/schema_migrations/20240702120757

diff --git a/db/post_migrate/20240702120756_fk_to_ci_pipelines_from_ci_pipelines_config_on_partition_id_and_pipeline_id.rb b/db/post_migrate/20240702120756_fk_to_ci_pipelines_from_ci_pipelines_config_on_partition_id_and_pipeline_id.rb
new file mode 100644
index 0000000000000..5afffbd3b63cc
--- /dev/null
+++ b/db/post_migrate/20240702120756_fk_to_ci_pipelines_from_ci_pipelines_config_on_partition_id_and_pipeline_id.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class FkToCiPipelinesFromCiPipelinesConfigOnPartitionIdAndPipelineId < Gitlab::Database::Migration[2.2]
+  milestone '17.2'
+  disable_ddl_transaction!
+
+  SOURCE_TABLE_NAME = :ci_pipelines_config
+  TARGET_TABLE_NAME = :ci_pipelines
+  COLUMN = :pipeline_id
+  TARGET_COLUMN = :id
+  FK_NAME = :fk_rails_906c9a2533_p
+  PARTITION_COLUMN = :partition_id
+
+  def up
+    add_concurrent_foreign_key(
+      SOURCE_TABLE_NAME,
+      TARGET_TABLE_NAME,
+      column: [PARTITION_COLUMN, COLUMN],
+      target_column: [PARTITION_COLUMN, TARGET_COLUMN],
+      validate: false,
+      reverse_lock_order: true,
+      on_update: :cascade,
+      on_delete: :cascade,
+      name: FK_NAME
+    )
+  end
+
+  def down
+    with_lock_retries do
+      remove_foreign_key_if_exists(
+        SOURCE_TABLE_NAME,
+        TARGET_TABLE_NAME,
+        name: FK_NAME,
+        reverse_lock_order: true
+      )
+    end
+  end
+end
diff --git a/db/post_migrate/20240702120757_validate_async_fk_on_ci_pipelines_config_partition_id_and_pipeline_id.rb b/db/post_migrate/20240702120757_validate_async_fk_on_ci_pipelines_config_partition_id_and_pipeline_id.rb
new file mode 100644
index 0000000000000..831074456e214
--- /dev/null
+++ b/db/post_migrate/20240702120757_validate_async_fk_on_ci_pipelines_config_partition_id_and_pipeline_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class ValidateAsyncFkOnCiPipelinesConfigPartitionIdAndPipelineId < Gitlab::Database::Migration[2.2]
+  milestone '17.2'
+
+  TABLE_NAME = :ci_pipelines_config
+  FK_NAME = :fk_rails_906c9a2533_p
+  COLUMNS = [:partition_id, :pipeline_id]
+
+  def up
+    prepare_async_foreign_key_validation(TABLE_NAME, COLUMNS, name: FK_NAME)
+  end
+
+  def down
+    unprepare_async_foreign_key_validation(TABLE_NAME, COLUMNS, name: FK_NAME)
+  end
+end
diff --git a/db/schema_migrations/20240702120756 b/db/schema_migrations/20240702120756
new file mode 100644
index 0000000000000..87ce14c40c0d0
--- /dev/null
+++ b/db/schema_migrations/20240702120756
@@ -0,0 +1 @@
+3b4f41807108f102864526cdb0275c3150e88e9071473c29c2873d82eceea5c2
\ No newline at end of file
diff --git a/db/schema_migrations/20240702120757 b/db/schema_migrations/20240702120757
new file mode 100644
index 0000000000000..e74852c1745bb
--- /dev/null
+++ b/db/schema_migrations/20240702120757
@@ -0,0 +1 @@
+4743abce6fbb7965a5befa24d1582fd5cadf7a73e3f757bb3d33975a97c8f5c3
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b86a0c4dbbc72..3144483628197 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -34224,6 +34224,9 @@ ALTER TABLE ONLY organization_details
 ALTER TABLE ONLY ci_pipelines_config
     ADD CONSTRAINT fk_rails_906c9a2533 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
 
+ALTER TABLE ONLY ci_pipelines_config
+    ADD CONSTRAINT fk_rails_906c9a2533_p FOREIGN KEY (partition_id, pipeline_id) REFERENCES ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID;
+
 ALTER TABLE ONLY approval_project_rules_groups
     ADD CONSTRAINT fk_rails_9071e863d1 FOREIGN KEY (approval_project_rule_id) REFERENCES approval_project_rules(id) ON DELETE CASCADE;
 
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index c708fe880da5a..7e44afe853bca 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -19,6 +19,7 @@
     users: [%w[accepted_term_id]],
     ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id], %w[partition_id upstream_pipeline_id], %w[auto_canceled_by_partition_id auto_canceled_by_id], %w[partition_id commit_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
     ci_pipeline_variables: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+    ci_pipelines_config: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
     ci_pipeline_metadata: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
     ci_pipeline_messages: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
     p_ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
diff --git a/spec/lib/gitlab/background_migration/backfill_partition_id_ci_pipeline_config_spec.rb b/spec/lib/gitlab/background_migration/backfill_partition_id_ci_pipeline_config_spec.rb
index fad3e277888ef..3e3e82d07b5a1 100644
--- a/spec/lib/gitlab/background_migration/backfill_partition_id_ci_pipeline_config_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_partition_id_ci_pipeline_config_spec.rb
@@ -8,7 +8,7 @@
   let(:ci_pipeline_config_table) { table(:ci_pipelines_config, database: :ci) }
   let!(:pipeline_1) { ci_pipelines_table.create!(id: 1, partition_id: 100) }
   let!(:pipeline_2) { ci_pipelines_table.create!(id: 2, partition_id: 101) }
-  let!(:pipeline_3) { ci_pipelines_table.create!(id: 3, partition_id: 101) }
+  let!(:pipeline_3) { ci_pipelines_table.create!(id: 3, partition_id: 100) }
   let!(:ci_pipeline_config_100) do
     ci_pipeline_config_table.create!(
       pipeline_id: pipeline_1.id,
@@ -41,18 +41,29 @@
       batch_column: :pipeline_id,
       sub_batch_size: 1,
       pause_ms: 0,
-      connection: Ci::ApplicationRecord.connection
+      connection: connection
     }
   end
 
   let!(:migration) { described_class.new(**migration_attrs) }
+  let(:connection) { Ci::ApplicationRecord.connection }
+
+  around do |example|
+    connection.transaction do
+      connection.execute(<<~SQL)
+        ALTER TABLE ci_pipelines DISABLE TRIGGER ALL;
+      SQL
+
+      example.run
+
+      connection.execute(<<~SQL)
+        ALTER TABLE ci_pipelines ENABLE TRIGGER ALL;
+      SQL
+    end
+  end
 
   describe '#perform' do
     context 'when second partition does not exist' do
-      before do
-        pipeline_3.update!(partition_id: 100)
-      end
-
       it 'does not execute the migration' do
         expect { migration.perform }
           .not_to change { invalid_ci_pipeline_config.reload.partition_id }
@@ -60,6 +71,10 @@
     end
 
     context 'when second partition exists' do
+      before do
+        pipeline_3.update!(partition_id: 101)
+      end
+
       it 'fixes invalid records in the wrong the partition' do
         expect { migration.perform }
           .to not_change { ci_pipeline_config_100.reload.partition_id }
-- 
GitLab