ci-queue 0.65.0 → 0.67.0

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: 0bd235c5c9a18d43b30d5fc614e960e297b60fbaa3391a47db9eaa07cfa1bb4f
4
- data.tar.gz: 9f61f75c48456aaac0abb2b25e3cb200a22f00baf5c5612828bd61b54a59ed46
3
+ metadata.gz: d3a701add5867e968b8dcc2c8d9fe301f88b54ede5b0f2f615171fa1d620e6bd
4
+ data.tar.gz: 401f2f2c45c09edbd609c177a077b1aec2fe361f376439d6f5b8a29770d7e488
5
5
  SHA512:
6
- metadata.gz: 640842ac7e07b54184cab3a9881ec5f0401f3c128b6d8bf0011d576650e94c4f85864664fcc51b3fac6d6028b4fd95a7943364aa397e060acc3ddc56647662f6
7
- data.tar.gz: 4d8aa80210424460498cb29bee6fb7d18db2f1a9c453550c98e4133f34862d7a9882df5c60bf0b0f88e245210af7b6be943cf811c20d3e0f5a53d5ab2bf8e8dd
6
+ metadata.gz: f32ff8fa093d89df2194ac043943de43acfedbc5cbcf942c3638d0cc6ccf9659eb107dbf87ef04b3f19ba8329adf1b1275cb2125b8bc9c3cafd7072b49de3e5c
7
+ data.tar.gz: 01c75cfae762cb874df2160b04c7b96d14d7665631dfaaf642553587b14f7e86bd4d093aad6b9feefa3bee67b6626c3b2a143f3695f50b942579c59949eb16ed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ci-queue (0.65.0)
4
+ ci-queue (0.67.0)
5
5
  logger
6
6
 
7
7
  GEM
@@ -2,9 +2,18 @@
2
2
  local zset_key = KEYS[1]
3
3
  local processed_key = KEYS[2]
4
4
  local owners_key = KEYS[3]
5
+ local error_reports_key = KEYS[4]
5
6
 
6
7
  local test = ARGV[1]
7
-
8
+ local error = ARGV[2]
9
+ local ttl = ARGV[3]
8
10
  redis.call('zrem', zset_key, test)
9
11
  redis.call('hdel', owners_key, test) -- Doesn't matter if it was reclaimed by another workers
10
- return redis.call('sadd', processed_key, test)
12
+ local acknowledged = redis.call('sadd', processed_key, test)
13
+
14
+ if acknowledged and error ~= "" then
15
+ redis.call('hset', error_reports_key, test, error)
16
+ redis.call('expire', error_reports_key, ttl)
17
+ end
18
+
19
+ return acknowledged
@@ -11,6 +11,8 @@ module CI
11
11
  ::SocketError, # https://github.com/redis/redis-rb/pull/631
12
12
  ].freeze
13
13
 
14
+ DEFAULT_TIMEOUT = 2
15
+
14
16
  module RedisInstrumentation
15
17
  def call(command, redis_config)
16
18
  logger = redis_config.custom[:debug_log]
@@ -45,9 +47,10 @@ module CI
45
47
  # ci-queue should not contain any sensitive data, so we can just disable the verification.
46
48
  ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE },
47
49
  custom: custom_config,
50
+ timeout: DEFAULT_TIMEOUT,
48
51
  )
49
52
  else
50
- @redis = ::Redis.new(url: redis_url)
53
+ @redis = ::Redis.new(url: redis_url, timeout: DEFAULT_TIMEOUT)
51
54
  end
52
55
  end
53
56
 
@@ -178,8 +181,8 @@ module CI
178
181
  master_status == 'setup'
179
182
  end
180
183
 
181
- def increment_test_failed
182
- redis.incr(key('test_failed_count'))
184
+ def increment_test_failed(pipeline: redis)
185
+ pipeline.incr(key('test_failed_count'))
183
186
  end
184
187
 
185
188
  def test_failed
@@ -222,8 +225,8 @@ module CI
222
225
  redis.get(key('master-status'))
223
226
  end
224
227
 
225
- def eval_script(script, *args)
226
- redis.evalsha(load_script(script), *args)
228
+ def eval_script(script, keys:, argv:, pipeline: redis)
229
+ pipeline.evalsha(load_script(script), keys: keys, argv: argv)
227
230
  end
228
231
 
229
232
  def load_script(script)
@@ -56,23 +56,22 @@ module CI
56
56
  redis.rpush(key('warnings'), Marshal.dump([type, attributes]))
