diff --git a/app/controllers/v3/service_credential_bindings_controller.rb b/app/controllers/v3/service_credential_bindings_controller.rb index 859b42e087a..0f64b72617f 100644 --- a/app/controllers/v3/service_credential_bindings_controller.rb +++ b/app/controllers/v3/service_credential_bindings_controller.rb @@ -66,7 +66,6 @@ def create when 'key' unauthorized! unless can_write_to_active_space?(service_instance.space) suspended! unless is_space_active?(service_instance.space) - create_key_binding(message, service_instance) end rescue V3::ServiceCredentialBindingAppCreate::UnprocessableCreate, @@ -182,10 +181,12 @@ def override_default_order_by(message) def create_key_binding(message, service_instance) action = V3::ServiceCredentialBindingKeyCreate.new(user_audit_info, message.audit_hash) - binding = action.precursor(service_instance, message:) + VCAP::CloudController::ServiceBinding.db.transaction do + binding = action.precursor(service_instance, message:) - pollable_job_guid = enqueue_bind_job(:key, binding.guid, message) - head :accepted, 'Location' => url_builder.build_url(path: "/v3/jobs/#{pollable_job_guid}") + pollable_job_guid = enqueue_bind_job(:key, binding.guid, message) + head :accepted, 'Location' => url_builder.build_url(path: "/v3/jobs/#{pollable_job_guid}") + end end def build_create_message(params) diff --git a/app/controllers/v3/service_instances_controller.rb b/app/controllers/v3/service_instances_controller.rb index bf54de7d644..b3efe12e62e 100644 --- a/app/controllers/v3/service_instances_controller.rb +++ b/app/controllers/v3/service_instances_controller.rb @@ -257,17 +257,20 @@ def create_managed(message, space:) unprocessable_service_plan! unless service_plan_valid?(service_plan, space) action = V3::ServiceInstanceCreateManaged.new(user_audit_info, message.audit_hash) - instance = action.precursor(message:, service_plan:) + VCAP::CloudController::ServiceInstance.db.transaction do + instance = action.precursor(message:, service_plan:) - provision_job = VCAP::CloudController::V3::CreateServiceInstanceJob.new( - instance.guid, - arbitrary_parameters: message.parameters, - user_audit_info: user_audit_info, - audit_hash: message.audit_hash - ) - pollable_job = Jobs::Enqueuer.new(provision_job, queue: Jobs::Queues.generic).enqueue_pollable + provision_job = VCAP::CloudController::V3::CreateServiceInstanceJob.new( + instance.guid, + arbitrary_parameters: message.parameters, + user_audit_info: user_audit_info, + audit_hash: message.audit_hash + ) + + pollable_job = Jobs::Enqueuer.new(provision_job, queue: Jobs::Queues.generic).enqueue_pollable - head :accepted, 'Location' => url_builder.build_url(path: "/v3/jobs/#{pollable_job.guid}") + head :accepted, 'Location' => url_builder.build_url(path: "/v3/jobs/#{pollable_job.guid}") + end rescue VCAP::CloudController::ServiceInstanceCreateMixin::UnprocessableOperation, V3::ServiceInstanceCreateManaged::InvalidManagedServiceInstance => e unprocessable!(e.message) diff --git a/spec/request/service_instances_spec.rb b/spec/request/service_instances_spec.rb index 6c8b185a97a..ab2f78dda5e 100644 --- a/spec/request/service_instances_spec.rb +++ b/spec/request/service_instances_spec.rb @@ -1174,6 +1174,23 @@ def check_filtered_instances(*instances) end end + describe 'when db is unavailable' do + before do + allow_any_instance_of(VCAP::CloudController::Jobs::Enqueuer).to receive(:enqueue_pollable).and_raise(Sequel::DatabaseDisconnectError) + end + + it 'rolls back the transaction' do + api_call.call(admin_headers) + + expect(last_response).to have_status_code(503) + expect(parsed_response['errors']).to include(include({ + 'detail' => include('Database connection failure'), + 'title' => 'CF-ServiceUnavailable', + 'code' => 10_015 + })) + end + end + describe 'service plan checks' do context 'does not exist' do let(:service_plan_guid) { 'does-not-exist' }