minitest-distributed 0.2.12 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1605e583ce4427da1909789380c1ac20ffe629a5f1ddf96452f93e45d50188f2
|
|
4
|
+
data.tar.gz: 052ba1210bee26b878052bb28207b800a67682f023e6fdcc1e03bebaea758376
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fb550faaa06a888561a3557b10f35fc867ca341e6e4008a49eb5c5137aa79b3490e6a5b24444d67c85ffc79c2e683aff7a21f948e7867b1b78bbafeccdebe9b8
|
|
7
|
+
data.tar.gz: ad60584710485b804af453696912a2676d56905e69642ead265111689ea10212768925f5c48017fd3c6da7547a94e8450b181dd2f89b5db82dc480d79d843d10
|
|
@@ -153,63 +153,65 @@ module Minitest
|
|
|
153
153
|
|
|
154
154
|
return if consumer_group_exists
|
|
155
155
|
|
|
156
|
-
tests = T.let(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
156
|
+
tests = T.let(
|
|
157
|
+
if initial_attempt
|
|
158
|
+
# If this is the first attempt for this run ID, we will schedule the full
|
|
159
|
+
# test suite as returned by the test selector to run.
|
|
160
|
+
|
|
161
|
+
tests_from_selector = test_selector.tests
|
|
162
|
+
adjust_combined_results(ResultAggregate.new(size: tests_from_selector.size))
|
|
163
|
+
tests_from_selector
|
|
164
|
+
|
|
165
|
+
elsif configuration.retry_failures
|
|
166
|
+
# Before starting a retry attempt, we first check if the previous attempt
|
|
167
|
+
# was aborted before it was completed. If this is the case, we cannot use
|
|
168
|
+
# retry mode, and should immediately fail the attempt.
|
|
169
|
+
if combined_results.abort?
|
|
170
|
+
# We mark this run as aborted, which causes this worker to not be successful.
|
|
171
|
+
@aborted = true
|
|
172
|
+
|
|
173
|
+
# We still publish an empty size run to Redis, so if there are any followers,
|
|
174
|
+
# they will wind down normally. Only the leader will exit
|
|
175
|
+
# with a non-zero exit status and fail the build; any follower will
|
|
176
|
+
# exit with status 0.
|
|
177
|
+
adjust_combined_results(ResultAggregate.new(size: 0))
|
|
178
|
+
[]
|
|
179
|
+
else
|
|
180
|
+
previous_failures, previous_errors, _deleted = redis.multi do |pipeline|
|
|
181
|
+
pipeline.lrange(list_key(ResultType::Failed.serialize), 0, -1)
|
|
182
|
+
pipeline.lrange(list_key(ResultType::Error.serialize), 0, -1)
|
|
183
|
+
pipeline.del(list_key(ResultType::Failed.serialize), list_key(ResultType::Error.serialize))
|
|
184
|
+
end
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
186
|
+
# We set the `size` key to the number of tests we are planning to schedule.
|
|
187
|
+
# We also adjust the number of failures and errors back to 0.
|
|
188
|
+
# We set the number of requeues to the number of tests that failed, so the
|
|
189
|
+
# run statistics will reflect that we retried some failed test.
|
|
190
|
+
#
|
|
191
|
+
# However, normally requeues are not acked, as we expect the test to be acked
|
|
192
|
+
# by another worker later. This makes the test loop think iot is already done.
|
|
193
|
+
# To prevent this, we initialize the number of acks negatively, so it evens out
|
|
194
|
+
# in the statistics.
|
|
195
|
+
total_failures = previous_failures.length + previous_errors.length
|
|
196
|
+
adjust_combined_results(ResultAggregate.new(
|
|
197
|
+
size: total_failures,
|
|
198
|
+
failures: -previous_failures.length,
|
|
199
|
+
errors: -previous_errors.length,
|
|
200
|
+
requeues: total_failures,
|
|
201
|
+
))
|
|
202
|
+
|
|
203
|
+
# For subsequent attempts, we check the list of previous failures and
|
|
204
|
+
# errors, and only schedule to re-run those tests. This allows for faster
|
|
205
|
+
# retries of potentially flaky tests.
|
|
206
|
+
test_identifiers_to_retry = T.let(previous_failures + previous_errors, T::Array[String])
|
|
207
|
+
test_identifiers_to_retry.map { |identifier| DefinedRunnable.from_identifier(identifier) }
|
|
208
|
+
end
|
|
209
|
+
else
|
|
210
|
+
adjust_combined_results(ResultAggregate.new(size: 0))
|
|
211
|
+
[]
|
|
212
|
+
end,
|
|
213
|
+
T::Array[Minitest::Runnable],
|
|
214
|
+
)
|
|
213
215
|
|
|
214
216
|
redis.pipelined do |pipeline|
|
|
215
217
|
tests.each do |test|
|
|
@@ -243,10 +245,17 @@ module Minitest
|
|
|
243
245
|
# To make sure we don't end up in a busy loop overwhelming Redis with commands
|
|
244
246
|
# when there is no work to do, we increase the blocking time exponentially,
|
|
245
247
|
# and reset it to the initial value if we processed any tests.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
+
#
|
|
249
|
+
# The backoff is capped at MAX_BACKOFF to bound how long a worker can sit
|
|
250
|
+
# inside a single XREADGROUP BLOCK call. Without a cap, after ~15 empty
|
|
251
|
+
# iterations the worker is blocked in Redis for 5+ minutes and cannot
|
|
252
|
+
# re-check `complete?` / `abort?` until the BLOCK returns, which manifests
|
|
253
|
+
# as a long post-100% teardown hang when pipelined XACKs race the progress
|
|
254
|
+
# reporter.
|
|
255
|
+
exponential_backoff = if stale_runnables.empty? && fresh_runnables.empty?
|
|
256
|
+
next_backoff(exponential_backoff)
|
|
248
257
|
else
|
|
249
|
-
|
|
258
|
+
INITIAL_BACKOFF
|
|
250
259
|
end
|
|
251
260
|
end
|
|
252
261
|
|
|
@@ -543,8 +552,20 @@ module Minitest
|
|
|
543
552
|
@logger ||= T.let(Logger.new(log_path), T.nilable(Logger))
|
|
544
553
|
end
|
|
545
554
|
|
|
555
|
+
sig { params(backoff: Integer).returns(Integer) }
|
|
556
|
+
def next_backoff(backoff)
|
|
557
|
+
[backoff << 1, MAX_BACKOFF].min
|
|
558
|
+
end
|
|
559
|
+
|
|
546
560
|
INITIAL_BACKOFF = 10 # milliseconds
|
|
547
561
|
private_constant :INITIAL_BACKOFF
|
|
562
|
+
|
|
563
|
+
# Cap on the XREADGROUP BLOCK timeout used by `consume`. Reached after roughly
|
|
564
|
+
# 9 consecutive empty iterations (10 ms * 2^9 = 5120 ms). Bounds the worst-case
|
|
565
|
+
# time a worker can be unresponsive to `complete?` / `abort?` after the queue
|
|
566
|
+
# is drained.
|
|
567
|
+
MAX_BACKOFF = 5_000 # milliseconds
|
|
568
|
+
private_constant :MAX_BACKOFF
|
|
548
569
|
end
|
|
549
570
|
end
|
|
550
571
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: minitest-distributed
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Willem van Bergen
|
|
@@ -183,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
183
183
|
- !ruby/object:Gem::Version
|
|
184
184
|
version: '0'
|
|
185
185
|
requirements: []
|
|
186
|
-
rubygems_version:
|
|
186
|
+
rubygems_version: 4.0.12
|
|
187
187
|
specification_version: 4
|
|
188
188
|
summary: Distributed test executor plugin for Minitest
|
|
189
189
|
test_files: []
|