57
57
  end
58
58
 
59
+ Test = Struct.new(:id) # Hack
60
+
59
61
  def record_error(id, payload, stats: nil)
60
62
  redis.pipelined do |pipeline|
61
- pipeline.hset(
62
- key('error-reports'),
63
- id.dup.force_encoding(Encoding::BINARY),
64
- payload.dup.force_encoding(Encoding::BINARY),
65
- )
66
- pipeline.expire(key('error-reports'), config.redis_ttl)
63
+ @queue.acknowledge(id, error: payload, pipeline: pipeline)
67
64
  record_stats(stats, pipeline: pipeline)
65
+ @queue.increment_test_failed(pipeline: pipeline)
68
66
  end
69
67
  nil
70
68
  end
71
69
 
72
- def record_success(id, stats: nil, skip_flaky_record: false)
70
+ def record_success(id, stats: nil, skip_flaky_record: false, acknowledge: true)
71
+ @queue.acknowledge(id) if acknowledge
73
72
  error_reports_deleted_count, requeued_count, _ = redis.pipelined do |pipeline|
74
- pipeline.hdel(key('error-reports'), id.dup.force_encoding(Encoding::BINARY))
75
- pipeline.hget(key('requeues-count'), id.b)
73
+ pipeline.hdel(key('error-reports'), id)
74
+ pipeline.hget(key('requeues-count'), id)
76
75
  record_stats(stats, pipeline: pipeline)
77
76
  end
78
77
  record_flaky(id) if !skip_flaky_record && (error_reports_deleted_count.to_i > 0 || requeued_count.to_i > 0)
@@ -14,7 +14,7 @@ module CI
14
14
  redis.pipelined do |pipeline|
15
15
  pipeline.lpush(
16
16
  key('error-reports'),
17
- payload.force_encoding(Encoding::BINARY),
17
+ payload,
18
18
  )
19
19
  pipeline.expire(key('error-reports'), config.redis_ttl)
20
20
  record_stats(stats, pipeline: pipeline)
@@ -22,7 +22,7 @@ module CI
22
22
  redis.pipelined do |pipeline|
23
23
  pipeline.lpush(
24
24
  test_time_key(test_name),
25
- duration.to_s.force_encoding(Encoding::BINARY),
25
+ duration.to_s,
26
26
  )
27
27
  pipeline.expire(test_time_key(test_name), config.redis_ttl)
28
28
  end
@@ -33,7 +33,7 @@ module CI
33
33
  redis.pipelined do |pipeline|
34
34
  pipeline.lpush(
35
35
  all_test_names_key,
36
- test_name.dup.force_encoding(Encoding::BINARY),
36
+ test_name,
37
37
  )
38
38
  pipeline.expire(all_test_names_key, config.redis_ttl)
39
39
  end
@@ -53,11 +53,11 @@ module CI
53
53
  end
54
54
 
55
55
  def all_test_names_key
56
- "build:#{config.build_id}:list_of_test_names".dup.force_encoding(Encoding::BINARY)
56
+ "build:#{config.build_id}:list_of_test_names"
57
57
  end
58
58
 
59
59
  def test_time_key(test_name)
60
- "build:#{config.build_id}:#{test_name}".dup.force_encoding(Encoding::BINARY)
60
+ "build:#{config.build_id}:#{test_name}"
61
61
  end
62
62
  end
63
63
  end
@@ -97,13 +97,13 @@ module CI
97
97
  build.report_worker_error(error)
98
98
  end
99
99
 
100
- def acknowledge(test)
101
- test_key = test.id
100
+ def acknowledge(test_key, error: nil, pipeline: redis)
102
101
  raise_on_mismatching_test(test_key)
103
102
  eval_script(
104
103
  :acknowledge,
105
- keys: [key('running'), key('processed'), key('owners')],
106
- argv: [test_key],
104
+ keys: [key('running'), key('processed'), key('owners'), key('error-reports')],
105
+ argv: [test_key, error.to_s, config.redis_ttl],
106
+ pipeline: pipeline,
107
107
  ) == 1
108
108
  end
109
109
 
@@ -146,11 +146,11 @@ module CI
146
146
  config.worker_id
147
147
  end
148
148
 
149
- def raise_on_mismatching_test(test)
150
- if @reserved_test == test
149
+ def raise_on_mismatching_test(test_key)
150
+ if @reserved_test == test_key
151
151
  @reserved_test = nil
