From 9e1df0b61efb7f5b9c92d1e39ae045bf86fe7fbf Mon Sep 17 00:00:00 2001 From: Furkan Ayhan <furkanayhn@gmail.com> Date: Wed, 1 Dec 2021 16:34:14 +0000 Subject: [PATCH] Create ci_namespace_mirrors and ci_project_mirrors tables --- app/models/ci/namespace_mirror.rb | 9 ++++ app/models/ci/project_mirror.rb | 9 ++++ ...11011140930_create_ci_namespace_mirrors.rb | 15 ++++++ ...0211011140931_create_ci_project_mirrors.rb | 12 +++++ db/schema_migrations/20211011140930 | 1 + db/schema_migrations/20211011140931 | 1 + db/structure.sql | 48 +++++++++++++++++++ .../database/gitlab_loose_foreign_keys.yml | 11 +++++ lib/gitlab/database/gitlab_schemas.yml | 2 + spec/models/namespace_spec.rb | 4 ++ spec/models/project_spec.rb | 6 +++ .../have_loose_foreign_key.rb | 10 ++-- 12 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 app/models/ci/namespace_mirror.rb create mode 100644 app/models/ci/project_mirror.rb create mode 100644 db/migrate/20211011140930_create_ci_namespace_mirrors.rb create mode 100644 db/migrate/20211011140931_create_ci_project_mirrors.rb create mode 100644 db/schema_migrations/20211011140930 create mode 100644 db/schema_migrations/20211011140931 diff --git a/app/models/ci/namespace_mirror.rb b/app/models/ci/namespace_mirror.rb new file mode 100644 index 0000000000000..a497d2cabe557 --- /dev/null +++ b/app/models/ci/namespace_mirror.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Ci + # This model represents a record in a shadow table of the main database's namespaces table. + # It allows us to navigate the namespace hierarchy on the ci database without resorting to a JOIN. + class NamespaceMirror < ApplicationRecord + # Will be filled by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75517 + end +end diff --git a/app/models/ci/project_mirror.rb b/app/models/ci/project_mirror.rb new file mode 100644 index 0000000000000..c6e3101fb3ab7 --- /dev/null +++ b/app/models/ci/project_mirror.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Ci + # This model represents a shadow table of the main database's projects table. + # It allows us to navigate the project and namespace hierarchy on the ci database. + class ProjectMirror < ApplicationRecord + # Will be filled by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75517 + end +end diff --git a/db/migrate/20211011140930_create_ci_namespace_mirrors.rb b/db/migrate/20211011140930_create_ci_namespace_mirrors.rb new file mode 100644 index 0000000000000..b9a708c5d7ba3 --- /dev/null +++ b/db/migrate/20211011140930_create_ci_namespace_mirrors.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateCiNamespaceMirrors < Gitlab::Database::Migration[1.0] + TABLE_NAME = :ci_namespace_mirrors + INDEX_NAME = "index_gin_#{TABLE_NAME}_on_traversal_ids" + + def change + create_table TABLE_NAME do |t| + t.integer :namespace_id, null: false, index: { unique: true } + t.integer :traversal_ids, array: true, default: [], null: false + + t.index :traversal_ids, name: INDEX_NAME, using: :gin + end + end +end diff --git a/db/migrate/20211011140931_create_ci_project_mirrors.rb b/db/migrate/20211011140931_create_ci_project_mirrors.rb new file mode 100644 index 0000000000000..2407b7e0b84e9 --- /dev/null +++ b/db/migrate/20211011140931_create_ci_project_mirrors.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateCiProjectMirrors < Gitlab::Database::Migration[1.0] + TABLE_NAME = :ci_project_mirrors + + def change + create_table TABLE_NAME do |t| + t.integer :project_id, null: false, index: { unique: true } + t.integer :namespace_id, null: false, index: true + end + end +end diff --git a/db/schema_migrations/20211011140930 b/db/schema_migrations/20211011140930 new file mode 100644 index 0000000000000..6347ee5d51d84 --- /dev/null +++ b/db/schema_migrations/20211011140930 @@ -0,0 +1 @@ +cdae819e8de3b5ad721014376bfd9af97a45e953e2d345daf62784f986a5eb31 \ No newline at end of file diff --git a/db/schema_migrations/20211011140931 b/db/schema_migrations/20211011140931 new file mode 100644 index 0000000000000..c959d97074e02 --- /dev/null +++ b/db/schema_migrations/20211011140931 @@ -0,0 +1 @@ +7e51eb4443fd74da9bef4d9c1c3cc40376c311abbc05ca7871f725fada79b48a \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index a932ea30de515..3cb591e3b52ba 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -11767,6 +11767,21 @@ CREATE SEQUENCE ci_minutes_additional_packs_id_seq ALTER SEQUENCE ci_minutes_additional_packs_id_seq OWNED BY ci_minutes_additional_packs.id; +CREATE TABLE ci_namespace_mirrors ( + id bigint NOT NULL, + namespace_id integer NOT NULL, + traversal_ids integer[] DEFAULT '{}'::integer[] NOT NULL +); + +CREATE SEQUENCE ci_namespace_mirrors_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE ci_namespace_mirrors_id_seq OWNED BY ci_namespace_mirrors.id; + CREATE TABLE ci_namespace_monthly_usages ( id bigint NOT NULL, namespace_id bigint NOT NULL, @@ -12015,6 +12030,21 @@ CREATE SEQUENCE ci_platform_metrics_id_seq ALTER SEQUENCE ci_platform_metrics_id_seq OWNED BY ci_platform_metrics.id; +CREATE TABLE ci_project_mirrors ( + id bigint NOT NULL, + project_id integer NOT NULL, + namespace_id integer NOT NULL +); + +CREATE SEQUENCE ci_project_mirrors_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE ci_project_mirrors_id_seq OWNED BY ci_project_mirrors.id; + CREATE TABLE ci_project_monthly_usages ( id bigint NOT NULL, project_id bigint NOT NULL, @@ -21264,6 +21294,8 @@ ALTER TABLE ONLY ci_job_variables ALTER COLUMN id SET DEFAULT nextval('ci_job_va ALTER TABLE ONLY ci_minutes_additional_packs ALTER COLUMN id SET DEFAULT nextval('ci_minutes_additional_packs_id_seq'::regclass); +ALTER TABLE ONLY ci_namespace_mirrors ALTER COLUMN id SET DEFAULT nextval('ci_namespace_mirrors_id_seq'::regclass); + ALTER TABLE ONLY ci_namespace_monthly_usages ALTER COLUMN id SET DEFAULT nextval('ci_namespace_monthly_usages_id_seq'::regclass); ALTER TABLE ONLY ci_pending_builds ALTER COLUMN id SET DEFAULT nextval('ci_pending_builds_id_seq'::regclass); @@ -21286,6 +21318,8 @@ ALTER TABLE ONLY ci_pipelines_config ALTER COLUMN pipeline_id SET DEFAULT nextva ALTER TABLE ONLY ci_platform_metrics ALTER COLUMN id SET DEFAULT nextval('ci_platform_metrics_id_seq'::regclass); +ALTER TABLE ONLY ci_project_mirrors ALTER COLUMN id SET DEFAULT nextval('ci_project_mirrors_id_seq'::regclass); + ALTER TABLE ONLY ci_project_monthly_usages ALTER COLUMN id SET DEFAULT nextval('ci_project_monthly_usages_id_seq'::regclass); ALTER TABLE ONLY ci_refs ALTER COLUMN id SET DEFAULT nextval('ci_refs_id_seq'::regclass); @@ -22727,6 +22761,9 @@ ALTER TABLE ONLY ci_job_variables ALTER TABLE ONLY ci_minutes_additional_packs ADD CONSTRAINT ci_minutes_additional_packs_pkey PRIMARY KEY (id); +ALTER TABLE ONLY ci_namespace_mirrors + ADD CONSTRAINT ci_namespace_mirrors_pkey PRIMARY KEY (id); + ALTER TABLE ONLY ci_namespace_monthly_usages ADD CONSTRAINT ci_namespace_monthly_usages_pkey PRIMARY KEY (id); @@ -22760,6 +22797,9 @@ ALTER TABLE ONLY ci_pipelines ALTER TABLE ONLY ci_platform_metrics ADD CONSTRAINT ci_platform_metrics_pkey PRIMARY KEY (id); +ALTER TABLE ONLY ci_project_mirrors + ADD CONSTRAINT ci_project_mirrors_pkey PRIMARY KEY (id); + ALTER TABLE ONLY ci_project_monthly_usages ADD CONSTRAINT ci_project_monthly_usages_pkey PRIMARY KEY (id); @@ -25337,6 +25377,8 @@ CREATE UNIQUE INDEX index_ci_job_variables_on_key_and_job_id ON ci_job_variables CREATE INDEX index_ci_minutes_additional_packs_on_namespace_id_purchase_xid ON ci_minutes_additional_packs USING btree (namespace_id, purchase_xid); +CREATE UNIQUE INDEX index_ci_namespace_mirrors_on_namespace_id ON ci_namespace_mirrors USING btree (namespace_id); + CREATE UNIQUE INDEX index_ci_namespace_monthly_usages_on_namespace_id_and_date ON ci_namespace_monthly_usages USING btree (namespace_id, date); CREATE INDEX index_ci_pending_builds_id_on_protected_partial ON ci_pending_builds USING btree (id) WHERE (protected = true); @@ -25425,6 +25467,10 @@ CREATE INDEX index_ci_pipelines_on_user_id_and_created_at_and_source ON ci_pipel CREATE INDEX index_ci_pipelines_on_user_id_and_id_and_cancelable_status ON ci_pipelines USING btree (user_id, id) WHERE ((status)::text = ANY (ARRAY[('running'::character varying)::text, ('waiting_for_resource'::character varying)::text, ('preparing'::character varying)::text, ('pending'::character varying)::text, ('created'::character varying)::text, ('scheduled'::character varying)::text])); +CREATE INDEX index_ci_project_mirrors_on_namespace_id ON ci_project_mirrors USING btree (namespace_id); + +CREATE UNIQUE INDEX index_ci_project_mirrors_on_project_id ON ci_project_mirrors USING btree (project_id); + CREATE UNIQUE INDEX index_ci_project_monthly_usages_on_project_id_and_date ON ci_project_monthly_usages USING btree (project_id, date); CREATE UNIQUE INDEX index_ci_refs_on_project_id_and_ref_path ON ci_refs USING btree (project_id, ref_path); @@ -26001,6 +26047,8 @@ CREATE INDEX index_geo_repository_updated_events_on_source ON geo_repository_upd CREATE INDEX index_geo_reset_checksum_events_on_project_id ON geo_reset_checksum_events USING btree (project_id); +CREATE INDEX index_gin_ci_namespace_mirrors_on_traversal_ids ON ci_namespace_mirrors USING gin (traversal_ids); + CREATE INDEX index_gin_ci_pending_builds_on_namespace_traversal_ids ON ci_pending_builds USING gin (namespace_traversal_ids); CREATE INDEX index_gitlab_subscription_histories_on_gitlab_subscription_id ON gitlab_subscription_histories USING btree (gitlab_subscription_id); diff --git a/lib/gitlab/database/gitlab_loose_foreign_keys.yml b/lib/gitlab/database/gitlab_loose_foreign_keys.yml index 430ac7854499c..907a10d0fa3c0 100644 --- a/lib/gitlab/database/gitlab_loose_foreign_keys.yml +++ b/lib/gitlab/database/gitlab_loose_foreign_keys.yml @@ -18,3 +18,14 @@ clusters_applications_runners: - table: ci_runners column: runner_id on_delete: async_nullify +ci_namespace_mirrors: + - table: namespaces + column: namespace_id + on_delete: async_delete +ci_project_mirrors: + - table: projects + column: project_id + on_delete: async_delete + - table: namespaces + column: namespace_id + on_delete: async_delete diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml index 5695f2f1c14ec..6733318bfafdc 100644 --- a/lib/gitlab/database/gitlab_schemas.yml +++ b/lib/gitlab/database/gitlab_schemas.yml @@ -86,6 +86,7 @@ ci_job_token_project_scope_links: :gitlab_ci ci_job_variables: :gitlab_ci ci_minutes_additional_packs: :gitlab_ci ci_namespace_monthly_usages: :gitlab_ci +ci_namespace_mirrors: :gitlab_ci ci_pending_builds: :gitlab_ci ci_pipeline_artifacts: :gitlab_ci ci_pipeline_chat_data: :gitlab_ci @@ -97,6 +98,7 @@ ci_pipelines: :gitlab_ci ci_pipeline_variables: :gitlab_ci ci_platform_metrics: :gitlab_ci ci_project_monthly_usages: :gitlab_ci +ci_project_mirrors: :gitlab_ci ci_refs: :gitlab_ci ci_resource_groups: :gitlab_ci ci_resources: :gitlab_ci diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 0baf75f890f7f..84b72c54f01fe 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -2056,4 +2056,8 @@ def project_rugged(project) it { is_expected.to be(true) } end end + + it_behaves_like 'it has loose foreign keys' do + let(:factory_name) { :group } + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index a612a30369d2d..20fec9c21a296 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -7453,6 +7453,12 @@ def has_external_wiki end end + it_behaves_like 'it has loose foreign keys' do + let(:factory_name) { :project } + end + + private + def finish_job(export_job) export_job.start export_job.finish diff --git a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb index 1f2b2937dc5f0..8c98254e134c8 100644 --- a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb +++ b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb @@ -4,6 +4,8 @@ let(:factory_name) { nil } let(:table_name) { described_class.table_name } let(:connection) { described_class.connection } + let(:fully_qualified_table_name) { "#{connection.current_schema}.#{table_name}" } + let(:deleted_records) { LooseForeignKeys::DeletedRecord.where(fully_qualified_table_name: fully_qualified_table_name) } it 'has at least one loose foreign key definition' do definitions = Gitlab::Database::LooseForeignKeys.definitions_by_table[table_name] @@ -29,7 +31,7 @@ # using delete to avoid cross-database modification errors when associations with dependent option are present model.delete - deleted_record = LooseForeignKeys::DeletedRecord.find_by(fully_qualified_table_name: "#{connection.current_schema}.#{table_name}", primary_key_value: model.id) + deleted_record = deleted_records.find_by(primary_key_value: model.id) expect(deleted_record).not_to be_nil end @@ -37,11 +39,11 @@ it 'cleans up record deletions' do model = create(factory_name) # rubocop: disable Rails/SaveBang - expect { model.delete }.to change { LooseForeignKeys::DeletedRecord.count }.by(1) + expect { model.delete }.to change { deleted_records.count }.by(1) LooseForeignKeys::ProcessDeletedRecordsService.new(connection: connection).execute - expect(LooseForeignKeys::DeletedRecord.status_pending.count).to be(0) - expect(LooseForeignKeys::DeletedRecord.status_processed.count).to be(1) + expect(deleted_records.status_pending.count).to be(0) + expect(deleted_records.status_processed.count).to be(1) end end -- GitLab