ci-queue 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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