Skip to content

Commit 6c75ebd

Browse files
committed
fix: handle race condition causing unique constraint violation when creating pacticipant versions
@mefellows
1 parent c0538f1 commit 6c75ebd

File tree

3 files changed

+30
-11
lines changed

3 files changed

+30
-11
lines changed

lib/pact_broker/versions/repository.rb

+12-3
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,19 @@ def find_by_pacticipant_name_and_number pacticipant_name, number
5151
.single_record
5252
end
5353

54+
# There may be a race condition if two simultaneous requests come in to create the same version
5455
def create args
55-
PactBroker.logger.info "Creating version #{args[:number]} for pacticipant_id=#{args[:pacticipant_id]}"
56-
version = PactBroker::Domain::Version.new(number: args[:number], pacticipant_id: args[:pacticipant_id]).save
57-
PactBroker::Domain::Version.find(id: version.id) # Need to reload with populated order
56+
PactBroker.logger.info "Upserting version #{args[:number]} for pacticipant_id=#{args[:pacticipant_id]}"
57+
version_params = {
58+
number: args[:number],
59+
pacticipant_id: args[:pacticipant_id],
60+
created_at: Sequel.datetime_class.now,
61+
updated_at: Sequel.datetime_class.now
62+
}
63+
id = PactBroker::Domain::Version.db[:versions].insert_ignore.insert(version_params)
64+
version = PactBroker::Domain::Version.find(number: args[:number], pacticipant_id: args[:pacticipant_id])
65+
PactBroker::Domain::OrderVersions.(version)
66+
version.refresh # reload with the populated order
5867
end
5968

6069
def find_by_pacticipant_id_and_number_or_create pacticipant_id, number

spec/lib/pact_broker/versions/repository_spec.rb

+13-5
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,25 @@ module Versions
3434

3535
describe "#create" do
3636
context "when a previous version exists" do
37-
38-
let!(:existing_order) do
39-
TestDataBuilder.new.create_version_with_hierarchy pacticipant_name, version_number
37+
let!(:existing_version) do
38+
TestDataBuilder.new.create_version_with_hierarchy(pacticipant_name, version_number).and_return(:version)
4039
end
4140

42-
subject { Repository.new.create pacticipant_id: existing_order.pacticipant_id, number: "1.2.4" }
41+
subject { Repository.new.create pacticipant_id: existing_version.pacticipant_id, number: "1.2.4" }
4342

4443
it "sets the order to the previous version's order plus one" do
45-
expect(subject.order).to eq existing_order.order + 1
44+
expect(subject.order).to eq existing_version.order + 1
4645
end
46+
end
47+
48+
context "when the same version already exists" do
49+
let!(:existing_version) { TestDataBuilder.new.create_version_with_hierarchy(pacticipant_name, version_number).and_return(:version) }
4750

51+
subject { Repository.new.create pacticipant_id: existing_version.pacticipant_id, number: version_number }
52+
53+
it "returns the pre-existing version" do
54+
expect(subject.id).to eq existing_version.id
55+
end
4856
end
4957
end
5058

spec/support/test_data_builder.rb

+5-3
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,14 @@ def create_pact_with_hierarchy consumer_name = "Consumer", consumer_version = "1
121121
def create_version_with_hierarchy pacticipant_name, pacticipant_version
122122
pacticipant = PactBroker::Domain::Pacticipant.create(:name => pacticipant_name)
123123
version = PactBroker::Domain::Version.create(:number => pacticipant_version, :pacticipant => pacticipant)
124-
PactBroker::Domain::Version.find(id: version.id) # Get version with populated order
124+
@version = PactBroker::Domain::Version.find(id: version.id) # Get version with populated order
125+
self
125126
end
126127

127128
def create_tag_with_hierarchy pacticipant_name, pacticipant_version, tag_name
128-
version = create_version_with_hierarchy pacticipant_name, pacticipant_version
129-
PactBroker::Domain::Tag.create(name: tag_name, version: version)
129+
create_version_with_hierarchy pacticipant_name, pacticipant_version
130+
PactBroker::Domain::Tag.create(name: tag_name, version: @version)
131+
self
130132
end
131133

132134
def create_pacticipant pacticipant_name, params = {}

0 commit comments

Comments
 (0)