Skip to content

Commit 05a7534

Browse files
committed
Remove service binding unique constraints to allow multiple bindings
1 parent c4a118b commit 05a7534

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
Sequel.migration do
2+
no_transaction # adding an index concurrently cannot be done within a transaction
3+
4+
up do
5+
alter_table(:service_bindings) do
6+
drop_constraint(:unique_service_binding_service_instance_guid_app_guid) if @db.indexes(:service_bindings).key?(:unique_service_binding_service_instance_guid_app_guid)
7+
drop_constraint(:unique_service_binding_app_guid_name) if @db.indexes(:service_bindings).key?(:unique_service_binding_app_guid_name)
8+
end
9+
10+
if database_type == :postgres
11+
VCAP::Migration.with_concurrent_timeout(self) do
12+
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
13+
add_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, concurrently: true, if_not_exists: true
14+
end
15+
elsif database_type == :mysql
16+
alter_table(:service_bindings) do
17+
# rubocop:disable Sequel/ConcurrentIndex
18+
unless @db.indexes(:service_bindings).key?(:service_bindings_app_guid_service_instance_guid_index)
19+
add_index %i[app_guid service_instance_guid],
20+
name: :service_bindings_app_guid_service_instance_guid_index
21+
end
22+
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)
23+
# rubocop:enable Sequel/ConcurrentIndex
24+
end
25+
end
26+
end
27+
28+
down do
29+
alter_table(:service_bindings) do
30+
if @db.indexes(:service_bindings)[:unique_service_binding_service_instance_guid_app_guid].blank?
31+
add_unique_constraint %i[service_instance_guid app_guid],
32+
name: :unique_service_binding_service_instance_guid_app_guid
33+
end
34+
end
35+
alter_table(:service_bindings) do
36+
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?
37+
end
38+
39+
if database_type == :postgres
40+
VCAP::Migration.with_concurrent_timeout(self) do
41+
drop_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, concurrently: true, if_exists: true
42+
drop_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, concurrently: true, if_exists: true
43+
end
44+
elsif database_type == :mysql
45+
alter_table(:service_bindings) do
46+
# rubocop:disable Sequel/ConcurrentIndex
47+
if @db.indexes(:service_bindings).key?(:service_bindings_app_guid_service_instance_guid_index)
48+
drop_index %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index
49+
end
50+
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)
51+
# rubocop:enable Sequel/ConcurrentIndex
52+
end
53+
end
54+
end
55+
end
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
require 'spec_helper'
2+
require 'migrations/helpers/migration_shared_context'
3+
4+
RSpec.describe 'migration to allow multiple service bindings', isolation: :truncation, type: :migration do
5+
include_context 'migration' do
6+
let(:migration_filename) { '20250731071027_allow_multiple_service_bindings.rb' }
7+
end
8+
9+
describe 'service_bindings table' do
10+
context 'up migration' do
11+
it 'is in the correct state before migration' do
12+
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_service_instance_guid_app_guid)
13+
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_app_guid_name)
14+
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_service_instance_guid_index)
15+
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_name_index)
16+
end
17+
18+
it 'migrates successfully' do
19+
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error
20+
expect(db.indexes(:service_bindings)).not_to include(:unique_service_binding_app_guid_name)
21+
expect(db.indexes(:service_bindings)).not_to include(:unique_service_binding_service_instance_guid_app_guid)
22+
expect(db.indexes(:service_bindings)).to include(:service_bindings_app_guid_service_instance_guid_index)
23+
expect(db.indexes(:service_bindings)).to include(:service_bindings_app_guid_name_index)
24+
end
25+
26+
it 'does not fail if indexes/constraints are already in desired state' do
27+
db.alter_table :service_bindings do
28+
drop_constraint :unique_service_binding_service_instance_guid_app_guid
29+
drop_constraint :unique_service_binding_app_guid_name
30+
end
31+
if db.database_type == :postgres
32+
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
33+
db.add_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_not_exists: true, concurrently: true
34+
else
35+
db.add_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, if_not_exists: true
36+
db.add_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_not_exists: true
37+
end
38+
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error
39+
end
40+
end
41+
42+
context 'down migration' do
43+
it 'rolls back successfully' do
44+
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error
45+
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_service_instance_guid_app_guid)
46+
expect(db.indexes(:service_bindings)).to include(:unique_service_binding_app_guid_name)
47+
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_service_instance_guid_index)
48+
expect(db.indexes(:service_bindings)).not_to include(:service_bindings_app_guid_name_index)
49+
end
50+
51+
it 'does not fail if indexes/constraints are already in desired state' do
52+
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error
53+
db.alter_table :service_bindings do
54+
add_unique_constraint %i[service_instance_guid app_guid], name: :unique_service_binding_service_instance_guid_app_guid
55+
add_unique_constraint %i[app_guid name], name: :unique_service_binding_app_guid_name
56+
end
57+
if db.database_type == :postgres
58+
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
59+
db.drop_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_exists: true, concurrently: true
60+
else
61+
db.drop_index :service_bindings, %i[app_guid service_instance_guid], name: :service_bindings_app_guid_service_instance_guid_index, if_exists: true
62+
db.drop_index :service_bindings, %i[app_guid name], name: :service_bindings_app_guid_name_index, if_exists: true
63+
end
64+
65+
expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error
66+
end
67+
end
68+
end
69+
end

0 commit comments

Comments
 (0)