From 7e3ca8cae72615f527eea66138d72cb6ad359e9a Mon Sep 17 00:00:00 2001 From: David Kim Date: Fri, 25 Jul 2025 11:36:02 +1000 Subject: [PATCH 1/3] Add MergeabilityStatus table to store merge_status Changelog: added Update import/export yml for MergeabilityStatus table Attempt to fix db/structure.sql ordering issue Something --- app/models/merge_request.rb | 1 + .../merge_requests/mergeability_status.rb | 19 +++++++++ .../merge_requests_mergeability_statuses.yml | 16 ++++++++ ...ate_merge_request_mergeability_statuses.rb | 41 +++++++++++++++++++ ..._request_id_fk_to_mergeability_statuses.rb | 20 +++++++++ ..._project_id_fk_to_mergeability_statuses.rb | 23 +++++++++++ db/schema_migrations/20250724040708 | 1 + db/schema_migrations/20250725012052 | 1 + db/schema_migrations/20250725012122 | 1 + db/structure.sql | 21 ++++++++++ .../import_export/base/relation_factory.rb | 2 +- spec/lib/gitlab/import_export/all_models.yml | 4 ++ 12 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 app/models/merge_requests/mergeability_status.rb create mode 100644 db/docs/merge_requests_mergeability_statuses.yml create mode 100644 db/migrate/20250724040708_create_merge_request_mergeability_statuses.rb create mode 100644 db/migrate/20250725012052_add_merge_request_id_fk_to_mergeability_statuses.rb create mode 100644 db/migrate/20250725012122_add_project_id_fk_to_mergeability_statuses.rb create mode 100644 db/schema_migrations/20250724040708 create mode 100644 db/schema_migrations/20250725012052 create mode 100644 db/schema_migrations/20250725012122 diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c11d3543183507..dc23cc96bb4b30 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -83,6 +83,7 @@ class MergeRequest < ApplicationRecord has_one :merge_schedule, class_name: 'MergeRequests::MergeSchedule', inverse_of: :merge_request has_one :metrics, inverse_of: :merge_request, autosave: true + has_one :mergeability_status, class_name: 'MergeRequests::MergeabilityStatus', inverse_of: :merge_request belongs_to :latest_merge_request_diff, class_name: 'MergeRequestDiff' manual_inverse_association :latest_merge_request_diff, :merge_request diff --git a/app/models/merge_requests/mergeability_status.rb b/app/models/merge_requests/mergeability_status.rb new file mode 100644 index 00000000000000..31435cf3cfb0e1 --- /dev/null +++ b/app/models/merge_requests/mergeability_status.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module MergeRequests + class MergeabilityStatus < ApplicationRecord + include PartitionedTable + + self.table_name = 'merge_requests_mergeability_statuses' + self.primary_key = 'merge_request_id' + + belongs_to :merge_request, inverse_of: :mergeability_status + belongs_to :project + + partitioned_by :merge_request_id, strategy: :int_range, partition_size: 100_000_000 + + validates :project, presence: true + validates :merge_request, presence: true + validates :status, presence: true + end +end diff --git a/db/docs/merge_requests_mergeability_statuses.yml b/db/docs/merge_requests_mergeability_statuses.yml new file mode 100644 index 00000000000000..c1e5ddc02e3ad8 --- /dev/null +++ b/db/docs/merge_requests_mergeability_statuses.yml @@ -0,0 +1,16 @@ +--- +table_name: merge_requests_mergeability_statuses +classes: +- MergeRequests::MergeabilityStatus +feature_categories: +- code_review_workflow +description: | + Stores the mergeability status for merge requests. This table was extracted from + the merge_requests table to reduce WAL pressure caused by frequent merge_status updates + on the heavily indexed merge_requests table. +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/XXXXX +milestone: "18.3" +gitlab_schema: gitlab_main_cell +sharding_key: + project_id: projects +table_size: small diff --git a/db/migrate/20250724040708_create_merge_request_mergeability_statuses.rb b/db/migrate/20250724040708_create_merge_request_mergeability_statuses.rb new file mode 100644 index 00000000000000..9505bd5c5fd8bc --- /dev/null +++ b/db/migrate/20250724040708_create_merge_request_mergeability_statuses.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class CreateMergeRequestMergeabilityStatuses < Gitlab::Database::Migration[2.3] + include Gitlab::Database::PartitioningMigrationHelpers + + disable_ddl_transaction! + milestone '18.3' + + TABLE_NAME = :merge_requests_mergeability_statuses + SOURCE_TABLE_NAME = 'merge_requests' + PARTITION_SIZE = 100_000_000 + MIN_ID = 1 + + def up + create_table TABLE_NAME, id: false, options: 'PARTITION BY RANGE (merge_request_id)' do |t| + t.bigint :merge_request_id, null: false, primary_key: true, default: nil, index: false + t.bigint :project_id, null: false, index: true + t.datetime_with_timezone :prepared_at + t.timestamps_with_timezone null: false + t.column :status, :smallint, null: false, default: 0 + end + + create_partitions + end + + def down + drop_table TABLE_NAME + end + + private + + def create_partitions + max_id = Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do + Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection.with_suppressed do + define_batchable_model(SOURCE_TABLE_NAME, connection: connection).maximum(:id) || MIN_ID + end + end + + create_int_range_partitions(TABLE_NAME, PARTITION_SIZE, MIN_ID, max_id) + end +end diff --git a/db/migrate/20250725012052_add_merge_request_id_fk_to_mergeability_statuses.rb b/db/migrate/20250725012052_add_merge_request_id_fk_to_mergeability_statuses.rb new file mode 100644 index 00000000000000..81aa168d62d304 --- /dev/null +++ b/db/migrate/20250725012052_add_merge_request_id_fk_to_mergeability_statuses.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddMergeRequestIdFkToMergeabilityStatuses < Gitlab::Database::Migration[2.3] + include Gitlab::Database::PartitioningMigrationHelpers + + disable_ddl_transaction! + milestone '18.3' + + TABLE_NAME = :merge_requests_mergeability_statuses + + def up + add_concurrent_partitioned_foreign_key TABLE_NAME, :merge_requests, column: :merge_request_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key TABLE_NAME, column: :merge_request_id + end + end +end diff --git a/db/migrate/20250725012122_add_project_id_fk_to_mergeability_statuses.rb b/db/migrate/20250725012122_add_project_id_fk_to_mergeability_statuses.rb new file mode 100644 index 00000000000000..7bb8176eb1e441 --- /dev/null +++ b/db/migrate/20250725012122_add_project_id_fk_to_mergeability_statuses.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# See https://docs.gitlab.com/ee/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddProjectIdFkToMergeabilityStatuses < Gitlab::Database::Migration[2.3] + include Gitlab::Database::PartitioningMigrationHelpers + + disable_ddl_transaction! + milestone '18.3' + + TABLE_NAME = :merge_requests_mergeability_statuses + + def up + add_concurrent_partitioned_foreign_key TABLE_NAME, :projects, column: :project_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key TABLE_NAME, column: :project_id + end + end +end diff --git a/db/schema_migrations/20250724040708 b/db/schema_migrations/20250724040708 new file mode 100644 index 00000000000000..349c3bb82104fe --- /dev/null +++ b/db/schema_migrations/20250724040708 @@ -0,0 +1 @@ +c40be25c0076cf40fbe1f4e2cba5875ece213bdd71a81fbfa736e3c5d8ba0714 \ No newline at end of file diff --git a/db/schema_migrations/20250725012052 b/db/schema_migrations/20250725012052 new file mode 100644 index 00000000000000..72e798c39d99db --- /dev/null +++ b/db/schema_migrations/20250725012052 @@ -0,0 +1 @@ +65dbae7edee06dd36d8c7a69662699b057add0bbae6f21402f1bf958f0d8166b \ No newline at end of file diff --git a/db/schema_migrations/20250725012122 b/db/schema_migrations/20250725012122 new file mode 100644 index 00000000000000..455b2c0e126f28 --- /dev/null +++ b/db/schema_migrations/20250725012122 @@ -0,0 +1 @@ +ba0ba921477fbc434203c251e1bd8e646ad28290513eb5393e9c4e3cf7d3a636 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 550304725905a6..c235a07162f728 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -18011,6 +18011,16 @@ CREATE SEQUENCE merge_requests_id_seq ALTER SEQUENCE merge_requests_id_seq OWNED BY merge_requests.id; +CREATE TABLE merge_requests_mergeability_statuses ( + merge_request_id bigint NOT NULL, + project_id bigint NOT NULL, + prepared_at timestamp with time zone, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + status smallint DEFAULT 0 NOT NULL +) +PARTITION BY RANGE (merge_request_id); + CREATE TABLE merge_trains ( id bigint NOT NULL, merge_request_id bigint NOT NULL, @@ -31070,6 +31080,9 @@ ALTER TABLE ONLY merge_requests_closing_issues ALTER TABLE ONLY merge_requests_compliance_violations ADD CONSTRAINT merge_requests_compliance_violations_pkey PRIMARY KEY (id); +ALTER TABLE ONLY merge_requests_mergeability_statuses + ADD CONSTRAINT merge_requests_mergeability_statuses_pkey PRIMARY KEY (merge_request_id); + ALTER TABLE ONLY merge_requests ADD CONSTRAINT merge_requests_pkey PRIMARY KEY (id); @@ -36858,6 +36871,8 @@ CREATE UNIQUE INDEX index_merge_requests_compliance_violations_unique_columns ON CREATE INDEX index_merge_requests_for_latest_diffs_with_state_merged ON merge_requests USING btree (latest_merge_request_diff_id, target_project_id) WHERE (state_id = 3); +CREATE INDEX index_merge_requests_mergeability_statuses_on_project_id ON ONLY merge_requests_mergeability_statuses USING btree (project_id); + CREATE INDEX index_merge_requests_on_assignee_id ON merge_requests USING btree (assignee_id); CREATE INDEX index_merge_requests_on_author_id_and_created_at ON merge_requests USING btree (author_id, created_at); @@ -46268,6 +46283,9 @@ ALTER TABLE ONLY merge_request_diff_details ALTER TABLE ONLY packages_package_file_build_infos ADD CONSTRAINT fk_rails_871ca3ae21 FOREIGN KEY (package_file_id) REFERENCES packages_package_files(id) ON DELETE CASCADE; +ALTER TABLE merge_requests_mergeability_statuses + ADD CONSTRAINT fk_rails_871fe00a04 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; + ALTER TABLE ONLY boards_epic_boards ADD CONSTRAINT fk_rails_874c573878 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; @@ -47165,6 +47183,9 @@ ALTER TABLE ONLY banned_users ALTER TABLE ONLY targeted_message_dismissals ADD CONSTRAINT fk_rails_fa9d2df3f9 FOREIGN KEY (targeted_message_id) REFERENCES targeted_messages(id) ON DELETE CASCADE; +ALTER TABLE merge_requests_mergeability_statuses + ADD CONSTRAINT fk_rails_fae63232c6 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY operations_feature_flags_issues ADD CONSTRAINT fk_rails_fb4d2a7cb1 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE; diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb index 7c63576b1f4e22..6946e6947203d4 100644 --- a/lib/gitlab/import_export/base/relation_factory.rb +++ b/lib/gitlab/import_export/base/relation_factory.rb @@ -9,7 +9,7 @@ class RelationFactory IMPORTED_OBJECT_MAX_RETRIES = 5 - OVERRIDES = { user_contributions: :user, merge_schedule: 'MergeRequests::MergeSchedule' }.freeze + OVERRIDES = { user_contributions: :user, merge_schedule: 'MergeRequests::MergeSchedule', mergeability_status: 'MergeRequests::MergeabilityStatus' }.freeze EXISTING_OBJECT_RELATIONS = %i[].freeze # This represents all relations that have unique key on `project_id` or `group_id` diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index b25df89ce96f04..d34062dcb5a0f9 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -225,6 +225,7 @@ merge_requests: - todos - target_project - source_project +- mergeability_status - merge_user - merge_request_diffs - merge_request_diff @@ -289,6 +290,9 @@ merge_requests: - approval_metrics external_pull_requests: - project +mergeability_status: +- merge_request +- project merge_request_diff: - merge_request - merge_request_diff_commits -- GitLab From 3d6a49af4a1a965380463b6970b655b7730123f1 Mon Sep 17 00:00:00 2001 From: Krasimir Angelov Date: Thu, 31 Jul 2025 13:05:46 +1200 Subject: [PATCH 2/3] Move table in structure.sql --- db/structure.sql | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/db/structure.sql b/db/structure.sql index c235a07162f728..def8a83702c07f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -5060,6 +5060,16 @@ CREATE TABLE merge_request_commits_metadata ( ) PARTITION BY RANGE (project_id); +CREATE TABLE merge_requests_mergeability_statuses ( + merge_request_id bigint NOT NULL, + project_id bigint NOT NULL, + prepared_at timestamp with time zone, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + status smallint DEFAULT 0 NOT NULL +) +PARTITION BY RANGE (merge_request_id); + CREATE TABLE p_ai_active_context_code_enabled_namespaces ( id bigint NOT NULL, namespace_id bigint NOT NULL, @@ -18011,16 +18021,6 @@ CREATE SEQUENCE merge_requests_id_seq ALTER SEQUENCE merge_requests_id_seq OWNED BY merge_requests.id; -CREATE TABLE merge_requests_mergeability_statuses ( - merge_request_id bigint NOT NULL, - project_id bigint NOT NULL, - prepared_at timestamp with time zone, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - status smallint DEFAULT 0 NOT NULL -) -PARTITION BY RANGE (merge_request_id); - CREATE TABLE merge_trains ( id bigint NOT NULL, merge_request_id bigint NOT NULL, -- GitLab From bcb6a9b9fe57ac5a2aed73a1541c4447c4df113c Mon Sep 17 00:00:00 2001 From: Krasimir Angelov Date: Thu, 31 Jul 2025 13:25:59 +1200 Subject: [PATCH 3/3] Restore prev schem check --- scripts/migration_schema_validator.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/migration_schema_validator.rb b/scripts/migration_schema_validator.rb index b977e843e854cc..b7ad41f71baa7d 100644 --- a/scripts/migration_schema_validator.rb +++ b/scripts/migration_schema_validator.rb @@ -45,9 +45,18 @@ def validate! end # only check allowed to be skipped is validate_schema_on_rollback! + # validate_ignore_columns! + # validate_schema_on_migrate! + # validate_schema_version_files! + # + # if skip_validation? + # puts "\e[32m Label #{SKIP_VALIDATION_LABEL} is present, skipping schema validation\e[0m" + # return + # end + # + # validate_schema_on_rollback! + # validate_ignore_columns! should never be skipped, the ignore_column directive must always be present validate_ignore_columns! - validate_schema_on_migrate! - validate_schema_version_files! if skip_validation? puts "\e[32m Label #{SKIP_VALIDATION_LABEL} is present, skipping schema validation\e[0m" @@ -55,6 +64,8 @@ def validate! end validate_schema_on_rollback! + validate_schema_on_migrate! + validate_schema_version_files! end private -- GitLab