diff --git a/app/models/runtime/quota_definition.rb b/app/models/runtime/quota_definition.rb index 09a216b282..738570d1bc 100644 --- a/app/models/runtime/quota_definition.rb +++ b/app/models/runtime/quota_definition.rb @@ -19,6 +19,15 @@ class QuotaDefinition < Sequel::Model :app_instance_limit, :app_task_limit, :total_service_keys, :total_reserved_route_ports, :log_rate_limit + def around_save + yield + rescue Sequel::UniqueConstraintViolation => e + raise e unless e.message.include?('qd_name_index') + + errors.add(:name, :unique) + raise validation_failed_error + end + def validate validates_presence :name validates_unique :name diff --git a/spec/unit/actions/organization_quotas_create_spec.rb b/spec/unit/actions/organization_quotas_create_spec.rb index c95052f871..c6817e57c2 100644 --- a/spec/unit/actions/organization_quotas_create_spec.rb +++ b/spec/unit/actions/organization_quotas_create_spec.rb @@ -132,6 +132,26 @@ module VCAP::CloudController end end + context 'when creating organization quotas concurrently' do + let(:name) { 'awesome' } + let(:message) { VCAP::CloudController::OrganizationQuotasCreateMessage.new(name:) } + + it 'ensures one creation is successful and the other fails due to name conflict' do + # First request, should succeed + expect do + org_quotas_create.create(message) + end.not_to raise_error + + # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation + allow_any_instance_of(QuotaDefinition).to receive(:validate).and_return(true) + + # Second request, should fail with correct error + expect do + org_quotas_create.create(message) + end.to raise_error(OrganizationQuotasCreate::Error, "Organization Quota 'awesome' already exists.") + end + end + context 'when the org guid is invalid' do let(:invalid_org_guid) { 'invalid_org_guid' } let(:message_with_invalid_org_guid) do