Skip to content
Snippets Groups Projects
Commit 5de4e4ec authored by Leonardo da Rosa's avatar Leonardo da Rosa :one: Committed by Krasimir Angelov
Browse files

Convert merge_request_user_mentions.note_id to bigint for self-managed

For self-managed instances:
1. Ensure the `note_id` to `note_id_convert_to_bigint`
conversion is completed
2. Copy indexes and foreign keys
3. Swap columns

See #417402

Changelog: other
parent da392afc
No related branches found
No related tags found
1 merge request!129134Swap merge_request_user_mentions.note_id for self-managed instances
# frozen_string_literal: true
class EnsureMrUserMentionsNoteIdBigintBackfillIsFinishedForSelfManaged < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
def up
return if com_or_dev_or_test_but_not_jh?
ensure_batched_background_migration_is_finished(
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: 'merge_request_user_mentions',
column_name: 'id',
job_arguments: [['note_id'], ['note_id_convert_to_bigint']]
)
end
def down
# no-op
end
end
# frozen_string_literal: true
class SwapMergeRequestUserMentionsNoteIdToBigintForSelfManaged < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
disable_ddl_transaction!
TABLE_NAME = 'merge_request_user_mentions'
def up
return if com_or_dev_or_test_but_not_jh?
return if temp_column_removed?(TABLE_NAME, :note_id)
return if columns_swapped?(TABLE_NAME, :note_id)
swap
end
def down
return if com_or_dev_or_test_but_not_jh?
return if temp_column_removed?(TABLE_NAME, :note_id)
return unless columns_swapped?(TABLE_NAME, :note_id)
swap
end
def swap
# This will replace the existing index_merge_request_user_mentions_on_note_id
add_concurrent_index TABLE_NAME, :note_id_convert_to_bigint, unique: true,
name: 'index_merge_request_user_mentions_note_id_convert_to_bigint',
where: 'note_id_convert_to_bigint IS NOT NULL'
# This will replace the existing merge_request_user_mentions_on_mr_id_and_note_id_index
add_concurrent_index TABLE_NAME, [:merge_request_id, :note_id_convert_to_bigint], unique: true,
name: 'mr_user_mentions_on_mr_id_and_note_id_convert_to_bigint_index'
# This will replace the existing merge_request_user_mentions_on_mr_id_index
add_concurrent_index TABLE_NAME, :merge_request_id, unique: true,
name: 'merge_request_user_mentions_on_mr_id_index_convert_to_bigint',
where: 'note_id_convert_to_bigint IS NULL'
# This will replace the existing fk_rails_c440b9ea31
add_concurrent_foreign_key TABLE_NAME, :notes, column: :note_id_convert_to_bigint,
name: 'fk_merge_request_user_mentions_note_id_convert_to_bigint',
on_delete: :cascade
with_lock_retries(raise_on_exhaustion: true) do
execute "LOCK TABLE notes, #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id TO note_id_tmp"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id_convert_to_bigint TO note_id"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id_tmp TO note_id_convert_to_bigint"
function_name = Gitlab::Database::UnidirectionalCopyTrigger
.on_table(TABLE_NAME, connection: connection)
.name(:note_id, :note_id_convert_to_bigint)
execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
execute 'DROP INDEX IF EXISTS index_merge_request_user_mentions_on_note_id'
rename_index TABLE_NAME, 'index_merge_request_user_mentions_note_id_convert_to_bigint',
'index_merge_request_user_mentions_on_note_id'
execute 'DROP INDEX IF EXISTS merge_request_user_mentions_on_mr_id_and_note_id_index'
rename_index TABLE_NAME, 'mr_user_mentions_on_mr_id_and_note_id_convert_to_bigint_index',
'merge_request_user_mentions_on_mr_id_and_note_id_index'
execute 'DROP INDEX IF EXISTS merge_request_user_mentions_on_mr_id_index'
rename_index TABLE_NAME, 'merge_request_user_mentions_on_mr_id_index_convert_to_bigint',
'merge_request_user_mentions_on_mr_id_index'
execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT IF EXISTS fk_rails_c440b9ea31"
rename_constraint(TABLE_NAME, 'fk_merge_request_user_mentions_note_id_convert_to_bigint', 'fk_rails_c440b9ea31')
end
end
end
2cdfb4fa3c4798123c14995f5dfdb1b55e163d290a566e6c6de922e22c84316a
\ No newline at end of file
c0726a34eb21a269b85290b4aa56d05abb83d293ba7b8c9a97ea62ccba6e31c6
\ No newline at end of file
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe EnsureMrUserMentionsNoteIdBigintBackfillIsFinishedForSelfManaged, feature_category: :database do
describe '#up' do
let(:migration_arguments) do
{
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: 'merge_request_user_mentions',
column_name: 'id',
job_arguments: [['note_id'], ['note_id_convert_to_bigint']]
}
end
it 'ensures the migration is completed for self-managed instances' do
expect_next_instance_of(described_class) do |instance|
expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(false)
expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments)
end
migrate!
end
it 'skips the check for GitLab.com, dev, or test' do
expect_next_instance_of(described_class) do |instance|
expect(instance).to receive(:com_or_dev_or_test_but_not_jh?).and_return(true)
expect(instance).not_to receive(:ensure_batched_background_migration_is_finished)
end
migrate!
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe SwapMergeRequestUserMentionsNoteIdToBigintForSelfManaged, feature_category: :database do
let(:connection) { described_class.new.connection }
let(:merge_request_user_mentions) { table(:merge_request_user_mentions) }
shared_examples 'column `note_id_convert_to_bigint` is already dropped' do
before do
connection.execute('ALTER TABLE merge_request_user_mentions ALTER COLUMN note_id TYPE bigint')
connection.execute('ALTER TABLE merge_request_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
end
after do
connection.execute('ALTER TABLE merge_request_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
end
it 'does not swaps the columns' do
disable_migrations_output do
reversible_migration do |migration|
migration.before -> {
merge_request_user_mentions.reset_column_information
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
}
migration.after -> {
merge_request_user_mentions.reset_column_information
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
}
end
end
end
end
describe '#up' do
before do
# rubocop:disable RSpec/AnyInstanceOf
allow_any_instance_of(described_class).to(
receive(:com_or_dev_or_test_but_not_jh?).and_return(com_or_dev_or_test_but_not_jh?)
)
# rubocop:enable RSpec/AnyInstanceOf
end
context 'when GitLab.com, dev, or test' do
let(:com_or_dev_or_test_but_not_jh?) { true }
it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
end
context 'when self-managed instance with the `note_id_convert_to_bigint` column already dropped' do
let(:com_or_dev_or_test_but_not_jh?) { false }
it_behaves_like 'column `note_id_convert_to_bigint` is already dropped'
end
context 'when self-managed instance columns already swapped' do
let(:com_or_dev_or_test_but_not_jh?) { false }
before do
connection.execute('ALTER TABLE merge_request_user_mentions ALTER COLUMN note_id TYPE bigint')
connection.execute(
'ALTER TABLE merge_request_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer'
)
disable_migrations_output { migrate! }
end
after do
connection.execute('ALTER TABLE merge_request_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
end
it 'does not swaps the columns' do
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
eq('integer')
)
end
end
context 'when self-managed instance' do
let(:com_or_dev_or_test_but_not_jh?) { false }
before do
connection.execute('ALTER TABLE merge_request_user_mentions ALTER COLUMN note_id TYPE integer')
connection.execute(
'ALTER TABLE merge_request_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint'
)
connection.execute('ALTER TABLE merge_request_user_mentions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
connection.execute(
'DROP INDEX IF EXISTS index_merge_request_user_mentions_on_note_id_convert_to_bigint CASCADE'
)
connection.execute('CREATE OR REPLACE FUNCTION trigger_bfcbace4260d() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN NEW."note_id_convert_to_bigint" := NEW."note_id"; RETURN NEW; END; $$;')
end
after do
connection.execute('ALTER TABLE merge_request_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
end
it 'swaps the columns' do
disable_migrations_output do
reversible_migration do |migration|
migration.before -> {
merge_request_user_mentions.reset_column_information
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
expect(merge_request_user_mentions.columns.find do |c|
c.name == 'note_id_convert_to_bigint'
end.sql_type).to(
eq('bigint')
)
}
migration.after -> {
merge_request_user_mentions.reset_column_information
expect(merge_request_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(merge_request_user_mentions.columns.find do |c|
c.name == 'note_id_convert_to_bigint'
end.sql_type).to(
eq('integer')
)
}
end
end
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment