Skip to content

Commit addd870

Browse files
authored
Merge pull request #231 from ikyn-inc/postgres_transaction_issue
Fixes #224. I believe this is a Postgres only issue
2 parents b7d4e1c + 7d36912 commit addd870

File tree

3 files changed

+33
-5
lines changed

3 files changed

+33
-5
lines changed

Gemfile.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ GEM
171171

172172
PLATFORMS
173173
arm64-darwin-22
174+
arm64-darwin-23
174175
x86_64-darwin-21
175176
x86_64-darwin-23
176177
x86_64-linux

app/models/solid_queue/semaphore.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ def signal(job)
1717
def signal_all(jobs)
1818
Proxy.signal_all(jobs)
1919
end
20+
21+
# Requires a unique index on key
22+
def create_unique_by(attributes)
23+
if connection.supports_insert_conflict_target?
24+
insert({ **attributes }, unique_by: :key).any?
25+
else
26+
create!(**attributes)
27+
end
28+
rescue ActiveRecord::RecordNotUnique
29+
false
30+
end
2031
end
2132

2233
class Proxy
@@ -41,18 +52,19 @@ def signal
4152
end
4253

4354
private
55+
4456
attr_accessor :job
4557

4658
def attempt_creation
47-
Semaphore.create!(key: key, value: limit - 1, expires_at: expires_at)
48-
true
49-
rescue ActiveRecord::RecordNotUnique
50-
if limit == 1 then false
59+
if Semaphore.create_unique_by(key: key, value: limit - 1, expires_at: expires_at)
60+
true
5161
else
52-
attempt_decrement
62+
check_limit_or_decrement
5363
end
5464
end
5565

66+
def check_limit_or_decrement = limit == 1 ? false : attempt_decrement
67+
5668
def attempt_decrement
5769
Semaphore.available.where(key: key).update_all([ "value = value - 1, expires_at = ?", expires_at ]) > 0
5870
end

test/integration/concurrency_controls_test.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,22 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
183183
assert job.reload.ready?
184184
end
185185

186+
test "verify transactions remain valid after Job creation conflicts via limits_concurrency" do
187+
ActiveRecord::Base.transaction do
188+
SequentialUpdateResultJob.perform_later(@result, name: "A", pause: 0.2.seconds)
189+
SequentialUpdateResultJob.perform_later(@result, name: "B")
190+
191+
begin
192+
assert_equal 2, SolidQueue::Job.count
193+
assert true, "Transaction state valid"
194+
rescue ActiveRecord::StatementInvalid
195+
assert false, "Transaction state unexpectedly invalid"
196+
end
197+
end
198+
end
199+
186200
private
201+
187202
def assert_stored_sequence(result, *sequences)
188203
expected = sequences.map { |sequence| "seq: " + sequence.map { |name| "s#{name}c#{name}" }.join }
189204
skip_active_record_query_cache do

0 commit comments

Comments
 (0)