diff --git a/db/docs/batched_background_migrations/populate_topics_slug_column.yml b/db/docs/batched_background_migrations/populate_topics_slug_column.yml new file mode 100644 index 0000000000000000000000000000000000000000..bdc927a5c7bc96f4ec9876bded671463daac82dc --- /dev/null +++ b/db/docs/batched_background_migrations/populate_topics_slug_column.yml @@ -0,0 +1,8 @@ +--- +migration_job_name: PopulateTopicsSlugColumn +description: Populates topics slug column from name column +feature_category: groups_and_projects +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141715 +milestone: '16.9' +queued_migration_version: 20240113071052 +finalize_after: '2024-02-13' diff --git a/db/post_migrate/20240113071052_queue_populate_topics_slug_column.rb b/db/post_migrate/20240113071052_queue_populate_topics_slug_column.rb new file mode 100644 index 0000000000000000000000000000000000000000..68d587c88e1fa14a7fa92721ab0fdb44a37fcad9 --- /dev/null +++ b/db/post_migrate/20240113071052_queue_populate_topics_slug_column.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class QueuePopulateTopicsSlugColumn < Gitlab::Database::Migration[2.2] + milestone '16.9' + + MIGRATION = "PopulateTopicsSlugColumn" + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 1000 + SUB_BATCH_SIZE = 100 + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :topics, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :topics, :id, []) + end +end diff --git a/db/schema_migrations/20240113071052 b/db/schema_migrations/20240113071052 new file mode 100644 index 0000000000000000000000000000000000000000..fcd2489e43ddf1a4f4de445d05258a9cb08578b3 --- /dev/null +++ b/db/schema_migrations/20240113071052 @@ -0,0 +1 @@ +34feec8741621a0228fb94d1737cb0f4f9aaba673f86f7fc0e03855713f9be7c \ No newline at end of file diff --git a/lib/gitlab/background_migration/populate_topics_slug_column.rb b/lib/gitlab/background_migration/populate_topics_slug_column.rb new file mode 100644 index 0000000000000000000000000000000000000000..f69237e07a550c09f75ab418c342da72165f7c8d --- /dev/null +++ b/lib/gitlab/background_migration/populate_topics_slug_column.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + class PopulateTopicsSlugColumn < BatchedMigrationJob + feature_category :groups_and_projects + scope_to ->(relation) { relation.where(slug: nil) } + operation_name :populate_topics_slug_column + + def perform + each_sub_batch do |sub_batch| + sub_batch.each { |topic| topic.update! slug: clean_name(topic) } + end + end + + private + + def clean_name(topic) + cleaned_name = ::Gitlab::Slug::Path.new(topic.name).generate + + Gitlab::Utils::Uniquify.new.string(cleaned_name) { |s| topic.class.find_by_slug(s) } + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/populate_topics_slug_column_spec.rb b/spec/lib/gitlab/background_migration/populate_topics_slug_column_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..3dfee7281ae9a1c146859ed23c76acbd382298cc --- /dev/null +++ b/spec/lib/gitlab/background_migration/populate_topics_slug_column_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::PopulateTopicsSlugColumn, feature_category: :groups_and_projects do + let(:migration) do + described_class.new( + start_id: topic1.id, + end_id: topic4.id, + batch_table: :topics, + batch_column: :id, + sub_batch_size: 100, + pause_ms: 2.minutes, + connection: ApplicationRecord.connection + ) + end + + let(:topics) { table(:topics) } + + let!(:topic1) { topics.create!(name: 'dog ðŸ¶') } + let!(:topic2) { topics.create!(name: 'some topic') } + let!(:topic3) { topics.create!(name: 'topic', slug: 'topic') } + let!(:topic4) { topics.create!(name: 'topicðŸ¶') } + + describe '#perform' do + subject(:perform_migration) { migration.perform } + + it 'populates topics slug column' do + expect { perform_migration }.to change { topic1.reload.slug }.from(nil) + .and change { topic2.reload.slug }.from(nil) + .and not_change { topic3.reload.slug } # already has slug + .and change { topic4.reload.slug }.from(nil) + end + end +end diff --git a/spec/migrations/20240113071052_queue_populate_topics_slug_column_spec.rb b/spec/migrations/20240113071052_queue_populate_topics_slug_column_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1fda927b5c9797d25c19b310881c939d93b5701e --- /dev/null +++ b/spec/migrations/20240113071052_queue_populate_topics_slug_column_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueuePopulateTopicsSlugColumn, feature_category: :groups_and_projects 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( + table_name: :topics, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + } + end + end +end