ci-queue 0.5.1 → 0.5.2

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
  SHA1:
3
- metadata.gz: '09984471792afae53d76117aea251073d3a204cf'
4
- data.tar.gz: 28948b3a043c71e218d772b30343d53c0097f7e4
3
+ metadata.gz: 442794179d88fc5fc9a2c3a781b5056ff27ee013
4
+ data.tar.gz: 03fa2c61961e02864f122854c9c21e09c0e2b387
5
5
  SHA512:
6
- metadata.gz: 399dea10f283781cff77c3e360cdd58f29dbe611c121ccacf92749eed98c4d71804cc2f948980b41ee38819f300e68ad4176673cd5f1e2300fb72ae5c3840f25
7
- data.tar.gz: 1b38218e090b7ae97783b3d072b50d64ff3fb2187e141903bf81b5df13dd6f82a416eb48e9fc1ab552559e00601b48d766f1b55c687822090788355cad9b1db0
6
+ metadata.gz: ca06529e6a43ec5100ce6d4da2c6ab1bd1f439f36706349e41da7b4653cd3879dc58243626687505a9fb63dd8fb714fc83c886c032c2db6c923fb24aa35c0a16
7
+ data.tar.gz: 7971b3b1d661227e89f1b2b807c8a060ae951748afa1ddd0eb5357285f2da39c16dbb1dd294661da605362d0dcff804f9267505cf9503e57ea35a8882eca8ff8
@@ -19,7 +19,7 @@ module CI
19
19
  @global_max_requeues = (tests.size * requeue_tolerance).ceil
20
20
  @shutdown_required = false
21
21
  super(redis: redis, build_id: build_id)
22
- @worker_id = worker_id
22
+ @worker_id = worker_id.to_s
23
23
  @timeout = timeout
24
24
  push(tests)
25
25
  end
@@ -69,47 +69,38 @@ module CI
69
69
  ]
70
70
  end
71
71
 
72
- def acknowledge(test, success)
73
- if @reserved_test == test
74
- @reserved_test = nil
75
- else
76
- raise ReservationError, "Acknowledged #{test.inspect} but #{@reserved_test.inspect} was reserved"
77
- end
78
-
79
- if !success && should_requeue?(test)
80
- requeue(test)
81
- false
82
- else
83
- ack(test)
84
- true
85
- end
72
+ def acknowledge(test)
73
+ raise_on_mismatching_test(test)
74
+ ack(test)
86
75
  end
87
76
 
