Skip to content

Commit 3a20cfa

Browse files
authored
Prevent 500 on parallel service instance creation (#3899)
We observed 500 responses on POST /v3/service_instances when requests run in parallel and the db is small. This occurs if the one POST bypasses the other one and inserts the record into the db after the first POST has passed the validate function but before the actual insert. This leads to a PG::UniqueViolation: ERROR: duplicate key value violates unique constraint which is not catched and thus results in a 500 response. To prevent this, we add a Sequel::UniqueConstraintViolation rescue to catch the PG::UniqueViolation error and to return an adequate 422 error "The service instance name is taken.."
1 parent 61ca706 commit 3a20cfa

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

app/models/services/service_instance.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ def name_clashes
110110
end
111111
end
112112

113+
def around_save
114+
yield
115+
rescue Sequel::UniqueConstraintViolation => e
116+
raise e unless e.message.include?('si_space_id_name_index')
117+
118+
errors.add(:name, :unique)
119+
raise validation_failed_error
120+
end
121+
113122
def validate
114123
validates_presence :name
115124
validates_presence :space

spec/unit/actions/v3/service_instance_create_managed_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,23 @@ module V3
214214
end
215215
end
216216

217+
context 'parallel creation of managed service instances' do
218+
it 'ensures one creation is successful and the other fails due to name conflict' do
219+
# First request, should succeed
220+
expect do
221+
action.precursor(message:, service_plan:)
222+
end.not_to raise_error
223+
224+
# Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation
225+
allow_any_instance_of(ManagedServiceInstance).to receive(:validate).and_return(true)
226+
227+
# Second request, should fail with correct error message
228+
expect do
229+
action.precursor(message:, service_plan:)
230+
end.to raise_error(ServiceInstanceCreateManaged::InvalidManagedServiceInstance, 'The service instance name is taken: si-test-name.')
231+
end
232+
end
233+
217234
context 'quotas' do
218235
context 'when service instance limit has been reached for the space' do
219236
before do

0 commit comments

Comments
 (0)