@@ -304,6 +304,28 @@ def test_streaming_timeout_raises_lost_master
304304 end
305305 end
306306
307+ def test_reserve_defers_own_requeued_test_once
308+ queue = worker ( 1 , populate : false , build_id : 'self-requeue-script' )
309+ queue . send ( :register )
310+ entry = "ATest#test_foo#{ DELIMITER } /tmp/a_test.rb"
311+ queue_key = queue . send ( :key , 'queue' )
312+ requeued_by_key = queue . send ( :key , 'requeued-by' )
313+ worker_queue_key = queue . send ( :key , 'worker' , queue . config . worker_id , 'queue' )
314+ workers_key = queue . send ( :key , 'workers' )
315+
316+ @redis . lpush ( queue_key , entry )
317+ @redis . hset ( requeued_by_key , entry , worker_queue_key )
318+ @redis . sadd ( workers_key , '2' )
319+
320+ first_try = queue . send ( :try_to_reserve_test )
321+ assert_nil first_try
322+ assert_equal [ entry ] , @redis . lrange ( queue_key , 0 , -1 )
323+ assert_nil @redis . hget ( requeued_by_key , entry )
324+
325+ second_try = queue . send ( :try_to_reserve_test )
326+ assert_equal entry , second_try
327+ end
328+
307329 def test_heartbeat_uses_test_id_for_processed_check
308330 queue = worker ( 1 , populate : false )
309331 entry = "ATest#test_foo#{ DELIMITER } /tmp/a_test.rb"
@@ -428,6 +450,87 @@ def test_worker_profiles_aggregates_multiple_workers
428450 assert_equal 'non-leader' , profiles [ '2' ] [ 'role' ]
429451 end
430452
453+ def test_worker_does_not_pick_up_its_own_requeued_test_when_others_are_available
454+ @redis . flushdb
455+
456+ test_list = TEST_LIST . first ( 3 )
457+ w1 = worker ( 1 , tests : test_list , build_id : 'self-requeue' , timeout : 10 , max_requeues : 1 , requeue_tolerance : 1.0 )
458+ w2 = worker ( 2 , populate : false , build_id : 'self-requeue' , timeout : 10 , max_requeues : 1 , requeue_tolerance : 1.0 )
459+ w3 = worker ( 3 , populate : false , build_id : 'self-requeue' , timeout : 10 , max_requeues : 1 , requeue_tolerance : 1.0 )
460+ w2 . send ( :register )
461+ w3 . send ( :register )
462+
463+ id_for = -> ( test ) { test . respond_to? ( :id ) ? test . id : CI ::Queue ::QueueEntry . test_id ( test ) }
464+
465+ requeued_test_id = nil
466+ picked_up_requeue = { }
467+ worker_two_reserved = false
468+ worker_three_reserved = false
469+ release_other_workers = false
470+
471+ mon = Monitor . new
472+ cond = mon . new_cond
473+
474+ threads = [
475+ Thread . new do
476+ w2 . poll do |test |
477+ test_id = id_for . call ( test )
478+ mon . synchronize do
479+ worker_two_reserved = true
480+ picked_up_requeue [ '2' ] = true if test_id == requeued_test_id
481+ cond . broadcast
482+ cond . wait_until { release_other_workers }
483+ end
484+ w2 . acknowledge ( test_id )
485+ end
486+ end ,
487+ Thread . new do
488+ w3 . poll do |test |
489+ test_id = id_for . call ( test )
490+ mon . synchronize do
491+ worker_three_reserved = true
492+ picked_up_requeue [ '3' ] = true if test_id == requeued_test_id
493+ cond . broadcast
494+ cond . wait_until { release_other_workers }
495+ end
496+ w3 . acknowledge ( test_id )
497+ end
498+ end ,
499+ ]
500+
501+ mon . synchronize do
502+ cond . wait_until { worker_two_reserved && worker_three_reserved }
503+ end
504+
505+ worker_one_picked_its_own_requeue = false
506+ first_test = true
507+
508+ w1 . poll do |test |
509+ test_id = id_for . call ( test )
510+
511+ if first_test
512+ first_test = false
513+ requeued_test_id = test_id
514+ w1 . report_failure!
515+ assert_equal true , w1 . requeue ( test )
516+ mon . synchronize do
517+ release_other_workers = true
518+ cond . broadcast
519+ end
520+ else
521+ worker_one_picked_its_own_requeue = true if test_id == requeued_test_id
522+ w1 . acknowledge ( test_id )
523+ end
524+ end
525+
526+ threads . each { |t | t . join ( 5 ) }
527+
528+ assert_equal false , worker_one_picked_its_own_requeue
529+ assert_equal true , picked_up_requeue . values . any?
530+ ensure
531+ threads &.each ( &:kill )
532+ end
533+
431534 private
432535
433536 def shuffled_test_list
0 commit comments