Skip to content

Commit 3703a30

Browse files
committed
Fix race condition
1 parent ea81469 commit 3703a30

File tree

2 files changed

+41
-10
lines changed

2 files changed

+41
-10
lines changed

ecommerce/taxes/.mutant.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ matcher:
1010
ignore:
1111
- Taxes::Test*
1212
- Taxes::Configuration*
13-
- Taxes::VatRateCatalog#vat_rate_for
13+
- Taxes::VatRateCatalog#vat_rate_for
14+
- Taxes::AddAvailableVatRateHandler*
15+
- Taxes::RemoveAvailableVatRateHandler*

ecommerce/taxes/lib/taxes/services.rb

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,32 @@ def stream_name(order_id)
4242
end
4343

4444
class AddAvailableVatRateHandler
45+
include Infra::Retry
46+
4547
def initialize(event_store)
46-
@catalog = VatRateCatalog.new(event_store)
4748
@event_store = event_store
4849
end
4950

5051
def call(cmd)
51-
raise VatRateAlreadyExists if catalog.vat_rate_by_code(cmd.vat_rate.code)
52+
with_retry do
53+
event = last_available_vat_rate_event(cmd.vat_rate.code)
54+
raise VatRateAlreadyExists if event.instance_of?(AvailableVatRateAdded)
5255

53-
event_store.publish(available_vat_rate_added_event(cmd), stream_name: stream_name(cmd))
56+
expected_version = event ? event_store.position_in_stream(event.event_id, stream_name(cmd)) : -1
57+
event_store.publish(available_vat_rate_added_event(cmd), stream_name: stream_name(cmd), expected_version: expected_version)
58+
end
5459
end
5560

5661
private
5762

58-
attr_reader :event_store, :catalog
63+
attr_reader :event_store
64+
65+
def last_available_vat_rate_event(vat_rate_code)
66+
@event_store
67+
.read
68+
.stream("Taxes::AvailableVatRate$#{vat_rate_code}")
69+
.last
70+
end
5971

6072
def available_vat_rate_added_event(cmd)
6173
AvailableVatRateAdded.new(
@@ -72,20 +84,37 @@ def stream_name(cmd)
7284
end
7385

7486
class RemoveAvailableVatRateHandler
87+
include Infra::Retry
88+
7589
def initialize(event_store)
76-
@catalog = VatRateCatalog.new(event_store)
7790
@event_store = event_store
7891
end
7992

8093
def call(cmd)
81-
raise VatRateNotExists unless catalog.vat_rate_by_code(cmd.vat_rate_code)
82-
83-
event_store.publish(available_vat_rate_removed_event(cmd), stream_name: stream_name(cmd))
94+
with_retry do
95+
event = last_available_vat_rate_event(cmd.vat_rate_code)
96+
raise VatRateNotExists if event.nil?
97+
98+
event_store.publish(
99+
available_vat_rate_removed_event(cmd),
100+
stream_name: stream_name(cmd),
101+
expected_version: event_store.position_in_stream(event.event_id, stream_name(cmd))
102+
)
103+
end
84104
end
85105

86106
private
87107

88-
attr_reader :event_store, :catalog
108+
attr_reader :event_store
109+
110+
def last_available_vat_rate_event(vat_rate_code)
111+
event = event_store
112+
.read
113+
.stream("Taxes::AvailableVatRate$#{vat_rate_code}")
114+
.last
115+
116+
event if event.instance_of?(AvailableVatRateAdded)
117+
end
89118

90119
def available_vat_rate_removed_event(cmd)
91120
AvailableVatRateRemoved.new(data: { vat_rate_code: cmd.vat_rate_code })

0 commit comments

Comments
 (0)