diff --git a/db/docs/jira_tracker_data.yml b/db/docs/jira_tracker_data.yml index 2dbf419e26f771a707d73b9bb752764d209e2337..4fd19fd7c6666e664d1352b4887c0011bce3141a 100644 --- a/db/docs/jira_tracker_data.yml +++ b/db/docs/jira_tracker_data.yml @@ -8,5 +8,8 @@ description: Data related to the Jira integration. introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1f332ae8da994509232c7601074b25514ad23c52 milestone: '12.0' gitlab_schema: gitlab_main_org -sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/558259 table_size: small +sharding_key: + project_id: projects + group_id: namespaces + organization_id: organizations diff --git a/db/post_migrate/20251020115917_add_jira_tracker_data_index_on_null_sharding_key.rb b/db/post_migrate/20251020115917_add_jira_tracker_data_index_on_null_sharding_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..1ef157169261583ef54fb33faf29b29d0570839f --- /dev/null +++ b/db/post_migrate/20251020115917_add_jira_tracker_data_index_on_null_sharding_key.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class AddJiraTrackerDataIndexOnNullShardingKey < Gitlab::Database::Migration[2.3] + milestone '18.6' + disable_ddl_transaction! + + INDEX_NAME = 'tmp_idx_jira_tracker_data_on_null_sharding_key' + + def up + add_concurrent_index( + :jira_tracker_data, + :id, + where: 'project_id IS NULL AND group_id IS NULL AND organization_id IS NULL', + name: INDEX_NAME + ) + end + + def down + remove_concurrent_index_by_name :jira_tracker_data, INDEX_NAME + end +end diff --git a/db/post_migrate/20251020115921_add_jira_tracker_data_index_on_id_group_id_organization_id.rb b/db/post_migrate/20251020115921_add_jira_tracker_data_index_on_id_group_id_organization_id.rb new file mode 100644 index 0000000000000000000000000000000000000000..3fdc9f4ac7a629d0517be5cb920206b67bbefc04 --- /dev/null +++ b/db/post_migrate/20251020115921_add_jira_tracker_data_index_on_id_group_id_organization_id.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class AddJiraTrackerDataIndexOnIdGroupIdOrganizationId < Gitlab::Database::Migration[2.3] + milestone '18.5' + disable_ddl_transaction! + + INDEX_NAME = 'tmp_idx_jira_tracker_data_on_id_group_id_organization_id' + + def up + add_concurrent_index( + :jira_tracker_data, + :id, + where: 'group_id IS NOT NULL AND organization_id IS NOT NULL', + name: INDEX_NAME + ) + end + + def down + remove_concurrent_index_by_name :jira_tracker_data, INDEX_NAME + end +end diff --git a/db/post_migrate/20251020115924_add_jira_tracker_data_index_on_id_project_id_organization_id.rb b/db/post_migrate/20251020115924_add_jira_tracker_data_index_on_id_project_id_organization_id.rb new file mode 100644 index 0000000000000000000000000000000000000000..62d39914f73175acd8ddad1c9269083e19ad350d --- /dev/null +++ b/db/post_migrate/20251020115924_add_jira_tracker_data_index_on_id_project_id_organization_id.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class AddJiraTrackerDataIndexOnIdProjectIdOrganizationId < Gitlab::Database::Migration[2.3] + milestone '18.6' + disable_ddl_transaction! + + INDEX_NAME = 'tmp_idx_jira_tracker_data_on_id_project_id_organization_id' + + def up + add_concurrent_index( + :jira_tracker_data, + :id, + where: 'project_id IS NOT NULL AND organization_id IS NOT NULL', + name: INDEX_NAME + ) + end + + def down + remove_concurrent_index_by_name :jira_tracker_data, INDEX_NAME + end +end diff --git a/db/post_migrate/20251020115927_add_jira_tracker_data_multiple_column_not_null_constraint.rb b/db/post_migrate/20251020115927_add_jira_tracker_data_multiple_column_not_null_constraint.rb new file mode 100644 index 0000000000000000000000000000000000000000..f9f4438749c3da87c478892070473cd33463e58f --- /dev/null +++ b/db/post_migrate/20251020115927_add_jira_tracker_data_multiple_column_not_null_constraint.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class AddJiraTrackerDataMultipleColumnNotNullConstraint < Gitlab::Database::Migration[2.3] + milestone '18.6' + disable_ddl_transaction! + + def up + add_multi_column_not_null_constraint( + :jira_tracker_data, + :project_id, + :group_id, + :organization_id, + validate: false + ) + end + + def down + remove_multi_column_not_null_constraint( + :jira_tracker_data, + :project_id, + :group_id, + :organization_id + ) + end +end diff --git a/db/post_migrate/20251020115929_backfill_jira_tracker_data_null_sharding_key.rb b/db/post_migrate/20251020115929_backfill_jira_tracker_data_null_sharding_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..5967c7b41edd8e672a7a10fc9bf68315e46a8ecf --- /dev/null +++ b/db/post_migrate/20251020115929_backfill_jira_tracker_data_null_sharding_key.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class BackfillJiraTrackerDataNullShardingKey < Gitlab::Database::Migration[2.3] + milestone '18.6' + restrict_gitlab_migration gitlab_schema: :gitlab_main_org + disable_ddl_transaction! + + BATCH_SIZE = 1000 + + def up + jira_tracker_data_model = define_batchable_model('jira_tracker_data') + integrations_model = define_batchable_model('integrations') + + jira_tracker_data_model + .where(group_id: nil, project_id: nil, organization_id: nil) + .each_batch(of: BATCH_SIZE) do |batch| + integration_ids = batch.pluck(:integration_id) + integrations = integrations_model.where(id: integration_ids).index_by(&:id) + + batch.each do |record| + integration = integrations[record.integration_id] + + record.update( + project_id: integration.project_id, + group_id: integration.group_id, + organization_id: integration.organization_id + ) + end + end + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20251020115932_backfill_group_jira_tracker_data_organization_id.rb b/db/post_migrate/20251020115932_backfill_group_jira_tracker_data_organization_id.rb new file mode 100644 index 0000000000000000000000000000000000000000..2c6a10d8e67290f7320eacfa112d38fe13e83def --- /dev/null +++ b/db/post_migrate/20251020115932_backfill_group_jira_tracker_data_organization_id.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class BackfillGroupJiraTrackerDataOrganizationId < Gitlab::Database::Migration[2.3] + milestone '18.6' + restrict_gitlab_migration gitlab_schema: :gitlab_main_org + disable_ddl_transaction! + + BATCH_SIZE = 1000 + + def up + jira_tracker_data = define_batchable_model('jira_tracker_data') + + jira_tracker_data + .where.not(group_id: nil) + .where.not(organization_id: nil) + .each_batch(of: BATCH_SIZE) do |batch| + batch.update_all(organization_id: nil) + end + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20251020115936_backfill_project_jira_tracker_data_organization_id.rb b/db/post_migrate/20251020115936_backfill_project_jira_tracker_data_organization_id.rb new file mode 100644 index 0000000000000000000000000000000000000000..1f377b587a25db7d2f75f6628521707dfa767652 --- /dev/null +++ b/db/post_migrate/20251020115936_backfill_project_jira_tracker_data_organization_id.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class BackfillProjectJiraTrackerDataOrganizationId < Gitlab::Database::Migration[2.3] + milestone '18.6' + restrict_gitlab_migration gitlab_schema: :gitlab_main_org + disable_ddl_transaction! + + BATCH_SIZE = 1000 + + def up + jira_tracker_data = define_batchable_model('jira_tracker_data') + + jira_tracker_data + .where.not(project_id: nil) + .where.not(organization_id: nil) + .each_batch(of: BATCH_SIZE) do |batch| + batch.update_all(organization_id: nil) + end + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20251020115938_validate_jira_tracker_data_multiple_column_not_null_constraint.rb b/db/post_migrate/20251020115938_validate_jira_tracker_data_multiple_column_not_null_constraint.rb new file mode 100644 index 0000000000000000000000000000000000000000..56daf2952ee75e7f021717bd1429e311059dd95a --- /dev/null +++ b/db/post_migrate/20251020115938_validate_jira_tracker_data_multiple_column_not_null_constraint.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class ValidateJiraTrackerDataMultipleColumnNotNullConstraint < Gitlab::Database::Migration[2.3] + milestone '18.6' + + CONSTRAINT_NAME = 'check_eca1fbd6bd' + + def up + validate_multi_column_not_null_constraint( + :jira_tracker_data, + :project_id, + :group_id, + :organization_id, + constraint_name: CONSTRAINT_NAME + ) + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20251020115941_remove_jira_tracker_data_index_on_id_group_id_organization_id.rb b/db/post_migrate/20251020115941_remove_jira_tracker_data_index_on_id_group_id_organization_id.rb new file mode 100644 index 0000000000000000000000000000000000000000..b902e8da0a3995d237fc0c429a0781ec3b110cd5 --- /dev/null +++ b/db/post_migrate/20251020115941_remove_jira_tracker_data_index_on_id_group_id_organization_id.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RemoveJiraTrackerDataIndexOnIdGroupIdOrganizationId < Gitlab::Database::Migration[2.3] + milestone '18.6' + disable_ddl_transaction! + + INDEX_NAME = 'tmp_idx_jira_tracker_data_on_id_group_id_organization_id' + + def up + remove_concurrent_index_by_name :jira_tracker_data, INDEX_NAME + end + + def down + add_concurrent_index( + :jira_tracker_data, + :id, + where: 'group_id IS NOT NULL AND organization_id IS NOT NULL', + name: INDEX_NAME + ) + end +end diff --git a/db/post_migrate/20251020115945_remove_jira_tracker_data_index_on_id_project_id_organization_id.rb b/db/post_migrate/20251020115945_remove_jira_tracker_data_index_on_id_project_id_organization_id.rb new file mode 100644 index 0000000000000000000000000000000000000000..d867710af6c23b34e1d108201b775ff2e8bd557c --- /dev/null +++ b/db/post_migrate/20251020115945_remove_jira_tracker_data_index_on_id_project_id_organization_id.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RemoveJiraTrackerDataIndexOnIdProjectIdOrganizationId < Gitlab::Database::Migration[2.3] + milestone '18.6' + disable_ddl_transaction! + + INDEX_NAME = 'tmp_idx_jira_tracker_data_on_id_project_id_organization_id' + + def up + remove_concurrent_index_by_name :jira_tracker_data, INDEX_NAME + end + + def down + add_concurrent_index( + :jira_tracker_data, + :id, + where: 'project_id IS NOT NULL AND organization_id IS NOT NULL', + name: INDEX_NAME + ) + end +end diff --git a/db/post_migrate/20251020115950_remove_jira_tracker_data_index_on_null_sharding_key.rb b/db/post_migrate/20251020115950_remove_jira_tracker_data_index_on_null_sharding_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..8be29fe98116fa7201630d1dda004a20db4c0e0c --- /dev/null +++ b/db/post_migrate/20251020115950_remove_jira_tracker_data_index_on_null_sharding_key.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RemoveJiraTrackerDataIndexOnNullShardingKey < Gitlab::Database::Migration[2.3] + milestone '18.6' + disable_ddl_transaction! + + INDEX_NAME = 'tmp_idx_jira_tracker_data_on_null_sharding_key' + + def up + remove_concurrent_index_by_name :jira_tracker_data, INDEX_NAME + end + + def down + add_concurrent_index( + :jira_tracker_data, + :id, + where: 'project_id IS NULL AND group_id IS NULL AND organization_id IS NULL', + name: INDEX_NAME + ) + end +end diff --git a/db/schema_migrations/20251020115917 b/db/schema_migrations/20251020115917 new file mode 100644 index 0000000000000000000000000000000000000000..d531cdb16f2d1747b2bdedd6d4480bc5714dd255 --- /dev/null +++ b/db/schema_migrations/20251020115917 @@ -0,0 +1 @@ +f9e06131f95dbd5241148e2f0970d49df5a578ec518f38a29dc4d5e38c151fb6 \ No newline at end of file diff --git a/db/schema_migrations/20251020115921 b/db/schema_migrations/20251020115921 new file mode 100644 index 0000000000000000000000000000000000000000..25d7cd5ff7d5fe6090d31e8d32f679edab25334a --- /dev/null +++ b/db/schema_migrations/20251020115921 @@ -0,0 +1 @@ +e744d503aa43a63969db0fb1bea5bc0958fe6b8a03355f9e2eb71e6e614cfb3c \ No newline at end of file diff --git a/db/schema_migrations/20251020115924 b/db/schema_migrations/20251020115924 new file mode 100644 index 0000000000000000000000000000000000000000..df523b33608bedd6f30153115fa0a4b44b8a7819 --- /dev/null +++ b/db/schema_migrations/20251020115924 @@ -0,0 +1 @@ +2febf44807f0eb5fe46ba670a47adae38ec559f1968e85025cee7f55dfe53dcc \ No newline at end of file diff --git a/db/schema_migrations/20251020115927 b/db/schema_migrations/20251020115927 new file mode 100644 index 0000000000000000000000000000000000000000..676d53c0f0226989e8227903ccd8aa352c15fdb8 --- /dev/null +++ b/db/schema_migrations/20251020115927 @@ -0,0 +1 @@ +9f9b01edf4d6681b86d987a79cb68222ead4a558a5c896927f55588ae19297c5 \ No newline at end of file diff --git a/db/schema_migrations/20251020115929 b/db/schema_migrations/20251020115929 new file mode 100644 index 0000000000000000000000000000000000000000..835928128ba754729d66561d825b563448431a31 --- /dev/null +++ b/db/schema_migrations/20251020115929 @@ -0,0 +1 @@ +fefd67fcc04d36b257eaf6b572d72fb5738bc811392ac437c5b821b3bbefc292 \ No newline at end of file diff --git a/db/schema_migrations/20251020115932 b/db/schema_migrations/20251020115932 new file mode 100644 index 0000000000000000000000000000000000000000..b0216860b9e33784fe759751f99e074b6ec8cc58 --- /dev/null +++ b/db/schema_migrations/20251020115932 @@ -0,0 +1 @@ +d5cd59d6326e83e4e9633e8cd8bf32ba8d273ce1040515ec8580c2cf6cd2d495 \ No newline at end of file diff --git a/db/schema_migrations/20251020115936 b/db/schema_migrations/20251020115936 new file mode 100644 index 0000000000000000000000000000000000000000..dc6307c1b6c12ddd94a1e9121f6777e6bb287dcf --- /dev/null +++ b/db/schema_migrations/20251020115936 @@ -0,0 +1 @@ +11efcd6b0cfdf71496f36e29f3756edf2b9db84c5c1114ac3fb9984bdb14fffa \ No newline at end of file diff --git a/db/schema_migrations/20251020115938 b/db/schema_migrations/20251020115938 new file mode 100644 index 0000000000000000000000000000000000000000..de29cbb488afd9276ebb44bffdea5553d2c7814e --- /dev/null +++ b/db/schema_migrations/20251020115938 @@ -0,0 +1 @@ +8ab8f2b5c1c8af9289083f875316cdbf0f7da9ebb34691746c49c0deeb587131 \ No newline at end of file diff --git a/db/schema_migrations/20251020115941 b/db/schema_migrations/20251020115941 new file mode 100644 index 0000000000000000000000000000000000000000..783b35621664f1f37612389da76ca5d52418b573 --- /dev/null +++ b/db/schema_migrations/20251020115941 @@ -0,0 +1 @@ +4088c0796d242c92134f5d8600d854d8f1936bad8d0172bd34dec4a83e04f497 \ No newline at end of file diff --git a/db/schema_migrations/20251020115945 b/db/schema_migrations/20251020115945 new file mode 100644 index 0000000000000000000000000000000000000000..540ed909cb55b4c1973f6d1a69c05499bd152400 --- /dev/null +++ b/db/schema_migrations/20251020115945 @@ -0,0 +1 @@ +691bd07fb6454760b61f88919f363c66be85609cbd8da7506e11929fe166f9c9 \ No newline at end of file diff --git a/db/schema_migrations/20251020115950 b/db/schema_migrations/20251020115950 new file mode 100644 index 0000000000000000000000000000000000000000..7ad26b8d616952b5253a843993fbe50ced209800 --- /dev/null +++ b/db/schema_migrations/20251020115950 @@ -0,0 +1 @@ +8739ad742d8f4417099c94bc5eafca0d0bf6d336d44be69cd60950fd7f5c6f4b \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index bb047bbf388c780cdbbf2949586383c801909eff..6badac2b8e3d3424182c6b3466442a68da83866b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -18877,7 +18877,8 @@ CREATE TABLE jira_tracker_data ( CONSTRAINT check_214cf6a48b CHECK ((char_length(project_key) <= 255)), CONSTRAINT check_4cc5bbc801 CHECK ((char_length(jira_issue_prefix) <= 255)), CONSTRAINT check_9863a0a5fd CHECK ((char_length(jira_issue_regex) <= 255)), - CONSTRAINT check_b5ab881f50 CHECK ((char_length(jira_allowed_statuses_string) <= 1024)) + CONSTRAINT check_b5ab881f50 CHECK ((char_length(jira_allowed_statuses_string) <= 1024)), + CONSTRAINT check_eca1fbd6bd CHECK ((num_nonnulls(group_id, organization_id, project_id) = 1)) ); CREATE SEQUENCE jira_tracker_data_id_seq diff --git a/spec/lib/gitlab/database/sharding_key_spec.rb b/spec/lib/gitlab/database/sharding_key_spec.rb index 7a6fab50a90157d7965463cd498870654bee108f..c0bb564f95751d6338dcc725f2fd5e033b214b1e 100644 --- a/spec/lib/gitlab/database/sharding_key_spec.rb +++ b/spec/lib/gitlab/database/sharding_key_spec.rb @@ -317,7 +317,6 @@ "ci_runner_taggings_group_type" => "https://gitlab.com/gitlab-org/gitlab/-/issues/549027", "ci_runner_taggings_project_type" => "https://gitlab.com/gitlab-org/gitlab/-/issues/549028", "customer_relations_contacts" => "https://gitlab.com/gitlab-org/gitlab/-/issues/549029", - "jira_tracker_data" => "https://gitlab.com/gitlab-org/gitlab/-/issues/549032", "abuse_reports" => "https://gitlab.com/gitlab-org/gitlab/-/issues/553435", "abuse_report_labels" => "https://gitlab.com/gitlab-org/gitlab/-/issues/553427", "abuse_report_events" => "https://gitlab.com/gitlab-org/gitlab/-/issues/553429", diff --git a/spec/migrations/backfill_group_jira_tracker_data_organization_id_spec.rb b/spec/migrations/backfill_group_jira_tracker_data_organization_id_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..e8607e8a0d8a048723841a7b36d65ab27ea1e0e9 --- /dev/null +++ b/spec/migrations/backfill_group_jira_tracker_data_organization_id_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe BackfillGroupJiraTrackerDataOrganizationId, feature_category: :integrations do + let(:organizations) { table(:organizations) } + let(:jira_tracker_data) { table(:jira_tracker_data) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:integrations) { table(:integrations) } + + let!(:organization) { organizations.create!(id: 1, name: 'Default', path: 'default') } + let!(:group) { namespaces.create!(name: 'bar', path: 'bar', type: 'Group', organization_id: 1) } + + let!(:project) do + projects.create!( + name: 'baz', + path: 'baz', + organization_id: 1, + namespace_id: group.id, + project_namespace_id: group.id + ) + end + + let!(:integration) do + integrations.create!(group_id: group.id, type_new: 'Integrations::Jira') + end + + let(:tracker_data_to_backfill) do + jira_tracker_data.create!(group_id: group.id, integration_id: integration.id, organization_id: organization.id) + end + + let(:another_tracker_data_to_backfill) do + jira_tracker_data.create!(group_id: group.id, integration_id: integration.id, organization_id: organization.id) + end + + let(:valid_tracker_data) do + jira_tracker_data.create!(group_id: group.id, integration_id: integration.id) + end + + let(:project_tracker_data) do + jira_tracker_data.create!(project_id: project.id, integration_id: integration.id) + end + + let(:organization_tracker_data) do + instance_integration = integrations.create!( + instance: true, + organization_id: organization.id, + type_new: 'Integrations::Jira' + ) + + jira_tracker_data.create!(organization_id: organization.id, integration_id: instance_integration.id) + end + + before do + ApplicationRecord.connection.execute('ALTER TABLE jira_tracker_data DROP CONSTRAINT IF EXISTS check_eca1fbd6bd;') + end + + after do + ApplicationRecord + .connection + .execute( + 'ALTER TABLE jira_tracker_data ADD CONSTRAINT check_eca1fbd6bd ' \ + 'CHECK ((num_nonnulls(group_id, organization_id, project_id) = 1)) NOT VALID;' + ) + end + + describe "#up" do + it 'sets organization_id to nil for group jira_tracker_data that have it' do + expect(tracker_data_to_backfill.group_id).to eq(group.id) + expect(tracker_data_to_backfill.organization_id).to eq(organization.id) + + expect(another_tracker_data_to_backfill.group_id).to eq(group.id) + expect(another_tracker_data_to_backfill.organization_id).to eq(organization.id) + + expect(valid_tracker_data.group_id).to eq(group.id) + expect(valid_tracker_data.organization_id).to be_nil + + expect(project_tracker_data.project_id).to eq(project.id) + expect(project_tracker_data.organization_id).to be_nil + + expect(organization_tracker_data.organization_id).to eq(organization.id) + expect(organization_tracker_data.project_id).to be_nil + expect(organization_tracker_data.group_id).to be_nil + + migrate! + + expect(tracker_data_to_backfill.reload.group_id).to eq(group.id) + expect(tracker_data_to_backfill.reload.organization_id).to be_nil + + expect(another_tracker_data_to_backfill.reload.group_id).to eq(group.id) + expect(another_tracker_data_to_backfill.reload.organization_id).to be_nil + + expect(valid_tracker_data.reload.group_id).to eq(group.id) + expect(valid_tracker_data.reload.organization_id).to be_nil + + expect(project_tracker_data.reload.project_id).to eq(project.id) + expect(project_tracker_data.reload.organization_id).to be_nil + + expect(organization_tracker_data.reload.organization_id).to eq(organization.id) + expect(organization_tracker_data.reload.project_id).to be_nil + expect(organization_tracker_data.reload.group_id).to be_nil + end + end +end diff --git a/spec/migrations/backfill_jira_tracker_data_null_sharding_key_spec.rb b/spec/migrations/backfill_jira_tracker_data_null_sharding_key_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..717cfddf2ca2265e56f0e1d4bee1cfccf43f7a92 --- /dev/null +++ b/spec/migrations/backfill_jira_tracker_data_null_sharding_key_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe BackfillJiraTrackerDataNullShardingKey, feature_category: :integrations do + let(:organizations) { table(:organizations) } + let(:jira_tracker_data) { table(:jira_tracker_data) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:integrations) { table(:integrations) } + + let!(:organization) { organizations.create!(id: 1, name: 'Default', path: 'default') } + let!(:group) { namespaces.create!(name: 'bar', path: 'bar', type: 'Group', organization_id: 1) } + let!(:another_group) { namespaces.create!(name: 'bar', path: 'bar', type: 'Group', organization_id: 1) } + + let!(:project) do + projects.create!( + name: 'baz', + path: 'baz', + organization_id: 1, + namespace_id: group.id, + project_namespace_id: group.id + ) + end + + let!(:group_integration) do + integrations.create!(group_id: group.id, type_new: 'Integrations::Jira') + end + + let!(:another_group_integration) do + integrations.create!(group_id: another_group.id, type_new: 'Integrations::Jira') + end + + let!(:project_integration) do + integrations.create!(project_id: project.id, type_new: 'Integrations::Jira') + end + + let!(:organization_integration) do + integrations.create!(organization_id: organization.id, type_new: 'Integrations::Jira', instance: true) + end + + let(:project_tracker_data_to_backfill) do + jira_tracker_data.create!(integration_id: project_integration.id) + end + + let(:group_tracker_data_to_backfill) do + jira_tracker_data.create!(integration_id: group_integration.id) + end + + let(:organization_tracker_data_to_backfill) do + jira_tracker_data.create!(integration_id: organization_integration.id) + end + + let(:valid_group_tracker_data) do + jira_tracker_data.create!(group_id: another_group.id, integration_id: another_group_integration.id) + end + + before do + ApplicationRecord.connection.execute('ALTER TABLE jira_tracker_data DROP CONSTRAINT IF EXISTS check_eca1fbd6bd;') + end + + after do + ApplicationRecord + .connection + .execute( + 'ALTER TABLE jira_tracker_data ADD CONSTRAINT check_eca1fbd6bd ' \ + 'CHECK ((num_nonnulls(group_id, organization_id, project_id) = 1));' + ) + end + + describe "#up" do + it 'sets sharding key for records that do not have it' do + expect(project_tracker_data_to_backfill.project_id).to be_nil + expect(project_tracker_data_to_backfill.group_id).to be_nil + expect(project_tracker_data_to_backfill.organization_id).to be_nil + + expect(group_tracker_data_to_backfill.project_id).to be_nil + expect(group_tracker_data_to_backfill.group_id).to be_nil + expect(group_tracker_data_to_backfill.organization_id).to be_nil + + expect(organization_tracker_data_to_backfill.project_id).to be_nil + expect(organization_tracker_data_to_backfill.group_id).to be_nil + expect(organization_tracker_data_to_backfill.organization_id).to be_nil + + expect(valid_group_tracker_data.project_id).to be_nil + expect(valid_group_tracker_data.group_id).to eq(another_group.id) + expect(valid_group_tracker_data.organization_id).to be_nil + + migrate! + + expect(project_tracker_data_to_backfill.reload.project_id).to eq(project.id) + expect(project_tracker_data_to_backfill.reload.group_id).to be_nil + expect(project_tracker_data_to_backfill.reload.organization_id).to be_nil + + expect(group_tracker_data_to_backfill.reload.project_id).to be_nil + expect(group_tracker_data_to_backfill.reload.group_id).to eq(group.id) + expect(group_tracker_data_to_backfill.reload.organization_id).to be_nil + + expect(organization_tracker_data_to_backfill.reload.project_id).to be_nil + expect(organization_tracker_data_to_backfill.reload.group_id).to be_nil + expect(organization_tracker_data_to_backfill.reload.organization_id).to eq(organization.id) + + expect(valid_group_tracker_data.reload.project_id).to be_nil + expect(valid_group_tracker_data.reload.group_id).to eq(another_group.id) + expect(valid_group_tracker_data.reload.organization_id).to be_nil + end + end +end diff --git a/spec/migrations/backfill_project_jira_tracker_data_organization_id_spec.rb b/spec/migrations/backfill_project_jira_tracker_data_organization_id_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..9b23fc3671c1003bdb22d444dd0d754c1bad6d85 --- /dev/null +++ b/spec/migrations/backfill_project_jira_tracker_data_organization_id_spec.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe BackfillProjectJiraTrackerDataOrganizationId, feature_category: :integrations do + let(:organizations) { table(:organizations) } + let(:jira_tracker_data) { table(:jira_tracker_data) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:integrations) { table(:integrations) } + + let!(:organization) { organizations.create!(id: 1, name: 'Default', path: 'default') } + let!(:group) { namespaces.create!(name: 'bar', path: 'bar', type: 'Group', organization_id: 1) } + + let!(:project) do + projects.create!( + name: 'baz', + path: 'baz', + organization_id: 1, + namespace_id: group.id, + project_namespace_id: group.id + ) + end + + let!(:integration) do + integrations.create!(project_id: project.id, type_new: 'Integrations::Jira') + end + + let(:tracker_data_to_backfill) do + jira_tracker_data.create!(project_id: project.id, integration_id: integration.id, organization_id: organization.id) + end + + let(:another_tracker_data_to_backfill) do + jira_tracker_data.create!(project_id: project.id, integration_id: integration.id, organization_id: organization.id) + end + + let(:valid_tracker_data) do + jira_tracker_data.create!(project_id: project.id, integration_id: integration.id) + end + + let(:group_tracker_data) do + group_integration = integrations.create!(group_id: group.id, type_new: 'Integrations::Jira') + jira_tracker_data.create!(group_id: group.id, integration_id: group_integration.id) + end + + let(:organization_tracker_data) do + instance_integration = integrations.create!( + instance: true, + organization_id: organization.id, + type_new: 'Integrations::Jira' + ) + + jira_tracker_data.create!(organization_id: organization.id, integration_id: instance_integration.id) + end + + before do + ApplicationRecord.connection.execute('ALTER TABLE jira_tracker_data DROP CONSTRAINT IF EXISTS check_eca1fbd6bd;') + end + + after do + ApplicationRecord + .connection + .execute( + 'ALTER TABLE jira_tracker_data ADD CONSTRAINT check_eca1fbd6bd ' \ + 'CHECK ((num_nonnulls(group_id, organization_id, project_id) = 1));' + ) + end + + describe "#up" do + it 'sets organization_id to nil for project jira_tracker_data that have it' do + expect(tracker_data_to_backfill.project_id).to eq(project.id) + expect(tracker_data_to_backfill.organization_id).to eq(organization.id) + + expect(another_tracker_data_to_backfill.project_id).to eq(project.id) + expect(another_tracker_data_to_backfill.organization_id).to eq(organization.id) + + expect(valid_tracker_data.project_id).to eq(project.id) + expect(valid_tracker_data.organization_id).to be_nil + + expect(group_tracker_data.group_id).to eq(group.id) + expect(group_tracker_data.organization_id).to be_nil + + expect(organization_tracker_data.organization_id).to eq(organization.id) + expect(organization_tracker_data.project_id).to be_nil + expect(organization_tracker_data.group_id).to be_nil + + migrate! + + expect(tracker_data_to_backfill.reload.project_id).to eq(project.id) + expect(tracker_data_to_backfill.reload.organization_id).to be_nil + + expect(another_tracker_data_to_backfill.reload.project_id).to eq(project.id) + expect(another_tracker_data_to_backfill.reload.organization_id).to be_nil + + expect(valid_tracker_data.reload.project_id).to eq(project.id) + expect(valid_tracker_data.reload.organization_id).to be_nil + + expect(group_tracker_data.reload.group_id).to eq(group.id) + expect(group_tracker_data.reload.organization_id).to be_nil + + expect(organization_tracker_data.reload.organization_id).to eq(organization.id) + expect(organization_tracker_data.reload.project_id).to be_nil + expect(organization_tracker_data.reload.group_id).to be_nil + end + end +end