diff --git a/db/migrations/20250318112800_add_user_id_index_to_roles_tables.rb b/db/migrations/20250318112800_add_user_id_index_to_roles_tables.rb new file mode 100644 index 00000000000..64318f2b737 --- /dev/null +++ b/db/migrations/20250318112800_add_user_id_index_to_roles_tables.rb @@ -0,0 +1,40 @@ +Sequel.migration do + roles_tables = %w[ + organizations_auditors + organizations_billing_managers + organizations_managers + organizations_users + spaces_auditors + spaces_developers + spaces_managers + spaces_supporters + ] + + # adding an index concurrently cannot be done within a transaction + no_transaction + + up do + roles_tables.each do |table| + # MySQL already has an index on user_id (foreign key constraint) + next unless database_type == :postgres + + table_sym = table.to_sym + index_sym = :"#{table}_user_id_index" + VCAP::Migration.with_concurrent_timeout(self) do + add_index table_sym, :user_id, name: index_sym, if_not_exists: true, concurrently: true + end + end + end + + down do + roles_tables.each do |table| + next unless database_type == :postgres + + table_sym = table.to_sym + index_sym = :"#{table}_user_id_index" + VCAP::Migration.with_concurrent_timeout(self) do + drop_index table_sym, :user_id, name: index_sym, if_exists: true, concurrently: true + end + end + end +end diff --git a/spec/migrations/20250318112800_add_user_id_index_to_roles_tables_spec.rb b/spec/migrations/20250318112800_add_user_id_index_to_roles_tables_spec.rb new file mode 100644 index 00000000000..5e66ac7a0aa --- /dev/null +++ b/spec/migrations/20250318112800_add_user_id_index_to_roles_tables_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' +require 'migrations/helpers/migration_shared_context' + +RSpec.shared_examples 'adding an index for table' do |table| + describe "#{table} table" do + let(:table_sym) { table.to_sym } + let(:index_sym) { :"#{table}_user_id_index" } + + before do + skip unless db.database_type == :postgres + end + + describe 'up migration' do + context 'index does not exist' do + it 'adds the index' do + expect(db.indexes(table_sym)).not_to include(index_sym) + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error + expect(db.indexes(table_sym)).to include(index_sym) + end + end + + context 'index already exists' do + before do + db.add_index table_sym, :user_id, name: index_sym + end + + it 'does not fail' do + expect(db.indexes(table_sym)).to include(index_sym) + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error + expect(db.indexes(table_sym)).to include(index_sym) + end + end + end + + describe 'down migration' do + before do + Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) + end + + context 'index exists' do + it 'removes the index' do + expect(db.indexes(table_sym)).to include(index_sym) + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error + expect(db.indexes(table_sym)).not_to include(index_sym) + end + end + + context 'index does not exist' do + before do + db.drop_index table_sym, :user_id, name: index_sym + end + + it 'does not fail' do + expect(db.indexes(table_sym)).not_to include(index_sym) + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error + expect(db.indexes(table_sym)).not_to include(index_sym) + end + end + end + end +end + +RSpec.describe 'migration to add an index for user_id on all roles tables', isolation: :truncation, type: :migration do + include_context 'migration' do + let(:migration_filename) { '20250318112800_add_user_id_index_to_roles_tables.rb' } + end + + include_examples 'adding an index for table', 'organizations_auditors' + include_examples 'adding an index for table', 'organizations_billing_managers' + include_examples 'adding an index for table', 'organizations_managers' + include_examples 'adding an index for table', 'organizations_users' + include_examples 'adding an index for table', 'spaces_auditors' + include_examples 'adding an index for table', 'spaces_developers' + include_examples 'adding an index for table', 'spaces_managers' + include_examples 'adding an index for table', 'spaces_supporters' +end