File tree Expand file tree Collapse file tree 2 files changed +60
-2
lines changed Expand file tree Collapse file tree 2 files changed +60
-2
lines changed Original file line number Diff line number Diff line change @@ -215,7 +215,11 @@ def find_or_create_by!(attributes, &block)
215
215
def create_or_find_by ( attributes , &block )
216
216
transaction ( requires_new : true ) { create ( attributes , &block ) }
217
217
rescue ActiveRecord ::RecordNotUnique
218
- find_by! ( attributes )
218
+ if connection . transaction_open?
219
+ where ( attributes ) . lock . find_by! ( attributes )
220
+ else
221
+ find_by! ( attributes )
222
+ end
219
223
end
220
224
221
225
# Like #create_or_find_by, but calls
@@ -224,7 +228,11 @@ def create_or_find_by(attributes, &block)
224
228
def create_or_find_by! ( attributes , &block )
225
229
transaction ( requires_new : true ) { create! ( attributes , &block ) }
226
230
rescue ActiveRecord ::RecordNotUnique
227
- find_by! ( attributes )
231
+ if connection . transaction_open?
232
+ where ( attributes ) . lock . find_by! ( attributes )
233
+ else
234
+ find_by! ( attributes )
235
+ end
228
236
end
229
237
230
238
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
Original file line number Diff line number Diff line change @@ -2452,3 +2452,53 @@ def custom_post_relation(alias_name = "omg_posts")
2452
2452
)
2453
2453
end
2454
2454
end
2455
+
2456
+ class CreateOrFindByWithinTransactions < ActiveRecord ::TestCase
2457
+ unless current_adapter? ( :SQLite3Adapter )
2458
+ self . use_transactional_tests = false
2459
+
2460
+ def teardown
2461
+ Subscriber . delete_all
2462
+ end
2463
+
2464
+ def test_multiple_find_or_create_by_within_transactions
2465
+ duel { Subscriber . find_or_create_by ( nick : "bob" ) }
2466
+ end
2467
+
2468
+ def test_multiple_find_or_create_by_bang_within_transactions
2469
+ duel { Subscriber . find_or_create_by! ( nick : "bob" ) }
2470
+ end
2471
+
2472
+ private
2473
+ def duel
2474
+ assert_nil Subscriber . find_by ( nick : "bob" )
2475
+
2476
+ a_wakeup = Concurrent ::Event . new
2477
+ b_wakeup = Concurrent ::Event . new
2478
+
2479
+ a = Thread . new do
2480
+ Subscriber . transaction do
2481
+ a_wakeup . wait
2482
+ yield
2483
+ b_wakeup . set
2484
+ end
2485
+ end
2486
+
2487
+ b = Thread . new do
2488
+ Subscriber . transaction do
2489
+ # Read the record prematurely for MySQL REPEATABLE READ to kick in
2490
+ Subscriber . find_by ( nick : "bob" )
2491
+
2492
+ a_wakeup . set
2493
+ b_wakeup . wait
2494
+ yield
2495
+ end
2496
+ end
2497
+
2498
+ a . join
2499
+ b . join
2500
+
2501
+ assert_equal 1 , Subscriber . where ( nick : "bob" ) . count
2502
+ end
2503
+ end
2504
+ end
You can’t perform that action at this time.
0 commit comments