44require 'minitest/queue'
55require 'ci/queue'
66require 'digest/md5'
7+ require 'fileutils'
78require 'minitest/reporters/bisect_reporter'
89require 'minitest/reporters/statsd_reporter'
910
@@ -49,16 +50,21 @@ def retry_command
4950
5051 def run_command
5152 require_worker_id!
53+ FileUtils . rm_f ( queue_config . recovery_manifest ) if queue_config . recovery_manifest
5254 if queue . retrying? || retry?
5355 if queue . expired?
5456 abort! "The test run is too old and can't be retried"
5557 end
5658 reset_counters
57- retry_queue = queue . retry_queue
59+ retry_queue = retry_queue_for_retry
5860 if retry_queue . exhausted?
5961 puts "The retry queue does not contain any failure, we'll process the main queue instead."
6062 else
61- puts "Retrying failed tests."
63+ if worker_history_retry?
64+ puts "Replaying #{ retry_queue . replayed_tests } tests from #{ retry_queue . history_items } worker reservations."
65+ else
66+ puts "Retrying failed tests."
67+ end
6268 self . queue = retry_queue
6369 end
6470 end
@@ -81,6 +87,13 @@ def run_command
8187 if queue_config . statsd_endpoint
8288 reporters << Minitest ::Reporters ::StatsdReporter . new ( statsd_endpoint : queue_config . statsd_endpoint )
8389 end
90+ if queue_config . recovery_manifest
91+ reporters << RecoveryReporter . new (
92+ path : queue_config . recovery_manifest ,
93+ queue : queue ,
94+ config : queue_config
95+ )
96+ end
8497 Minitest . queue_reporters = reporters
8598
8699 trap ( 'TERM' ) { Minitest . queue . shutdown! }
@@ -324,6 +337,22 @@ def reset_counters
324337 queue . build . reset_stats ( BuildStatusRecorder ::COUNTERS )
325338 end
326339
340+ def worker_history_retry?
341+ queue_config . retry_mode == :worker_history
342+ end
343+
344+ def retry_queue_for_retry
345+ if worker_history_retry?
346+ abort! 'Worker history recovery requires a distributed Redis queue' unless queue . distributed?
347+
348+ queue . retry_queue ( scope : :worker_history )
349+ else
350+ queue . retry_queue
351+ end
352+ rescue CI ::Queue ::Redis ::WorkerHistoryError => error
353+ abort! error . message
354+ end
355+
327356 def populate_queue
328357 Minitest . queue . populate ( Minitest . loaded_tests , random : ordering_seed , &:id )
329358 end
@@ -494,6 +523,22 @@ def parser
494523 queue_config . worker_id = worker_id
495524 end
496525
526+ help = <<~EOS
527+ Retry behavior: failures (default) or worker-history.
528+ EOS
529+ opts . separator ""
530+ opts . on ( '--retry-mode MODE' , %w[ failures worker-history ] , help ) do |mode |
531+ queue_config . retry_mode = mode . tr ( '-' , '_' ) . to_sym
532+ end
533+
534+ help = <<~EOS
535+ Write worker-history recovery details to a JSON file after a completed run.
536+ EOS
537+ opts . separator ""
538+ opts . on ( '--recovery-manifest PATH' , help ) do |path |
539+ queue_config . recovery_manifest = path
540+ end
541+
497542 help = <<~EOS
498543 Defines how many time a single test can be requeued.
499544 Defaults to 0.
0 commit comments