rrrspec-client 0.2.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 +7 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/Rakefile +1 -0
- data/bin/rrrspec +13 -0
- data/bin/rrrspec-client +4 -0
- data/lib/rrrspec/client/cli.rb +131 -0
- data/lib/rrrspec/client/configuration.rb +82 -0
- data/lib/rrrspec/client/rspec_runner.rb +83 -0
- data/lib/rrrspec/client/slave_runner.rb +128 -0
- data/lib/rrrspec/client/support.rb +147 -0
- data/lib/rrrspec/client/version.rb +5 -0
- data/lib/rrrspec/client.rb +5 -0
- data/lib/rrrspec/configuration.rb +42 -0
- data/lib/rrrspec/redis_models.rb +1091 -0
- data/lib/rrrspec.rb +103 -0
- data/rrrspec-client.gemspec +37 -0
- data/spec/fixture.rb +32 -0
- data/spec/rrrspec/client/cli_spec.rb +45 -0
- data/spec/rrrspec/redis_models_spec.rb +34 -0
- data/spec/spec_helper.rb +54 -0
- metadata +225 -0
@@ -0,0 +1,1091 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'uuidtools'
|
3
|
+
|
4
|
+
module RRRSpec
|
5
|
+
def self.convert_if_present(h, key)
|
6
|
+
if h[key].present?
|
7
|
+
h[key] = yield h[key]
|
8
|
+
else
|
9
|
+
h[key] = nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module RSyncInfo
|
14
|
+
WAIT_SERVER_SEC = 10
|
15
|
+
RSYNC_INFO_KEY = 'rrrspec:rsync_info'
|
16
|
+
|
17
|
+
module_function
|
18
|
+
|
19
|
+
def self.hget_loop(key)
|
20
|
+
loop do
|
21
|
+
v = RRRSpec.redis.hget(RSYNC_INFO_KEY, key)
|
22
|
+
return v if v.present?
|
23
|
+
sleep WAIT_SERVER_SEC
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public
|
28
|
+
def self.rsync_server
|
29
|
+
hget_loop('rsync_server')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public
|
33
|
+
def self.rsync_dir
|
34
|
+
hget_loop('rsync_dir')
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public
|
38
|
+
def self.rsync_options
|
39
|
+
hget_loop('rsync_options')
|
40
|
+
end
|
41
|
+
|
42
|
+
# ==========================================================================
|
43
|
+
# Heartbeat
|
44
|
+
|
45
|
+
# Public: Check its existence.
|
46
|
+
#
|
47
|
+
# Returns bool
|
48
|
+
def self.exist?
|
49
|
+
RRRSpec.redis.exists('rrrspec:rsync_info')
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public: Maintain heartbeat
|
53
|
+
def self.heartbeat(time)
|
54
|
+
unless Dir.exist?(RRRSpec.configuration.rsync_dir)
|
55
|
+
FileUtils.makedirs(RRRSpec.configuration.rsync_dir)
|
56
|
+
end
|
57
|
+
RRRSpec.redis.multi do
|
58
|
+
RRRSpec.redis.hmset(
|
59
|
+
'rrrspec:rsync_info',
|
60
|
+
'rsync_server', RRRSpec.configuration.rsync_server,
|
61
|
+
'rsync_dir', RRRSpec.configuration.rsync_dir,
|
62
|
+
'rsync_options', RRRSpec.configuration.rsync_options
|
63
|
+
)
|
64
|
+
RRRSpec.redis.expire('rrrspec:rsync_info', time)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ArbiterQueue
|
70
|
+
ARBITER_QUEUE_KEY = 'rrrspec:arbiter_queue'
|
71
|
+
|
72
|
+
# Public: Cancel the taskset.
|
73
|
+
def self.cancel(taskset)
|
74
|
+
RRRSpec.redis.rpush(ARBITER_QUEUE_KEY, "cancel\t#{taskset.key}")
|
75
|
+
end
|
76
|
+
|
77
|
+
# Public: Mark taskset failed
|
78
|
+
def self.fail(taskset)
|
79
|
+
RRRSpec.redis.rpush(ARBITER_QUEUE_KEY, "fail\t#{taskset.key}")
|
80
|
+
end
|
81
|
+
|
82
|
+
# Public: Check if there is no tasks left in the taskset.
|
83
|
+
def self.check(taskset)
|
84
|
+
RRRSpec.redis.rpush(ARBITER_QUEUE_KEY, "check\t#{taskset.key}")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Public: Update the status of the task based on the result of the trial.
|
88
|
+
def self.trial(trial)
|
89
|
+
RRRSpec.redis.rpush(ARBITER_QUEUE_KEY, "trial\t#{trial.key}")
|
90
|
+
end
|
91
|
+
|
92
|
+
# Public: Dequeue the task.
|
93
|
+
#
|
94
|
+
# Returns [command_name, arg]
|
95
|
+
def self.dequeue
|
96
|
+
_, line = RRRSpec.redis.blpop(ARBITER_QUEUE_KEY, 0)
|
97
|
+
command, arg = line.split("\t", 2)
|
98
|
+
case command
|
99
|
+
when 'cancel', 'check', 'fail'
|
100
|
+
arg = Taskset.new(arg)
|
101
|
+
when 'trial'
|
102
|
+
arg = Trial.new(arg)
|
103
|
+
else
|
104
|
+
raise 'Unknown command'
|
105
|
+
end
|
106
|
+
return command, arg
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module DispatcherQueue
|
111
|
+
DISPATCHER_QUEUE_KEY = 'rrrspec:dispatcher_queue'
|
112
|
+
DEFAULT_TIMEOUT = 10
|
113
|
+
|
114
|
+
# Public: Check the active tasksets and dispatch them
|
115
|
+
def self.notify
|
116
|
+
RRRSpec.redis.rpush(DISPATCHER_QUEUE_KEY, 0)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Public: Wait for the check command
|
120
|
+
def self.wait(timeout=DEFAULT_TIMEOUT)
|
121
|
+
RRRSpec.redis.blpop(DISPATCHER_QUEUE_KEY, timeout)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module PersisterQueue
|
126
|
+
PERSISTER_QUEUE_KEY = 'rrrspec:persister_queue'
|
127
|
+
|
128
|
+
module_function
|
129
|
+
|
130
|
+
# Public: Request the taskset to be persisted.
|
131
|
+
def enqueue(taskset)
|
132
|
+
RRRSpec.redis.rpush(PERSISTER_QUEUE_KEY, taskset.key)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Public: Wait for the persistence request.
|
136
|
+
def dequeue
|
137
|
+
_, line = RRRSpec.redis.blpop(PERSISTER_QUEUE_KEY, 0)
|
138
|
+
Taskset.new(line)
|
139
|
+
end
|
140
|
+
|
141
|
+
def empty?
|
142
|
+
RRRSpec.redis.llen(PERSISTER_QUEUE_KEY) == 0
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
module ActiveTaskset
|
147
|
+
ACTIVE_TASKSET_KEY = 'rrrspec:active_taskset'
|
148
|
+
|
149
|
+
# Public: Add the taskset to the active tasksets
|
150
|
+
def self.add(taskset)
|
151
|
+
RRRSpec.redis.rpush(ACTIVE_TASKSET_KEY, taskset.key)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Public: Remove the taskset from the active tasksets
|
155
|
+
def self.remove(taskset)
|
156
|
+
RRRSpec.redis.lrem(ACTIVE_TASKSET_KEY, 0, taskset.key)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Public: Returns an array of the active tasksets.
|
160
|
+
def self.list
|
161
|
+
RRRSpec.redis.lrange(ACTIVE_TASKSET_KEY, 0, -1).map do |key|
|
162
|
+
Taskset.new(key)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Public: Returns an array of the active tasksets whose rsync name is
|
167
|
+
# specified one.
|
168
|
+
def self.all_tasksets_of(rsync_name)
|
169
|
+
list.select { |taskset| taskset.rsync_name == rsync_name }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Taskset
|
174
|
+
attr_reader :key
|
175
|
+
|
176
|
+
def initialize(taskset_key)
|
177
|
+
@key = taskset_key
|
178
|
+
end
|
179
|
+
|
180
|
+
# Public: Create a new taskset.
|
181
|
+
# NOTE: This method will **NOT** call ActiveTaskset.add.
|
182
|
+
def self.create(rsync_name, setup_command, slave_command, worker_type,
|
183
|
+
taskset_class, max_workers, max_trials,
|
184
|
+
unknown_spec_timeout_sec, least_timeout_sec)
|
185
|
+
now = Time.zone.now
|
186
|
+
# For the reasons unknown, UUIDTools::UUID.timestamp_create changes 'now'.
|
187
|
+
taskset_key = RRRSpec.make_key(
|
188
|
+
'rrrspec', 'taskset', UUIDTools::UUID.timestamp_create(now.dup)
|
189
|
+
)
|
190
|
+
RRRSpec.redis.hmset(
|
191
|
+
taskset_key,
|
192
|
+
'rsync_name', rsync_name,
|
193
|
+
'setup_command', setup_command,
|
194
|
+
'slave_command', slave_command,
|
195
|
+
'worker_type', worker_type,
|
196
|
+
'max_workers', max_workers,
|
197
|
+
'max_trials', max_trials,
|
198
|
+
'taskset_class', taskset_class,
|
199
|
+
'unknown_spec_timeout_sec', unknown_spec_timeout_sec.to_s,
|
200
|
+
'least_timeout_sec', least_timeout_sec.to_s,
|
201
|
+
'created_at', now.to_s,
|
202
|
+
)
|
203
|
+
return new(taskset_key)
|
204
|
+
end
|
205
|
+
|
206
|
+
def ==(other)
|
207
|
+
@key == other.key
|
208
|
+
end
|
209
|
+
|
210
|
+
def exist?
|
211
|
+
RRRSpec.redis.exists(key)
|
212
|
+
end
|
213
|
+
|
214
|
+
def persisted?
|
215
|
+
RRRSpec.redis.ttl(key) != -1
|
216
|
+
end
|
217
|
+
|
218
|
+
def cancel
|
219
|
+
ArbiterQueue.cancel(self)
|
220
|
+
end
|
221
|
+
|
222
|
+
# ==========================================================================
|
223
|
+
# Property
|
224
|
+
|
225
|
+
# Public: The path name that is used in rsync
|
226
|
+
#
|
227
|
+
# Returns string
|
228
|
+
def rsync_name
|
229
|
+
RRRSpec.redis.hget(key, 'rsync_name')
|
230
|
+
end
|
231
|
+
|
232
|
+
# Public: The command used in setup
|
233
|
+
#
|
234
|
+
# Returns string
|
235
|
+
def setup_command
|
236
|
+
RRRSpec.redis.hget(key, 'setup_command')
|
237
|
+
end
|
238
|
+
|
239
|
+
# Public: The command that invokes rrrspec slave
|
240
|
+
#
|
241
|
+
# Returns string
|
242
|
+
def slave_command
|
243
|
+
RRRSpec.redis.hget(key, 'slave_command')
|
244
|
+
end
|
245
|
+
|
246
|
+
# Public: Type of the worker required to run the specs
|
247
|
+
#
|
248
|
+
# Returns string
|
249
|
+
def worker_type
|
250
|
+
RRRSpec.redis.hget(key, 'worker_type')
|
251
|
+
end
|
252
|
+
|
253
|
+
# Public: The number of workers that is used to run the specs
|
254
|
+
#
|
255
|
+
# Returns number
|
256
|
+
def max_workers
|
257
|
+
RRRSpec.redis.hget(key, 'max_workers').to_i
|
258
|
+
end
|
259
|
+
|
260
|
+
# Public: The number of trials that should be made.
|
261
|
+
#
|
262
|
+
# Returns number
|
263
|
+
def max_trials
|
264
|
+
RRRSpec.redis.hget(key, 'max_trials').to_i
|
265
|
+
end
|
266
|
+
|
267
|
+
# Public: A value that identifies the same taskset.
|
268
|
+
#
|
269
|
+
# Returns string
|
270
|
+
def taskset_class
|
271
|
+
RRRSpec.redis.hget(key, 'taskset_class')
|
272
|
+
end
|
273
|
+
|
274
|
+
# Public: The timeout sec for unknown spec files.
|
275
|
+
#
|
276
|
+
# Returns number
|
277
|
+
def unknown_spec_timeout_sec
|
278
|
+
RRRSpec.redis.hget(key, 'unknown_spec_timeout_sec').to_i
|
279
|
+
end
|
280
|
+
|
281
|
+
# Public: Timeout sec at least any specs should wait.
|
282
|
+
#
|
283
|
+
# Returns number
|
284
|
+
def least_timeout_sec
|
285
|
+
RRRSpec.redis.hget(key, 'least_timeout_sec').to_i
|
286
|
+
end
|
287
|
+
|
288
|
+
# Public: Returns the created_at
|
289
|
+
#
|
290
|
+
# Returns Time
|
291
|
+
def created_at
|
292
|
+
v = RRRSpec.redis.hget(key, 'created_at')
|
293
|
+
v.present? ? Time.zone.parse(v) : nil
|
294
|
+
end
|
295
|
+
|
296
|
+
# ==========================================================================
|
297
|
+
# WorkerLogs
|
298
|
+
|
299
|
+
# Public: Add a worker log
|
300
|
+
def add_worker_log(worker_log)
|
301
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'worker_log'),
|
302
|
+
worker_log.key)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Public: Return an array of worker_logs
|
306
|
+
def worker_logs
|
307
|
+
RRRSpec.redis.lrange(RRRSpec.make_key(key, 'worker_log'), 0, -1).map do |key|
|
308
|
+
WorkerLog.new(key)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# ==========================================================================
|
313
|
+
# Slaves
|
314
|
+
|
315
|
+
# Public: Add a slave
|
316
|
+
def add_slave(slave)
|
317
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'slave'),
|
318
|
+
slave.key)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Public: Return an array of slaves
|
322
|
+
def slaves
|
323
|
+
RRRSpec.redis.lrange(RRRSpec.make_key(key, 'slave'), 0, -1).map do |key|
|
324
|
+
Slave.new(key)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# ==========================================================================
|
329
|
+
# Tasks
|
330
|
+
|
331
|
+
# Public: Add a task.
|
332
|
+
# NOTE: This method does **NOT** enqueue to the task_queue
|
333
|
+
def add_task(task)
|
334
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'tasks'), task.key)
|
335
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'tasks_left'), task.key)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Public: Finish the task. It is no longer appeared in the `tasks_left`.
|
339
|
+
def finish_task(task)
|
340
|
+
RRRSpec.redis.lrem(RRRSpec.make_key(key, 'tasks_left'), 0, task.key)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Public: All the tasks that are contained by the taskset.
|
344
|
+
#
|
345
|
+
# Returns an array of the task instances
|
346
|
+
def tasks
|
347
|
+
RRRSpec.redis.lrange(RRRSpec.make_key(key, 'tasks'), 0, -1).map do |key|
|
348
|
+
Task.new(key)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Public: Size of all tasks.
|
353
|
+
def task_size
|
354
|
+
RRRSpec.redis.llen(RRRSpec.make_key(key, 'tasks')).to_i
|
355
|
+
end
|
356
|
+
|
357
|
+
# Public: All the tasks that are not migrated into the persistent store.
|
358
|
+
# In short, the tasks that are `add_task`ed but not `finish_task`ed.
|
359
|
+
#
|
360
|
+
# Returns an array of the task instances.
|
361
|
+
def tasks_left
|
362
|
+
RRRSpec.redis.lrange(RRRSpec.make_key(key, 'tasks_left'), 0, -1).map do |key|
|
363
|
+
Task.new(key)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Public: Enqueue the task to the task_queue.
|
368
|
+
def enqueue_task(task)
|
369
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'task_queue'), task.key)
|
370
|
+
end
|
371
|
+
|
372
|
+
# Public: Enqueue the task in the reversed way.
|
373
|
+
def reversed_enqueue_task(task)
|
374
|
+
RRRSpec.redis.lpush(RRRSpec.make_key(key, 'task_queue'), task.key)
|
375
|
+
end
|
376
|
+
|
377
|
+
# Public: Dequeue the task from the task_queue.
|
378
|
+
#
|
379
|
+
# Returns a task or nil if timeouts
|
380
|
+
def dequeue_task(timeout)
|
381
|
+
if timeout < 0
|
382
|
+
task_key = RRRSpec.redis.lpop(RRRSpec.make_key(key, 'task_queue'))
|
383
|
+
else
|
384
|
+
_, task_key = RRRSpec.redis.blpop(RRRSpec.make_key(key, 'task_queue'), timeout)
|
385
|
+
end
|
386
|
+
return nil unless task_key
|
387
|
+
Task.new(task_key)
|
388
|
+
end
|
389
|
+
|
390
|
+
# Public: Remove all the tasks enqueued to the task_queue.
|
391
|
+
def clear_queue
|
392
|
+
RRRSpec.redis.del(RRRSpec.make_key(key, 'task_queue'))
|
393
|
+
end
|
394
|
+
|
395
|
+
# Public: Checks whether the task_queue is empty.
|
396
|
+
def queue_empty?
|
397
|
+
RRRSpec.redis.llen(RRRSpec.make_key(key, 'task_queue')) == 0
|
398
|
+
end
|
399
|
+
|
400
|
+
# ==========================================================================
|
401
|
+
# Status
|
402
|
+
|
403
|
+
# Public: Current status
|
404
|
+
#
|
405
|
+
# Returns either nil, "running", "succeeded", "cancelled" or "failed"
|
406
|
+
def status
|
407
|
+
RRRSpec.redis.hget(key, 'status')
|
408
|
+
end
|
409
|
+
|
410
|
+
# Public: Update the status. It should be one of:
|
411
|
+
# ["running", "succeeded", "cancelled", "failed"]
|
412
|
+
def update_status(status)
|
413
|
+
RRRSpec.redis.hset(key, 'status', status)
|
414
|
+
end
|
415
|
+
|
416
|
+
# Public: Current succeeded task count. A task is counted as succeeded one
|
417
|
+
# if its status is "passed" or "pending".
|
418
|
+
#
|
419
|
+
# Returns a number
|
420
|
+
def succeeded_count
|
421
|
+
RRRSpec.redis.hget(key, 'succeeded_count').to_i
|
422
|
+
end
|
423
|
+
|
424
|
+
# Public: Increment succeeded_count
|
425
|
+
def incr_succeeded_count
|
426
|
+
RRRSpec.redis.hincrby(key, 'succeeded_count', 1)
|
427
|
+
end
|
428
|
+
|
429
|
+
# Public: Current failed task count. A task is counted as failed one if its
|
430
|
+
# status is "failed".
|
431
|
+
#
|
432
|
+
# Returns a number
|
433
|
+
def failed_count
|
434
|
+
RRRSpec.redis.hget(key, 'failed_count').to_i
|
435
|
+
end
|
436
|
+
|
437
|
+
# Public: Increment failed_count
|
438
|
+
def incr_failed_count
|
439
|
+
RRRSpec.redis.hincrby(key, 'failed_count', 1)
|
440
|
+
end
|
441
|
+
|
442
|
+
# Public: Returns the finished_at
|
443
|
+
def finished_at
|
444
|
+
v = RRRSpec.redis.hget(key, 'finished_at')
|
445
|
+
v.present? ? Time.zone.parse(v) : nil
|
446
|
+
end
|
447
|
+
|
448
|
+
# Public: Set finished_at time if it is empty
|
449
|
+
def set_finished_time
|
450
|
+
RRRSpec.redis.hsetnx(key, 'finished_at', Time.zone.now.to_s)
|
451
|
+
end
|
452
|
+
|
453
|
+
# Public: Overall logs of the taskset
|
454
|
+
def log
|
455
|
+
RRRSpec.redis.get(RRRSpec.make_key(key, 'log')) || ""
|
456
|
+
end
|
457
|
+
|
458
|
+
# Public: Append a line to the log
|
459
|
+
def append_log(string)
|
460
|
+
RRRSpec.redis.append(RRRSpec.make_key(key, 'log'), string)
|
461
|
+
end
|
462
|
+
|
463
|
+
# ==========================================================================
|
464
|
+
# Serialize
|
465
|
+
|
466
|
+
def to_h
|
467
|
+
h = RRRSpec.redis.hgetall(key)
|
468
|
+
h['key'] = key
|
469
|
+
h['log'] = log
|
470
|
+
h['tasks'] = tasks.map { |task| { 'key' => task.key } }
|
471
|
+
h['slaves'] = slaves.map { |slave| { 'key' => slave.key } }
|
472
|
+
h['worker_logs'] = worker_logs.map { |worker_log| { 'key' => worker_log.key } }
|
473
|
+
RRRSpec.convert_if_present(h, 'max_workers') { |v| v.to_i }
|
474
|
+
RRRSpec.convert_if_present(h, 'max_trials') { |v| v.to_i }
|
475
|
+
RRRSpec.convert_if_present(h, 'unknown_spec_timeout_sec') { |v| v.to_i }
|
476
|
+
RRRSpec.convert_if_present(h, 'least_timeout_sec') { |v| v.to_i }
|
477
|
+
RRRSpec.convert_if_present(h, 'created_at') { |v| Time.zone.parse(v) }
|
478
|
+
RRRSpec.convert_if_present(h, 'finished_at') { |v| Time.zone.parse(v) }
|
479
|
+
h.delete('succeeded_count')
|
480
|
+
h.delete('failed_count')
|
481
|
+
h
|
482
|
+
end
|
483
|
+
|
484
|
+
def to_json(options=nil)
|
485
|
+
to_h.to_json(options)
|
486
|
+
end
|
487
|
+
|
488
|
+
# ==========================================================================
|
489
|
+
# Persistence
|
490
|
+
|
491
|
+
def expire(sec)
|
492
|
+
tasks.each { |task| task.expire(sec) }
|
493
|
+
slaves.each { |slave| slave.expire(sec) }
|
494
|
+
worker_logs.each { |worker_log| worker_log.expire(sec) }
|
495
|
+
RRRSpec.redis.expire(key, sec)
|
496
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'log'), sec)
|
497
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'slave'), sec)
|
498
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'worker_log'), sec)
|
499
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'task_queue'), sec)
|
500
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'tasks'), sec)
|
501
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'tasks_left'), sec)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
class WorkerLog
|
506
|
+
attr_reader :key
|
507
|
+
|
508
|
+
def initialize(worker_log_key)
|
509
|
+
@key = worker_log_key
|
510
|
+
end
|
511
|
+
|
512
|
+
# Public: Create a new worker_log.
|
513
|
+
# This method will call Taskset#add_worker_log
|
514
|
+
def self.create(worker, taskset)
|
515
|
+
worker_log_key = RRRSpec.make_key(taskset.key, worker.key)
|
516
|
+
RRRSpec.redis.hmset(
|
517
|
+
worker_log_key,
|
518
|
+
'worker', worker.key,
|
519
|
+
'taskset', taskset.key,
|
520
|
+
'started_at', Time.zone.now.to_s,
|
521
|
+
)
|
522
|
+
worker_log = new(worker_log_key)
|
523
|
+
taskset.add_worker_log(worker_log)
|
524
|
+
return worker_log
|
525
|
+
end
|
526
|
+
|
527
|
+
# ==========================================================================
|
528
|
+
# Property
|
529
|
+
|
530
|
+
# Public: Returns the started_at
|
531
|
+
def started_at
|
532
|
+
v = RRRSpec.redis.hget(key, 'started_at')
|
533
|
+
v.present? ? Time.zone.parse(v) : nil
|
534
|
+
end
|
535
|
+
|
536
|
+
# ==========================================================================
|
537
|
+
# Status
|
538
|
+
|
539
|
+
# Public: Returns the rsync_finished_at
|
540
|
+
def rsync_finished_at
|
541
|
+
v = RRRSpec.redis.hget(key, 'rsync_finished_at')
|
542
|
+
v.present? ? Time.zone.parse(v) : nil
|
543
|
+
end
|
544
|
+
|
545
|
+
# Public: Set rsync_finished_at time
|
546
|
+
def set_rsync_finished_time
|
547
|
+
RRRSpec.redis.hset(key, 'rsync_finished_at', Time.zone.now.to_s)
|
548
|
+
end
|
549
|
+
|
550
|
+
# Public: Returns the setup_finished_at
|
551
|
+
def setup_finished_at
|
552
|
+
v = RRRSpec.redis.hget(key, 'setup_finished_at')
|
553
|
+
v.present? ? Time.zone.parse(v) : nil
|
554
|
+
end
|
555
|
+
|
556
|
+
# Public: Set setup_finished_at time
|
557
|
+
def set_setup_finished_time
|
558
|
+
RRRSpec.redis.hset(key, 'setup_finished_at', Time.zone.now.to_s)
|
559
|
+
end
|
560
|
+
|
561
|
+
# Public: Returns the finished_at
|
562
|
+
def finished_at
|
563
|
+
v = RRRSpec.redis.hget(key, 'finished_at')
|
564
|
+
v.present? ? Time.zone.parse(v) : nil
|
565
|
+
end
|
566
|
+
|
567
|
+
# Public: Set finished_at time if it is empty
|
568
|
+
def set_finished_time
|
569
|
+
RRRSpec.redis.hsetnx(key, 'finished_at', Time.zone.now.to_s)
|
570
|
+
end
|
571
|
+
|
572
|
+
# Public: Logs happend in worker
|
573
|
+
def log
|
574
|
+
RRRSpec.redis.get(RRRSpec.make_key(key, 'log')) || ""
|
575
|
+
end
|
576
|
+
|
577
|
+
# Public: Append a line to the log
|
578
|
+
def append_log(string)
|
579
|
+
RRRSpec.redis.append(RRRSpec.make_key(key, 'log'), string)
|
580
|
+
end
|
581
|
+
|
582
|
+
# ==========================================================================
|
583
|
+
# Serialize
|
584
|
+
|
585
|
+
def to_h
|
586
|
+
h = RRRSpec.redis.hgetall(key)
|
587
|
+
h['key'] = key
|
588
|
+
h['log'] = log
|
589
|
+
h['worker'] = { 'key' => h['worker'] }
|
590
|
+
h['taskset'] = { 'key' => h['taskset'] }
|
591
|
+
RRRSpec.convert_if_present(h, 'started_at') { |v| Time.zone.parse(v) }
|
592
|
+
RRRSpec.convert_if_present(h, 'rsync_finished_at') { |v| Time.zone.parse(v) }
|
593
|
+
RRRSpec.convert_if_present(h, 'setup_finished_at') { |v| Time.zone.parse(v) }
|
594
|
+
RRRSpec.convert_if_present(h, 'finished_at') { |v| Time.zone.parse(v) }
|
595
|
+
h
|
596
|
+
end
|
597
|
+
|
598
|
+
def to_json(options=nil)
|
599
|
+
to_h.to_json(options)
|
600
|
+
end
|
601
|
+
|
602
|
+
# ==========================================================================
|
603
|
+
# Persistence
|
604
|
+
|
605
|
+
def expire(sec)
|
606
|
+
RRRSpec.redis.expire(key, sec)
|
607
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'log'), sec)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
class Task
|
612
|
+
attr_reader :key
|
613
|
+
|
614
|
+
def initialize(task_key)
|
615
|
+
@key = task_key
|
616
|
+
end
|
617
|
+
|
618
|
+
def self.create(taskset, estimate_sec, spec_file)
|
619
|
+
task_key = RRRSpec.make_key(taskset.key, 'task', spec_file)
|
620
|
+
RRRSpec.redis.hmset(
|
621
|
+
task_key,
|
622
|
+
'taskset', taskset.key,
|
623
|
+
'estimate_sec', estimate_sec,
|
624
|
+
'spec_file', spec_file
|
625
|
+
)
|
626
|
+
return new(task_key)
|
627
|
+
end
|
628
|
+
|
629
|
+
def ==(other)
|
630
|
+
@key == other.key
|
631
|
+
end
|
632
|
+
|
633
|
+
# ==========================================================================
|
634
|
+
# Property
|
635
|
+
|
636
|
+
# Public: Estimate time to finishe the task.
|
637
|
+
#
|
638
|
+
# Returns seconds or nil if there is no estimation
|
639
|
+
def estimate_sec
|
640
|
+
v = RRRSpec.redis.hget(key, 'estimate_sec')
|
641
|
+
v.present? ? v.to_i : nil
|
642
|
+
end
|
643
|
+
|
644
|
+
# Public: Spec file to run.
|
645
|
+
#
|
646
|
+
# Returns a path to the spec
|
647
|
+
def spec_file
|
648
|
+
RRRSpec.redis.hget(key, 'spec_file')
|
649
|
+
end
|
650
|
+
|
651
|
+
# Public: Included taskset
|
652
|
+
#
|
653
|
+
# Returns a Taskset
|
654
|
+
def taskset
|
655
|
+
Taskset.new(RRRSpec.redis.hget(key, 'taskset'))
|
656
|
+
end
|
657
|
+
|
658
|
+
# ==========================================================================
|
659
|
+
# Trial
|
660
|
+
|
661
|
+
# Public: Returns the trials of the task.
|
662
|
+
# The return value should be sorted in the order added.
|
663
|
+
#
|
664
|
+
# Returns an array of the Trials
|
665
|
+
def trials
|
666
|
+
RRRSpec.redis.lrange(RRRSpec.make_key(key, 'trial'), 0, -1).map do |key|
|
667
|
+
Trial.new(key)
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
# Public: Add a trial of the task.
|
672
|
+
def add_trial(trial)
|
673
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'trial'),
|
674
|
+
trial.key)
|
675
|
+
end
|
676
|
+
|
677
|
+
# ==========================================================================
|
678
|
+
# Status
|
679
|
+
|
680
|
+
# Public: Current status
|
681
|
+
#
|
682
|
+
# Returns either nil, "running", "passed", "pending" or "failed"
|
683
|
+
def status
|
684
|
+
RRRSpec.redis.hget(key, 'status')
|
685
|
+
end
|
686
|
+
|
687
|
+
# Public: Update the status. It should be one of:
|
688
|
+
# [nil, "running", "passed", "pending", "failed"]
|
689
|
+
def update_status(status)
|
690
|
+
if status.present?
|
691
|
+
RRRSpec.redis.hset(key, 'status', status)
|
692
|
+
else
|
693
|
+
RRRSpec.redis.hdel(key, 'status')
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
# ==========================================================================
|
698
|
+
# Serialize
|
699
|
+
|
700
|
+
def to_h
|
701
|
+
h = RRRSpec.redis.hgetall(key)
|
702
|
+
h['key'] = key
|
703
|
+
h['trials'] = trials.map { |trial| { 'key' => trial.key } }
|
704
|
+
h['taskset'] = { 'key' => h['taskset'] }
|
705
|
+
RRRSpec.convert_if_present(h, 'estimate_sec') { |v| v.to_i }
|
706
|
+
h
|
707
|
+
end
|
708
|
+
|
709
|
+
def to_json(options=nil)
|
710
|
+
to_h.to_json(options)
|
711
|
+
end
|
712
|
+
|
713
|
+
# ==========================================================================
|
714
|
+
# Persistence
|
715
|
+
|
716
|
+
def expire(sec)
|
717
|
+
trials.each { |trial| trial.expire(sec) }
|
718
|
+
RRRSpec.redis.expire(key, sec)
|
719
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'trial'), sec)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
class Trial
|
724
|
+
attr_reader :key
|
725
|
+
|
726
|
+
def initialize(trial_key)
|
727
|
+
@key = trial_key
|
728
|
+
end
|
729
|
+
|
730
|
+
# Public: Create a new trial.
|
731
|
+
# This method will call Task#add_trial and Slave#add_trial.
|
732
|
+
def self.create(task, slave)
|
733
|
+
trial_key = RRRSpec.make_key(
|
734
|
+
task.key, 'trial', UUIDTools::UUID.timestamp_create
|
735
|
+
)
|
736
|
+
RRRSpec.redis.hmset(
|
737
|
+
trial_key,
|
738
|
+
'task', task.key,
|
739
|
+
'slave', slave.key,
|
740
|
+
)
|
741
|
+
trial = new(trial_key)
|
742
|
+
task.add_trial(trial)
|
743
|
+
slave.add_trial(trial)
|
744
|
+
return trial
|
745
|
+
end
|
746
|
+
|
747
|
+
# ==========================================================================
|
748
|
+
# Property
|
749
|
+
|
750
|
+
# Public: Tried task
|
751
|
+
#
|
752
|
+
# Returns a Task
|
753
|
+
def task
|
754
|
+
Task.new(RRRSpec.redis.hget(key, 'task'))
|
755
|
+
end
|
756
|
+
|
757
|
+
# Public: The slave worked for this.
|
758
|
+
#
|
759
|
+
# Returns a Slave
|
760
|
+
def slave
|
761
|
+
Slave.new(RRRSpec.redis.hget(key, 'slave'))
|
762
|
+
end
|
763
|
+
|
764
|
+
# ==========================================================================
|
765
|
+
# Status
|
766
|
+
|
767
|
+
# Public: Current status
|
768
|
+
#
|
769
|
+
# Returns either nil, "passed", "pending", "failed" or "error"
|
770
|
+
def status
|
771
|
+
RRRSpec.redis.hget(key, 'status')
|
772
|
+
end
|
773
|
+
|
774
|
+
# Public: Set started_at time.
|
775
|
+
def start
|
776
|
+
RRRSpec.redis.hset(key, 'started_at', Time.zone.now.to_s)
|
777
|
+
end
|
778
|
+
|
779
|
+
# Public: Finish the trial
|
780
|
+
# status should be one of ["passed", "pending", "failed", "error"].
|
781
|
+
# stdout and stderr should be string or nil.
|
782
|
+
# passed, pending and failed is the count of examplegroups and should be
|
783
|
+
# either nil or numbers.
|
784
|
+
def finish(status, stdout, stderr, passed, pending, failed)
|
785
|
+
RRRSpec.redis.hmset(
|
786
|
+
key,
|
787
|
+
'finished_at', Time.zone.now.to_s,
|
788
|
+
'status', status,
|
789
|
+
'stdout', stdout,
|
790
|
+
'stderr', stderr,
|
791
|
+
'passed', passed,
|
792
|
+
'pending', pending,
|
793
|
+
'failed', failed
|
794
|
+
)
|
795
|
+
end
|
796
|
+
|
797
|
+
# Public: Returns the started_at
|
798
|
+
def started_at
|
799
|
+
v = RRRSpec.redis.hget(key, 'started_at')
|
800
|
+
v.present? ? Time.zone.parse(v) : nil
|
801
|
+
end
|
802
|
+
|
803
|
+
# Public: Returns the finished_at
|
804
|
+
def finished_at
|
805
|
+
v = RRRSpec.redis.hget(key, 'finished_at')
|
806
|
+
v.present? ? Time.zone.parse(v) : nil
|
807
|
+
end
|
808
|
+
|
809
|
+
# Public: Returns the stdout
|
810
|
+
def stdout
|
811
|
+
RRRSpec.redis.hget(key, 'stdout')
|
812
|
+
end
|
813
|
+
|
814
|
+
# Public: Returns the stderr
|
815
|
+
def stderr
|
816
|
+
RRRSpec.redis.hget(key, 'stderr')
|
817
|
+
end
|
818
|
+
|
819
|
+
# Public: Returns the passed examples
|
820
|
+
def passed
|
821
|
+
v = RRRSpec.redis.hget(key, 'passed')
|
822
|
+
v.present? ? v.to_i : nil
|
823
|
+
end
|
824
|
+
|
825
|
+
# Public: Returns the pending examples
|
826
|
+
def pending
|
827
|
+
v = RRRSpec.redis.hget(key, 'pending')
|
828
|
+
v.present? ? v.to_i : nil
|
829
|
+
end
|
830
|
+
|
831
|
+
# Public: Returns the failed examples
|
832
|
+
def failed
|
833
|
+
v = RRRSpec.redis.hget(key, 'failed')
|
834
|
+
v.present? ? v.to_i : nil
|
835
|
+
end
|
836
|
+
|
837
|
+
# ==========================================================================
|
838
|
+
# Serialize
|
839
|
+
|
840
|
+
def to_h
|
841
|
+
h = RRRSpec.redis.hgetall(key)
|
842
|
+
h['key'] = key
|
843
|
+
h['task'] = { 'key' => h['task'] }
|
844
|
+
h['slave'] = { 'key' => h['slave'] }
|
845
|
+
RRRSpec.convert_if_present(h, 'started_at') { |v| Time.zone.parse(v) }
|
846
|
+
RRRSpec.convert_if_present(h, 'finished_at') { |v| Time.zone.parse(v) }
|
847
|
+
RRRSpec.convert_if_present(h, 'passed') { |v| v.to_i }
|
848
|
+
RRRSpec.convert_if_present(h, 'pending') { |v| v.to_i }
|
849
|
+
RRRSpec.convert_if_present(h, 'failed') { |v| v.to_i }
|
850
|
+
h
|
851
|
+
end
|
852
|
+
|
853
|
+
def to_json(options=nil)
|
854
|
+
to_h.to_json(options)
|
855
|
+
end
|
856
|
+
|
857
|
+
# ==========================================================================
|
858
|
+
# Persistence
|
859
|
+
|
860
|
+
def expire(sec)
|
861
|
+
RRRSpec.redis.expire(key, sec)
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
class Worker
|
866
|
+
attr_reader :key
|
867
|
+
|
868
|
+
def initialize(worker_key)
|
869
|
+
@key = worker_key
|
870
|
+
end
|
871
|
+
|
872
|
+
# Public: Create a new worker.
|
873
|
+
# The worker returned is **NOT** appeared in Worker.list.
|
874
|
+
def self.create(worker_type, hostname=RRRSpec.hostname)
|
875
|
+
worker_key = RRRSpec.make_key('rrrspec', 'worker', hostname)
|
876
|
+
RRRSpec.redis.hset(worker_key, 'worker_type', worker_type)
|
877
|
+
|
878
|
+
worker = new(worker_key)
|
879
|
+
return worker
|
880
|
+
end
|
881
|
+
|
882
|
+
# Public: A list of the workers which are possibly available.
|
883
|
+
#
|
884
|
+
# Returns an array of the workers
|
885
|
+
def self.list
|
886
|
+
RRRSpec.redis.smembers(RRRSpec.make_key('rrrspec', 'worker')).map do |key|
|
887
|
+
new(key)
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
# Public: Remove myself from the worker list.
|
892
|
+
def evict
|
893
|
+
RRRSpec.redis.srem(RRRSpec.make_key('rrrspec', 'worker'), key)
|
894
|
+
end
|
895
|
+
|
896
|
+
def ==(other)
|
897
|
+
@key == other.key
|
898
|
+
end
|
899
|
+
|
900
|
+
# ==========================================================================
|
901
|
+
# Property
|
902
|
+
|
903
|
+
# Public: The worker_type
|
904
|
+
def worker_type
|
905
|
+
RRRSpec.redis.hget(key, 'worker_type')
|
906
|
+
end
|
907
|
+
|
908
|
+
# ==========================================================================
|
909
|
+
# Taskset
|
910
|
+
|
911
|
+
# Public: Current taskset
|
912
|
+
#
|
913
|
+
# Returns a taskset or nil
|
914
|
+
def current_taskset
|
915
|
+
taskset_key = RRRSpec.redis.hget(key, 'taskset')
|
916
|
+
if taskset_key.present?
|
917
|
+
return Taskset.new(taskset_key)
|
918
|
+
else
|
919
|
+
nil
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
# Public: Update the current taskset
|
924
|
+
def update_current_taskset(taskset)
|
925
|
+
if taskset.present?
|
926
|
+
RRRSpec.redis.hset(key, 'taskset', taskset.key)
|
927
|
+
else
|
928
|
+
RRRSpec.redis.hset(key, 'taskset', nil)
|
929
|
+
end
|
930
|
+
end
|
931
|
+
|
932
|
+
# Public: Enqueue the taskset to the taskset_queue
|
933
|
+
def enqueue_taskset(taskset)
|
934
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'worker_queue'), taskset.key)
|
935
|
+
end
|
936
|
+
|
937
|
+
# Public: Dequeue the taskset from the taskset_queue
|
938
|
+
def dequeue_taskset
|
939
|
+
_, taskset_key = RRRSpec.redis.blpop(RRRSpec.make_key(key, 'worker_queue'), 0)
|
940
|
+
return Taskset.new(taskset_key)
|
941
|
+
end
|
942
|
+
|
943
|
+
# Public: Checks whether the taskset_queue is empty.
|
944
|
+
def queue_empty?
|
945
|
+
RRRSpec.redis.llen(RRRSpec.make_key(key, 'worker_queue')) == 0
|
946
|
+
end
|
947
|
+
|
948
|
+
# ==========================================================================
|
949
|
+
# Heartbeat
|
950
|
+
|
951
|
+
# Public: Check its existence with heartbeat key.
|
952
|
+
#
|
953
|
+
# Returns bool
|
954
|
+
def exist?
|
955
|
+
RRRSpec.redis.exists(RRRSpec.make_key(key, 'heartbeat'))
|
956
|
+
end
|
957
|
+
|
958
|
+
# Public: Maintain heartbeat
|
959
|
+
def heartbeat(time)
|
960
|
+
RRRSpec.redis.setex(RRRSpec.make_key(key, 'heartbeat'), time, "alive")
|
961
|
+
RRRSpec.redis.sadd(RRRSpec.make_key('rrrspec', 'worker'), key)
|
962
|
+
end
|
963
|
+
end
|
964
|
+
|
965
|
+
class Slave
|
966
|
+
attr_reader :key
|
967
|
+
|
968
|
+
def initialize(slave_key)
|
969
|
+
@key = slave_key
|
970
|
+
end
|
971
|
+
|
972
|
+
def self.create
|
973
|
+
slave_key = RRRSpec.make_key('rrrspec', 'worker', RRRSpec.hostname, 'slave', Process.getpgrp)
|
974
|
+
slave = new(slave_key)
|
975
|
+
return slave
|
976
|
+
end
|
977
|
+
|
978
|
+
def self.build_from_pid(pid)
|
979
|
+
slave_key = RRRSpec.make_key('rrrspec', 'worker', RRRSpec.hostname, 'slave', pid)
|
980
|
+
return new(slave_key)
|
981
|
+
end
|
982
|
+
|
983
|
+
# ==========================================================================
|
984
|
+
# Status
|
985
|
+
|
986
|
+
# Public: Returns the trials of the slave.
|
987
|
+
# The return value should be sorted in the order added.
|
988
|
+
#
|
989
|
+
# Returns an array of the Trials
|
990
|
+
def trials
|
991
|
+
RRRSpec.redis.lrange(RRRSpec.make_key(key, 'trial'), 0, -1).map do |key|
|
992
|
+
Trial.new(key)
|
993
|
+
end
|
994
|
+
end
|
995
|
+
|
996
|
+
# Public: Add trial to the list of the trials that the slave worked for.
|
997
|
+
def add_trial(trial)
|
998
|
+
RRRSpec.redis.rpush(RRRSpec.make_key(key, 'trial'),
|
999
|
+
trial.key)
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
# ==========================================================================
|
1003
|
+
# Status
|
1004
|
+
|
1005
|
+
# Public: Current status
|
1006
|
+
#
|
1007
|
+
# Returns either nil, "normal_exit", "timeout_exit" or "failure_exit"
|
1008
|
+
def status
|
1009
|
+
RRRSpec.redis.hget(key, 'status')
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
# Public: Update the status. It should be one of:
|
1013
|
+
# ["normal_exit", "timeout_exit", "failure_exit"]
|
1014
|
+
def update_status(status)
|
1015
|
+
RRRSpec.redis.hset(key, 'status', status)
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
# Public: Execution log of the slave
|
1019
|
+
def log
|
1020
|
+
RRRSpec.redis.get(RRRSpec.make_key(key, 'log')) || ""
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
# Public: Append a line to the worker_log
|
1024
|
+
def append_log(string)
|
1025
|
+
RRRSpec.redis.append(RRRSpec.make_key(key, 'log'), string)
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
# ==========================================================================
|
1029
|
+
# Heartbeat
|
1030
|
+
|
1031
|
+
# Public: Check its existence with heartbeat key.
|
1032
|
+
#
|
1033
|
+
# Returns bool
|
1034
|
+
def exist?
|
1035
|
+
RRRSpec.redis.exists(RRRSpec.make_key(key, 'heartbeat'))
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
# Public: Maintain heartbeat
|
1039
|
+
def heartbeat(time)
|
1040
|
+
RRRSpec.redis.setex(RRRSpec.make_key(key, 'heartbeat'), time, "alive")
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
# ==========================================================================
|
1044
|
+
# Serialize
|
1045
|
+
|
1046
|
+
def to_h
|
1047
|
+
h = RRRSpec.redis.hgetall(key)
|
1048
|
+
h['trials'] = trials.map { |trial| { 'key' => trial.key } }
|
1049
|
+
h['key'] = key
|
1050
|
+
h['log'] = log
|
1051
|
+
h
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
def to_json(options=nil)
|
1055
|
+
to_h.to_json(options)
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
# ==========================================================================
|
1059
|
+
# Persistence
|
1060
|
+
|
1061
|
+
def expire(sec)
|
1062
|
+
RRRSpec.redis.expire(key, sec)
|
1063
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'trial'), sec)
|
1064
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'log'), sec)
|
1065
|
+
RRRSpec.redis.expire(RRRSpec.make_key(key, 'heartbeat'), sec)
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
module TasksetEstimation
|
1070
|
+
# Public: Return the cache on the estimated execution time of the specs.
|
1071
|
+
#
|
1072
|
+
# Returns a hash of spec_file to estimate_sec
|
1073
|
+
def self.estimate_secs(taskset_class)
|
1074
|
+
h = RRRSpec.redis.hgetall(RRRSpec.make_key('rrrspec', 'estimate_sec', taskset_class))
|
1075
|
+
estimate_secs = {}
|
1076
|
+
h.each do |spec_file, estimate_sec|
|
1077
|
+
estimate_secs[spec_file] = estimate_sec.to_i
|
1078
|
+
end
|
1079
|
+
return estimate_secs
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
# Public: Update the estimation.
|
1083
|
+
#
|
1084
|
+
# The estimation argument should be a hash like {"spec_file" => 20}.
|
1085
|
+
def self.update_estimate_secs(taskset_class, estimation)
|
1086
|
+
return if estimation.empty?
|
1087
|
+
key = RRRSpec.make_key('rrrspec', 'estimate_sec', taskset_class)
|
1088
|
+
RRRSpec.redis.hmset(key, *estimation.to_a.flatten)
|
1089
|
+
end
|
1090
|
+
end
|
1091
|
+
end
|