152
152
  else
153
- raise ReservationError, "Acknowledged #{test.inspect} but #{@reserved_test.inspect} was reserved"
153
+ raise ReservationError, "Acknowledged #{test_key.inspect} but #{@reserved_test.inspect} was reserved"
154
154
  end
155
155
  end
156
156
 
@@ -201,7 +201,14 @@ module CI
201
201
  def push(tests)
202
202
  @total = tests.size
203
203
 
204
- if @master = redis.setnx(key('master-status'), 'setup')
204
+ # We set a unique value (worker_id) and read it back to make "SET if Not eXists" idempotent in case of a retry.
205
+ value = key('setup', worker_id)
206
+ _, status = redis.pipelined do |pipeline|
207
+ pipeline.set(key('master-status'), value, nx: true)
208
+ pipeline.get(key('master-status'))
209
+ end
210
+
211
+ if @master = (value == status)
205
212
  puts "Worker electected as leader, pushing #{@total} tests to the queue."
206
213
  puts
207
214
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ gem "redis", "~> 5.0"
3
4
  require 'redis'
4
5
  require 'ci/queue/redis/build_record'
5
6
  require 'ci/queue/redis/base'
@@ -103,12 +103,12 @@ module CI
103
103
  @queue.empty?
104
104
  end
105
105
 
106
- def acknowledge(test)
106
+ def acknowledge(...)
107
107
  @progress += 1
108
108
  true
109
109
  end
110
110
 
111
- def increment_test_failed
111
+ def increment_test_failed(...)
112
112
  @test_failed = test_failed + 1
113
113
  end
114
114
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CI
4
4
  module Queue
5
- VERSION = '0.65.0'
5
+ VERSION = '0.67.0'
6
6
  DEV_SCRIPTS_ROOT = ::File.expand_path('../../../../../redis', __FILE__)
7
7
  RELEASE_SCRIPTS_ROOT = ::File.expand_path('../redis', __FILE__)
8
8
  end
@@ -52,7 +52,7 @@ module Minitest
52
52
  if (test.failure || test.error?) && !test.skipped?
53
53
  build.record_error("#{test.klass}##{test.name}", dump(test), stats: stats)
54
54
  else
55
- build.record_success("#{test.klass}##{test.name}", stats: stats, skip_flaky_record: test.skipped?)
55
+ build.record_success("#{test.klass}##{test.name}", stats: stats, skip_flaky_record: test.skipped?, acknowledge: !test.requeued?)
56
56
  end
57
57
  end
58
58
 
@@ -150,6 +150,7 @@ module Minitest
150
150
  end
151
151
 
152
152
  class SingleExample
153
+ attr_reader :method_name
153
154
 
154
155
  def initialize(runnable, method_name)
155
156
  @runnable = runnable
@@ -250,15 +251,8 @@ module Minitest
250
251
 
251
252
  if failed && CI::Queue.requeueable?(result) && queue.requeue(example)
252
253
  result.requeue!
253
- reporter.record(result)
254
- elsif queue.acknowledge(example)
255
- reporter.record(result)
256
- queue.increment_test_failed if failed
257
- elsif !failed
258
- # If the test was already acknowledged by another worker (we timed out)
259
- # Then we only record it if it is successful.
260
- reporter.record(result)
261
254
  end
255
+ reporter.record(result)
262
256
  end
263
257
  queue.stop_heartbeat!
264
258
  rescue Errno::EPIPE
data/lib/rspec/queue.rb CHANGED
@@ -224,13 +224,8 @@ module RSpec
224
224
  reporter.cancel_run!
225
225
  dup.mark_as_requeued!(reporter)
226
226
  return true
227
- elsif reporter.acknowledge || !@exception
228
- # If the test was already acknowledged by another worker (we timed out)
229
- # Then we only record it if it is successful.
230
- super(reporter)
231
227
  else
232
- reporter.cancel_run!
233
- return
228
+ super(reporter)
234
229
  end
235
230
  else
236
231
  super(reporter)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ci-queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.65.0
4
+ version: 0.67.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
@@ -254,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
254
254
  - !ruby/object:Gem::Version
255
255
  version: '0'
256
256
  requirements: []
257
- rubygems_version: 3.6.8
257
+ rubygems_version: 3.6.9
258
258
  specification_version: 4
259
259
  summary: Distribute tests over many workers using a queue
260
260
  test_files: []