88
- private
77
+ REQUEUE = %{
78
+ local processed_key = KEYS[1]
79
+ local requeues_count_key = KEYS[2]
80
+ local queue_key = KEYS[3]
81
+ local zset_key = KEYS[4]
89
82
 
90
- attr_reader :worker_id, :timeout, :max_requeues, :global_max_requeues
83
+ local max_requeues = tonumber(ARGV[1])
84
+ local global_max_requeues = tonumber(ARGV[2])
85
+ local test = ARGV[3]
86
+ local offset = ARGV[4]
91
87
 
92
- def should_requeue?(test)
93
- individual_requeues, global_requeues = redis.multi do
94
- redis.hincrby(key('requeues-count'), test, 1)
95
- redis.hincrby(key('requeues-count'), '___total___'.freeze, 1)
88
+ if redis.call('sismember', processed_key, test) == 1 then
89
+ return false
96
90
  end
97
91
 
98
- if individual_requeues.to_i > max_requeues || global_requeues.to_i > global_max_requeues
99
- redis.multi do
100
- redis.hincrby(key('requeues-count'), test, -1)
101
- redis.hincrby(key('requeues-count'), '___total___'.freeze, -1)
102
- end
92
+ local global_requeues = tonumber(redis.call('hget', requeues_count_key, '___total___'))
93
+ if global_requeues and global_requeues >= tonumber(global_max_requeues) then
103
94
  return false
104
95
  end
105
96
 
106
- true
107
- end
97
+ local requeues = tonumber(redis.call('hget', requeues_count_key, test))
98
+ if requeues and requeues >= max_requeues then
99
+ return false
100
+ end
108
101
 
109
- REQUEUE = %{
110
- local queue_key = KEYS[1]
111
- local test = ARGV[1]
112
- local offset = ARGV[2]
102
+ redis.call('hincrby', requeues_count_key, '___total___', 1)
103
+ redis.call('hincrby', requeues_count_key, test, 1)
113
104
 
114
105
  local pivot = redis.call('lrange', queue_key, -1 - offset, 0 - offset)[1]
115
106
  if pivot then
@@ -117,14 +108,33 @@ module CI
117
108
  else
118
109
  redis.call('lpush', queue_key, test)
119
110
  end
111
+
112
+ redis.call('zrem', zset_key, test)
113
+
114
+ return true
120
115
  }
121
116
  def requeue(test, offset: Redis.requeue_offset)
122
- load_script(ACKNOWLEDGE)
123
- load_script(REQUEUE)
124
- redis.multi do
125
- redis.decr(key('processed'))
126
- eval_script(REQUEUE, keys: [key('queue')], argv: [test, offset])
127
- ack(test)
117
+ raise_on_mismatching_test(test)
118
+
119
+ requeued = eval_script(
120
+ REQUEUE,
121
+ keys: [key('processed'), key('requeues-count'), key('queue'), key('running')],
122
+ argv: [max_requeues, global_max_requeues, test, offset],
123
+ ) == 1
124
+
125
+ @reserved_test = test unless requeued
126
+ requeued
127
+ end
128
+
129
+ private
130
+
131
+ attr_reader :worker_id, :timeout, :max_requeues, :global_max_requeues
132
+
133
+ def raise_on_mismatching_test(test)
134
+ if @reserved_test == test
135
+ @reserved_test = nil
136
+ else
137
+ raise ReservationError, "Acknowledged #{test.inspect} but #{@reserved_test.inspect} was reserved"
128
138
  end
129
139
  end
130
140
 
@@ -140,6 +150,8 @@ module CI
140
150
  RESERVE_TEST = %{
141
151
  local queue_key = KEYS[1]
142
152
  local zset_key = KEYS[2]
153
+ local processed_key = KEYS[3]
154
+
143
155
  local current_time = ARGV[1]
144
156
 
145
157
  local test = redis.call('rpop', queue_key)
@@ -151,38 +163,54 @@ module CI
151
163
  end
152
164
  }
153
165
  def try_to_reserve_test
154
- eval_script(RESERVE_TEST, keys: [key('queue'), key('running')], argv: [Time.now.to_f])
166
+ eval_script(
167
+ RESERVE_TEST,
168
+ keys: [key('queue'), key('running'), key('processed')],
169
+ argv: [Time.now.to_f],
170
+ )
155
171
  end
156
172
 
157
173
  RESERVE_LOST_TEST = %{
158
174
  local zset_key = KEYS[1]
175
+ local processed_key = KEYS[2]
159
176
  local current_time = ARGV[1]
160
177
  local timeout = ARGV[2]
161
178
 
162
- local test = redis.call('zrangebyscore', zset_key, 0, current_time - timeout)[1]
163
- if test then
164
- redis.call('zadd', zset_key, current_time, test)
165
- return test
166
- else
167
- return nil
179
+ local lost_tests = redis.call('zrangebyscore', zset_key, 0, current_time - timeout)
180
+ for _, test in ipairs(lost_tests) do
181
+ if redis.call('sismember', processed_key, test) == 0 then
182
+ redis.call('zadd', zset_key, current_time, test)
183
+ return test
184
+ end
168
185
  end
186
+
187
+ return nil
169
188
  }
170
189
  def try_to_reserve_lost_test
171
- eval_script(RESERVE_LOST_TEST, keys: [key('running')], argv: [Time.now.to_f, timeout])
190
+ eval_script(
191
+ RESERVE_LOST_TEST,
192
+ keys: [key('running'), key('completed')],
193
+ argv: [Time.now.to_f, timeout],
194
+ )
172
195
  end
173
196
 
174
197
  ACKNOWLEDGE = %{
175
198
  local zset_key = KEYS[1]
176
- local processed_count_key = KEYS[2]
177
- local test = ARGV[1]
199
+ local processed_key = KEYS[2]
178
200
 
179
- if redis.call('zrem', zset_key, test) == 1 then
180
- redis.call('incr', processed_count_key)
181
- end
201
+ local worker_id = ARGV[1]
202
+ local test = ARGV[2]
203
+
204
+ redis.call('zrem', zset_key, test)
205
+ return redis.call('sadd', processed_key, test)
182
206
  }
183
207
  def ack(test)
184
- eval_script(ACKNOWLEDGE, keys: [key('running'), key('processed')], argv: [test])
185
208
  redis.lpush(key('worker', worker_id, 'queue'), test)
209
+ eval_script(
210
+ ACKNOWLEDGE,
211
+ keys: [key('running'), key('processed')],
212
+ argv: [worker_id, test],
213
+ ) == 1
186
214
  end
187
215
 
188
216
  def push(tests)
@@ -30,12 +30,14 @@ module CI
30
30
  @queue.empty?
31
31
  end
32
32
 
33
- def acknowledge(test, success)
34
- if !success && should_requeue?(test)
35
- requeue(test)
36
- return false
37
- end
38
-
33
+ def acknowledge(test)
34
+ true
35
+ end
36
+
37
+ def requeue(test)
38
+ return false unless should_requeue?(test)
39
+ requeues[test] += 1
40
+ @queue.unshift(test)
39
41
  true
40
42
  end
41
43
 
@@ -47,11 +49,6 @@ module CI
47
49
  requeues[test] < max_requeues && requeues.values.inject(0, :+) < global_max_requeues
48
50
  end
49
51
 
50
- def requeue(test)
51
- requeues[test] += 1
52
- @queue.unshift(test)
53
- end
54
-
55
52
  def requeues
56
53
  @requeues ||= Hash.new(0)
57
54
  end
@@ -1,5 +1,5 @@
1
1
  module CI
2
2
  module Queue
3
- VERSION = '0.5.1'
3
+ VERSION = '0.5.2'
4
4
  end
5
5
  end
@@ -88,10 +88,15 @@ module Minitest
88
88
 
89
89
  if klass = runnable_classes[class_name]
90
90
  result = Minitest.run_one_method(klass, method_name)
91
- unless queue.acknowledge(test_name, result.passed? || result.skipped?)
91
+ failed = !(result.passed? || result.skipped?)
92
+ if failed && queue.requeue(test_name)
92
93
  result.requeue!
94
+ reporter.record(result)
95
+ elsif queue.acknowledge(test_name) || !failed
96
+ # If the test was already acknowledged by another worker (we timed out)
97
+ # Then we only record it if it is successful.
98
+ reporter.record(result)
93
99
  end
94
- reporter.record(result)
95
100
  else
96
101
  raise SuiteNotFound, "Couldn't find suite matching: #{test_name}"
97
102
  end
@@ -64,7 +64,7 @@ module Minitest
64
64
  end
65
65
 
66
66
  def processed
67
- redis.get(key('processed')).to_i
67
+ redis.scard(key('processed')).to_i
68
68
  end
69
69
 
70
70
  def completed?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ci-queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-28 00:00:00.000000000 Z
11
+ date: 2017-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  version: '0'
148
148
  requirements: []
149
149
  rubyforge_project:
150
- rubygems_version: 2.6.11
150
+ rubygems_version: 2.2.3
151
151
  signing_key:
152
152
  specification_version: 4
153
153
  summary: Distribute tests over many workers using a queue