Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions db/migrations/20251015071027_allow_multiple_service_bindings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
Sequel.migration do
no_transaction # adding an index concurrently cannot be done within a transaction

up do
alter_table(:service_bindings) do
drop_constraint(:unique_service_binding_service_instance_guid_app_guid) if @db.indexes(:service_bindings).key?(:unique_service_binding_service_instance_guid_app_guid)
drop_constraint(:unique_service_binding_app_guid_name) if @db.indexes(:service_bindings).key?(:unique_service_binding_app_guid_name)
end

if database_type == :postgres
VCAP::Migration.with_concurrent_timeout(self) do
add_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, concurrently: true, if_not_exists: true
add_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, concurrently: true, if_not_exists: true
end
elsif database_type == :mysql
alter_table(:service_bindings) do
# rubocop:disable Sequel/ConcurrentIndex
unless @db.indexes(:service_bindings).key?(:service_bindings_app_guid_service_instance_guid_index)
add_index %i[app_guid service_instance_guid],
name: :service_bindings_app_guid_service_instance_guid_index
end
add_index %i[app_guid name], name: :service_bindings_app_guid_name_index unless @db.indexes(:service_bindings).key?(:service_bindings_app_guid_name_index)
# rubocop:enable Sequel/ConcurrentIndex
end
end
end

down do
alter_table(:service_bindings) do
if @db.indexes(:service_bindings)[:unique_service_binding_service_instance_guid_app_guid].blank?
add_unique_constraint %i[service_instance_guid app_guid],
name: :unique_service_binding_service_instance_guid_app_guid
end
end
alter_table(:service_bindings) do
add_unique_constraint %i[app_guid name], name: :unique_service_binding_app_guid_name if @db.indexes(:service_bindings)[:unique_service_binding_app_guid_name].blank?
end

if database_type == :postgres
VCAP::Migration.with_concurrent_timeout(self) do
drop_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, concurrently: true, if_exists: true
drop_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, concurrently: true, if_exists: true
end
elsif database_type == :mysql
alter_table(:service_bindings) do
# rubocop:disable Sequel/ConcurrentIndex
if @db.indexes(:service_bindings).key?(:service_bindings_app_guid_service_instance_guid_index)
drop_index %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index
end
drop_index %i[app_guid name], name: :service_bindings_app_guid_name_index if @db.indexes(:service_bindings).key?(:service_bindings_app_guid_name_index)
# rubocop:enable Sequel/ConcurrentIndex
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require 'spec_helper'
require 'migrations/helpers/migration_shared_context'

RSpec.describe 'migration to allow multiple service bindings', isolation: :truncation, type: :migration do
include_context 'migration' do
let(:migration_filename) { '20251015071027_allow_multiple_service_bindings.rb' }
end

describe 'service_bindings table' do
context 'up migration' do
it 'is in the correct state before migration' do
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_service_instance_guid_app_guid)
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_app_guid_name)
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_service_instance_guid_index)
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_name_index)
end

it 'migrates successfully' do
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error
expect(db.indexes(:service_bindings)).not_to include(:unique_service_binding_app_guid_name)
expect(db.indexes(:service_bindings)).not_to include(:unique_service_binding_service_instance_guid_app_guid)
expect(db.indexes(:service_bindings)).to include(:service_bindings_app_guid_service_instance_guid_index)
expect(db.indexes(:service_bindings)).to include(:service_bindings_app_guid_name_index)
end

it 'does not fail if indexes/constraints are already in desired state' do
db.alter_table :service_bindings do
drop_constraint :unique_service_binding_service_instance_guid_app_guid
drop_constraint :unique_service_binding_app_guid_name
end
if db.database_type == :postgres
db.add_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, if_not_exists: true, concurrently: true
db.add_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_not_exists: true, concurrently: true
else
db.add_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, if_not_exists: true
db.add_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_not_exists: true
end
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error
end
end

context 'down migration' do
it 'rolls back successfully' do
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_service_instance_guid_app_guid)
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_app_guid_name)
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_service_instance_guid_index)
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_name_index)
end

it 'does not fail if indexes/constraints are already in desired state' do
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error
db.alter_table :service_bindings do
add_unique_constraint %i[service_instance_guid app_guid], name: :unique_service_binding_service_instance_guid_app_guid
add_unique_constraint %i[app_guid name], name: :unique_service_binding_app_guid_name
end
if db.database_type == :postgres
db.drop_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, if_exists: true, concurrently: true
db.drop_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_exists: true, concurrently: true
else
db.drop_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, if_exists: true
db.drop_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_exists: true
end

expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error
end
end
end
end