Skip to content
Snippets Groups Projects
Commit 96625787 authored by Leonardo da Rosa's avatar Leonardo da Rosa :one:
Browse files

Convert issue_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 ccdd0f49
No related branches found
No related tags found
No related merge requests found
# frozen_string_literal: true
class EnsureIssueUserMentionsBigintBackfillIsFinishedForSelfManaged < 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: 'issue_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 SwapIssueUserMentionsNoteIdToBigintForSelfManaged < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
disable_ddl_transaction!
TABLE_NAME = 'issue_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_issue_user_mentions_on_note_id
add_concurrent_index TABLE_NAME, :note_id_convert_to_bigint, unique: true,
name: 'index_issue_user_mentions_on_note_id_convert_to_bigint',
where: 'note_id_convert_to_bigint IS NOT NULL'
# This will replace the existing issue_user_mentions_on_issue_id_and_note_id_index
add_concurrent_index TABLE_NAME, [:issue_id, :note_id_convert_to_bigint], unique: true,
name: 'tmp_issue_user_mentions_on_issue_id_and_note_id_index'
# This will replace the existing issue_user_mentions_on_issue_id_index
add_concurrent_index TABLE_NAME, :issue_id, unique: true,
name: 'tmp_issue_user_mentions_on_issue_id_index',
where: 'note_id_convert_to_bigint IS NULL'
# This will replace the existing fk_rails_3861d9fefa
add_concurrent_foreign_key TABLE_NAME, :notes, column: :note_id_convert_to_bigint,
name: 'fk_issue_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_issue_user_mentions_on_note_id'
rename_index TABLE_NAME, 'index_issue_user_mentions_on_note_id_convert_to_bigint',
'index_issue_user_mentions_on_note_id'
execute 'DROP INDEX IF EXISTS issue_user_mentions_on_issue_id_and_note_id_index'
rename_index TABLE_NAME, 'tmp_issue_user_mentions_on_issue_id_and_note_id_index',
'issue_user_mentions_on_issue_id_and_note_id_index'
execute 'DROP INDEX IF EXISTS issue_user_mentions_on_issue_id_index'
rename_index TABLE_NAME, 'tmp_issue_user_mentions_on_issue_id_index',
'issue_user_mentions_on_issue_id_index'
execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT IF EXISTS fk_rails_3861d9fefa"
rename_constraint(TABLE_NAME, 'fk_issue_user_mentions_note_id_convert_to_bigint', 'fk_rails_3861d9fefa')
end
end
end
19058c7442d020d7db5f0a8ec386adc27ce157254a9e00b42b1fa49551c2e585
\ No newline at end of file
b9a09d4b6cdfe5ac7e380af43b9e9ee508f90aecdaf03dbfbc348a966a4332fc
\ No newline at end of file
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe EnsureIssueUserMentionsBigintBackfillIsFinishedForSelfManaged, feature_category: :database do
describe '#up' do
let(:migration_arguments) do
{
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: 'issue_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 SwapIssueUserMentionsNoteIdToBigintForSelfManaged, feature_category: :database do
let(:connection) { described_class.new.connection }
let(:issue_user_mentions) { table(:issue_user_mentions) }
shared_examples 'column `note_id_convert_to_bigint` is already dropped' do
before do
connection.execute('ALTER TABLE issue_user_mentions ALTER COLUMN note_id TYPE bigint')
connection.execute('ALTER TABLE issue_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 -> {
issue_user_mentions.reset_column_information
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }).to be_nil
}
migration.after -> {
issue_user_mentions.reset_column_information
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(issue_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 issue_user_mentions ALTER COLUMN note_id TYPE bigint')
connection.execute(
'ALTER TABLE issue_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer'
)
disable_migrations_output { migrate! }
end
after do
connection.execute('ALTER TABLE issue_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint')
end
it 'does not swaps the columns' do
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(issue_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 issue_user_mentions ALTER COLUMN note_id TYPE integer')
connection.execute('ALTER TABLE issue_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint bigint')
connection.execute('ALTER TABLE issue_user_mentions ALTER COLUMN note_id_convert_to_bigint TYPE bigint')
connection.execute('DROP INDEX IF EXISTS index_issue_user_mentions_on_note_id_convert_to_bigint CASCADE')
connection.execute('CREATE OR REPLACE FUNCTION trigger_c2051020aa8b() 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 issue_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 -> {
issue_user_mentions.reset_column_information
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('integer')
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.sql_type).to(
eq('bigint')
)
}
migration.after -> {
issue_user_mentions.reset_column_information
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id' }.sql_type).to eq('bigint')
expect(issue_user_mentions.columns.find { |c| c.name == 'note_id_convert_to_bigint' }.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