rrrspec-server 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/rrrspec-server +4 -0
- data/db/migrate/20131105050718_create_tables.rb +77 -0
- data/db/schema.rb +94 -0
- data/lib/rrrspec/server/arbiter.rb +186 -0
- data/lib/rrrspec/server/cli.rb +107 -0
- data/lib/rrrspec/server/configuration.rb +54 -0
- data/lib/rrrspec/server/dispatcher.rb +53 -0
- data/lib/rrrspec/server/persistent_models.rb +111 -0
- data/lib/rrrspec/server/persister.rb +147 -0
- data/lib/rrrspec/server/version.rb +5 -0
- data/lib/rrrspec/server/worker_runner.rb +245 -0
- data/lib/rrrspec/server.rb +9 -0
- data/rrrspec-server.gemspec +39 -0
- data/spec/fixture.rb +32 -0
- data/spec/rrrspec/server/arbiter_spec.rb +497 -0
- data/spec/rrrspec/server/dispatcher_spec.rb +53 -0
- data/spec/rrrspec/server/persistent_models_spec.rb +54 -0
- data/spec/rrrspec/server/persister_spec.rb +121 -0
- data/spec/spec_helper.rb +75 -0
- data/tasks/db.rake +62 -0
- metadata +256 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ffdf010821a66bdb732d362cfd9d3c33acf9e6ad
|
4
|
+
data.tar.gz: 06a8ab51406619a53ffa3131c8a356d630b6e2ec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3b86f71da951714df9558c4918e6bcf36b29815d97aeb907e71212762566f089baaf6e6fcf514df6e3569c8bdec17b11e50690c0df3d803eb4079805132c45d1
|
7
|
+
data.tar.gz: 3c0d6cd34faa4fc5b2853775c890053e27761f5d7e1f9eed510323b487e33206c82b983ce93b07d53e7ab8a1b5c4bf1b12565ea0f45adb29f51b151e72d8076d
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/rrrspec-server
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
class CreateTables < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table(:tasksets) do |t|
|
4
|
+
t.string :key
|
5
|
+
t.string :rsync_name
|
6
|
+
t.text :setup_command
|
7
|
+
t.text :slave_command
|
8
|
+
t.string :worker_type
|
9
|
+
t.integer :max_workers
|
10
|
+
t.integer :max_trials
|
11
|
+
t.string :taskset_class
|
12
|
+
t.integer :unknown_spec_timeout_sec
|
13
|
+
t.integer :least_timeout_sec
|
14
|
+
t.datetime :created_at
|
15
|
+
|
16
|
+
t.string :status
|
17
|
+
t.datetime :finished_at
|
18
|
+
t.text :log
|
19
|
+
end
|
20
|
+
add_index :tasksets, :key
|
21
|
+
add_index :tasksets, :rsync_name
|
22
|
+
add_index :tasksets, :taskset_class
|
23
|
+
add_index :tasksets, :created_at
|
24
|
+
add_index :tasksets, :status
|
25
|
+
|
26
|
+
create_table(:tasks) do |t|
|
27
|
+
t.string :key
|
28
|
+
t.references :taskset
|
29
|
+
t.string :status
|
30
|
+
t.integer :estimate_sec
|
31
|
+
t.string :spec_file
|
32
|
+
end
|
33
|
+
add_index :tasks, :key
|
34
|
+
add_index :tasks, :taskset_id
|
35
|
+
add_index :tasks, :status
|
36
|
+
|
37
|
+
create_table(:trials) do |t|
|
38
|
+
t.string :key
|
39
|
+
t.references :task
|
40
|
+
t.references :slave
|
41
|
+
t.datetime :started_at
|
42
|
+
t.datetime :finished_at
|
43
|
+
t.string :status
|
44
|
+
t.text :stdout
|
45
|
+
t.text :stderr
|
46
|
+
t.integer :passed
|
47
|
+
t.integer :pending
|
48
|
+
t.integer :failed
|
49
|
+
end
|
50
|
+
add_index :trials, :key
|
51
|
+
add_index :trials, :task_id
|
52
|
+
add_index :trials, :slave_id
|
53
|
+
|
54
|
+
create_table(:worker_logs) do |t|
|
55
|
+
t.string :key
|
56
|
+
t.string :worker_key
|
57
|
+
t.references :taskset
|
58
|
+
t.datetime :started_at
|
59
|
+
t.datetime :rsync_finished_at
|
60
|
+
t.datetime :setup_finished_at
|
61
|
+
t.datetime :finished_at
|
62
|
+
t.text :log
|
63
|
+
end
|
64
|
+
add_index :worker_logs, :key
|
65
|
+
add_index :worker_logs, :worker_key
|
66
|
+
add_index :worker_logs, :taskset_id
|
67
|
+
|
68
|
+
create_table(:slaves) do |t|
|
69
|
+
t.string :key
|
70
|
+
t.references :taskset
|
71
|
+
t.string :status
|
72
|
+
t.text :log
|
73
|
+
end
|
74
|
+
add_index :slaves, :key
|
75
|
+
add_index :slaves, :taskset_id
|
76
|
+
end
|
77
|
+
end
|
data/db/schema.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# This file is auto-generated from the current state of the database. Instead
|
3
|
+
# of editing this file, please use the migrations feature of Active Record to
|
4
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
5
|
+
#
|
6
|
+
# Note that this schema.rb definition is the authoritative source for your
|
7
|
+
# database schema. If you need to create the application database on another
|
8
|
+
# system, you should be using db:schema:load, not running all the migrations
|
9
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
10
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
11
|
+
#
|
12
|
+
# It's strongly recommended to check this file into your version control system.
|
13
|
+
|
14
|
+
ActiveRecord::Schema.define(:version => 20131105050718) do
|
15
|
+
|
16
|
+
create_table "slaves", :force => true do |t|
|
17
|
+
t.string "key"
|
18
|
+
t.integer "taskset_id"
|
19
|
+
t.string "status"
|
20
|
+
t.text "log"
|
21
|
+
end
|
22
|
+
|
23
|
+
add_index "slaves", ["key"], :name => "index_slaves_on_key"
|
24
|
+
add_index "slaves", ["taskset_id"], :name => "index_slaves_on_taskset_id"
|
25
|
+
|
26
|
+
create_table "tasks", :force => true do |t|
|
27
|
+
t.string "key"
|
28
|
+
t.integer "taskset_id"
|
29
|
+
t.string "status"
|
30
|
+
t.integer "estimate_sec"
|
31
|
+
t.string "spec_file"
|
32
|
+
end
|
33
|
+
|
34
|
+
add_index "tasks", ["key"], :name => "index_tasks_on_key"
|
35
|
+
add_index "tasks", ["status"], :name => "index_tasks_on_status"
|
36
|
+
add_index "tasks", ["taskset_id"], :name => "index_tasks_on_taskset_id"
|
37
|
+
|
38
|
+
create_table "tasksets", :force => true do |t|
|
39
|
+
t.string "key"
|
40
|
+
t.string "rsync_name"
|
41
|
+
t.text "setup_command"
|
42
|
+
t.text "slave_command"
|
43
|
+
t.string "worker_type"
|
44
|
+
t.integer "max_workers"
|
45
|
+
t.integer "max_trials"
|
46
|
+
t.string "taskset_class"
|
47
|
+
t.integer "unknown_spec_timeout_sec"
|
48
|
+
t.integer "least_timeout_sec"
|
49
|
+
t.datetime "created_at"
|
50
|
+
t.string "status"
|
51
|
+
t.datetime "finished_at"
|
52
|
+
t.text "log"
|
53
|
+
end
|
54
|
+
|
55
|
+
add_index "tasksets", ["created_at"], :name => "index_tasksets_on_created_at"
|
56
|
+
add_index "tasksets", ["key"], :name => "index_tasksets_on_key"
|
57
|
+
add_index "tasksets", ["rsync_name"], :name => "index_tasksets_on_rsync_name"
|
58
|
+
add_index "tasksets", ["status"], :name => "index_tasksets_on_status"
|
59
|
+
add_index "tasksets", ["taskset_class"], :name => "index_tasksets_on_taskset_class"
|
60
|
+
|
61
|
+
create_table "trials", :force => true do |t|
|
62
|
+
t.string "key"
|
63
|
+
t.integer "task_id"
|
64
|
+
t.integer "slave_id"
|
65
|
+
t.datetime "started_at"
|
66
|
+
t.datetime "finished_at"
|
67
|
+
t.string "status"
|
68
|
+
t.text "stdout"
|
69
|
+
t.text "stderr"
|
70
|
+
t.integer "passed"
|
71
|
+
t.integer "pending"
|
72
|
+
t.integer "failed"
|
73
|
+
end
|
74
|
+
|
75
|
+
add_index "trials", ["key"], :name => "index_trials_on_key"
|
76
|
+
add_index "trials", ["slave_id"], :name => "index_trials_on_slave_id"
|
77
|
+
add_index "trials", ["task_id"], :name => "index_trials_on_task_id"
|
78
|
+
|
79
|
+
create_table "worker_logs", :force => true do |t|
|
80
|
+
t.string "key"
|
81
|
+
t.string "worker_key"
|
82
|
+
t.integer "taskset_id"
|
83
|
+
t.datetime "started_at"
|
84
|
+
t.datetime "rsync_finished_at"
|
85
|
+
t.datetime "setup_finished_at"
|
86
|
+
t.datetime "finished_at"
|
87
|
+
t.text "log"
|
88
|
+
end
|
89
|
+
|
90
|
+
add_index "worker_logs", ["key"], :name => "index_worker_logs_on_key"
|
91
|
+
add_index "worker_logs", ["taskset_id"], :name => "index_worker_logs_on_taskset_id"
|
92
|
+
add_index "worker_logs", ["worker_key"], :name => "index_worker_logs_on_worker_key"
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module RRRSpec
|
2
|
+
module Server
|
3
|
+
module Arbiter
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def work_loop
|
7
|
+
loop { work }
|
8
|
+
end
|
9
|
+
|
10
|
+
def work
|
11
|
+
command, arg = ArbiterQueue.dequeue
|
12
|
+
case command
|
13
|
+
when 'cancel'
|
14
|
+
cancel(arg)
|
15
|
+
when 'check'
|
16
|
+
check(arg)
|
17
|
+
when 'fail'
|
18
|
+
fail(arg)
|
19
|
+
when 'trial'
|
20
|
+
trial(arg)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
module_function
|
26
|
+
|
27
|
+
def cancel(taskset)
|
28
|
+
logger = TimedLogger.new(taskset)
|
29
|
+
logger.write("Cancel requested")
|
30
|
+
|
31
|
+
if [nil, 'running'].include?(taskset.status)
|
32
|
+
taskset.update_status('cancelled')
|
33
|
+
taskset.set_finished_time
|
34
|
+
taskset.clear_queue
|
35
|
+
end
|
36
|
+
PersisterQueue.enqueue(taskset)
|
37
|
+
ActiveTaskset.remove(taskset)
|
38
|
+
end
|
39
|
+
|
40
|
+
def check(taskset)
|
41
|
+
logger = TimedLogger.new(taskset)
|
42
|
+
logger.write("Check requested")
|
43
|
+
|
44
|
+
unless taskset.status == 'running'
|
45
|
+
PersisterQueue.enqueue(taskset)
|
46
|
+
ActiveTaskset.remove(taskset)
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
tasks_left = taskset.tasks_left
|
51
|
+
tasks_left.each do |task|
|
52
|
+
check_task(logger, taskset, task)
|
53
|
+
end
|
54
|
+
|
55
|
+
tasks_left = taskset.tasks_left
|
56
|
+
if tasks_left.empty?
|
57
|
+
if taskset.failed_count == 0
|
58
|
+
taskset.update_status('succeeded')
|
59
|
+
else
|
60
|
+
taskset.update_status('failed')
|
61
|
+
end
|
62
|
+
taskset.set_finished_time
|
63
|
+
PersisterQueue.enqueue(taskset)
|
64
|
+
ActiveTaskset.remove(taskset)
|
65
|
+
elsif taskset.queue_empty?
|
66
|
+
requeue_speculative(logger, taskset, tasks_left)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def check_task(logger, taskset, task)
|
71
|
+
running_trial = []
|
72
|
+
passed_trial = []
|
73
|
+
pending_trial = []
|
74
|
+
failed_trial = []
|
75
|
+
trials = task.trials
|
76
|
+
trials.each do |trial|
|
77
|
+
case trial.status
|
78
|
+
when nil, ''
|
79
|
+
if trial.slave.exist?
|
80
|
+
running_trial << trial
|
81
|
+
else
|
82
|
+
trial.finish('error', '', 'Failed by Arbiter', nil, nil, nil)
|
83
|
+
failed_trial << trial
|
84
|
+
end
|
85
|
+
when 'passed'
|
86
|
+
passed_trial << trial
|
87
|
+
when 'pending'
|
88
|
+
pending_trial << trial
|
89
|
+
when 'failed', 'error', 'timeout'
|
90
|
+
failed_trial << trial
|
91
|
+
end
|
92
|
+
end
|
93
|
+
case
|
94
|
+
when !passed_trial.empty?
|
95
|
+
task.update_status('passed')
|
96
|
+
taskset.incr_succeeded_count
|
97
|
+
taskset.finish_task(task)
|
98
|
+
when !pending_trial.empty?
|
99
|
+
task.update_status('pending')
|
100
|
+
taskset.incr_succeeded_count
|
101
|
+
taskset.finish_task(task)
|
102
|
+
when !running_trial.empty?
|
103
|
+
# Do nothing
|
104
|
+
when failed_trial.size >= taskset.max_trials
|
105
|
+
logger.write("Mark failed #{task.key}")
|
106
|
+
task.update_status('failed')
|
107
|
+
taskset.incr_failed_count
|
108
|
+
taskset.finish_task(task)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def requeue_speculative(logger, taskset, tasks_left)
|
113
|
+
running_tasks = Hash.new { |hash,key| hash[key] = [] }
|
114
|
+
not_running_tasks = []
|
115
|
+
tasks_left.each do |task|
|
116
|
+
trials = task.trials
|
117
|
+
if trials.any? { |trial| trial.status.blank? }
|
118
|
+
running_tasks[trials.size] << task
|
119
|
+
else
|
120
|
+
not_running_tasks << task
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if not_running_tasks.empty?
|
125
|
+
task = running_tasks[running_tasks.keys.min].sample
|
126
|
+
logger.write("Speculatively enqueue the task #{task.key}")
|
127
|
+
taskset.enqueue_task(task)
|
128
|
+
else
|
129
|
+
task = not_running_tasks.sample
|
130
|
+
logger.write("Enqueue the task #{task.key}")
|
131
|
+
taskset.enqueue_task(task)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def fail(taskset)
|
136
|
+
logger = TimedLogger.new(taskset)
|
137
|
+
logger.write("Fail requested")
|
138
|
+
|
139
|
+
if [nil, 'running'].include?(taskset.status)
|
140
|
+
taskset.update_status('failed')
|
141
|
+
taskset.set_finished_time
|
142
|
+
taskset.clear_queue
|
143
|
+
end
|
144
|
+
PersisterQueue.enqueue(taskset)
|
145
|
+
ActiveTaskset.remove(taskset)
|
146
|
+
end
|
147
|
+
|
148
|
+
def trial(trial)
|
149
|
+
task = trial.task
|
150
|
+
return if task.status.present?
|
151
|
+
taskset = task.taskset
|
152
|
+
logger = TimedLogger.new(taskset)
|
153
|
+
|
154
|
+
if trial.status == nil
|
155
|
+
trial.finish('error', '', 'Failed by Arbiter', nil, nil, nil)
|
156
|
+
end
|
157
|
+
|
158
|
+
case trial.status
|
159
|
+
when 'passed'
|
160
|
+
task.update_status('passed')
|
161
|
+
taskset.incr_succeeded_count
|
162
|
+
taskset.finish_task(task)
|
163
|
+
when 'pending'
|
164
|
+
task.update_status('pending')
|
165
|
+
taskset.incr_succeeded_count
|
166
|
+
taskset.finish_task(task)
|
167
|
+
when 'failed', 'error', 'timeout'
|
168
|
+
trials = task.trials
|
169
|
+
finished_trials = []
|
170
|
+
trials.each do |trial|
|
171
|
+
finished_trials << trial unless [nil, ''].include?(trial.status)
|
172
|
+
end
|
173
|
+
if finished_trials.size >= taskset.max_trials
|
174
|
+
task.update_status('failed')
|
175
|
+
taskset.incr_failed_count
|
176
|
+
taskset.finish_task(task)
|
177
|
+
elsif trials.size < taskset.max_trials
|
178
|
+
task.update_status(nil)
|
179
|
+
logger.write("Enqueue the task #{task.key}")
|
180
|
+
taskset.reversed_enqueue_task(task)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'rrrspec/server'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
module RRRSpec
|
5
|
+
module Server
|
6
|
+
class CLI < Thor
|
7
|
+
package_name 'RRRSpec'
|
8
|
+
default_command 'help'
|
9
|
+
class_option :config, aliases: '-c', type: :string, default: ''
|
10
|
+
|
11
|
+
no_commands do
|
12
|
+
def setup(conf)
|
13
|
+
RRRSpec.setup(conf, options[:config].split(':'))
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_exception
|
17
|
+
yield
|
18
|
+
rescue
|
19
|
+
RRRSpec.logger.error($!)
|
20
|
+
raise
|
21
|
+
end
|
22
|
+
|
23
|
+
def auto_rebirth
|
24
|
+
Signal.trap('TERM') do
|
25
|
+
Signal.trap('TERM', 'DEFAULT')
|
26
|
+
Process.kill('TERM', 0)
|
27
|
+
end
|
28
|
+
loop do
|
29
|
+
pid = Process.fork do
|
30
|
+
log_exception { yield }
|
31
|
+
end
|
32
|
+
Process.waitpid(pid)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def daemonize
|
37
|
+
return unless options[:daemonize]
|
38
|
+
pidfile = options[:pidfile]
|
39
|
+
|
40
|
+
if pidfile
|
41
|
+
pidfile = File.absolute_path(pidfile)
|
42
|
+
if File.exists?(pidfile)
|
43
|
+
pid = open(pidfile, 'r') { |f| f.read.strip }
|
44
|
+
if pid
|
45
|
+
begin
|
46
|
+
if Process.kill(0, pid.to_i) == 1
|
47
|
+
$stderr.puts "Daemon process already exists: #{pid}"
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
rescue Errno::ESRCH
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Process.daemon
|
57
|
+
|
58
|
+
if pidfile
|
59
|
+
open(pidfile, 'w') { |f| f.write(Process.pid.to_s) }
|
60
|
+
|
61
|
+
parent_pid = Process.pid
|
62
|
+
at_exit do
|
63
|
+
if Process.pid == parent_pid
|
64
|
+
File.delete(pidfile)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
method_option :daemonize, type: :boolean
|
72
|
+
method_option :pidfile, type: :string
|
73
|
+
desc 'server', 'Run RRRSpec as a server'
|
74
|
+
def server
|
75
|
+
$0 = 'rrrspec server'
|
76
|
+
setup(ServerConfiguration.new)
|
77
|
+
daemonize
|
78
|
+
auto_rebirth do
|
79
|
+
ActiveRecord::Base.establish_connection(**RRRSpec.configuration.persistence_db)
|
80
|
+
Thread.abort_on_exception = true
|
81
|
+
Thread.fork { RRRSpec.pacemaker(RSyncInfo, 60, 5) }
|
82
|
+
Thread.fork { Dispatcher.work_loop }
|
83
|
+
Thread.fork { Arbiter.work_loop }
|
84
|
+
Thread.fork { Persister.work_loop }
|
85
|
+
Kernel.sleep
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
method_option :daemonize, type: :boolean
|
90
|
+
method_option :pidfile, type: :string
|
91
|
+
desc 'worker', 'Run RRRSpec as a worker'
|
92
|
+
def worker
|
93
|
+
$0 = 'rrrspec worker'
|
94
|
+
setup(WorkerConfiguration.new)
|
95
|
+
daemonize
|
96
|
+
auto_rebirth do
|
97
|
+
worker = Worker.create(RRRSpec.configuration.worker_type)
|
98
|
+
worker_runner = WorkerRunner.new(worker)
|
99
|
+
Thread.abort_on_exception = true
|
100
|
+
Thread.fork { RRRSpec.pacemaker(worker, 60, 5) }
|
101
|
+
Thread.fork { worker_runner.work_loop }
|
102
|
+
Kernel.sleep
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'facter'
|
2
|
+
|
3
|
+
module RRRSpec
|
4
|
+
module Server
|
5
|
+
class ServerConfiguration < Configuration
|
6
|
+
attr_accessor :rsync_server, :rsync_dir, :rsync_options
|
7
|
+
attr_accessor :persistence_db
|
8
|
+
attr_accessor :json_cache_path
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super()
|
12
|
+
@type = :server
|
13
|
+
end
|
14
|
+
|
15
|
+
def check_validity
|
16
|
+
validity = super
|
17
|
+
|
18
|
+
unless rsync_server and rsync_options and rsync_dir
|
19
|
+
$stderr.puts('The rsync options are not set')
|
20
|
+
validity = false
|
21
|
+
end
|
22
|
+
|
23
|
+
unless persistence_db
|
24
|
+
$stderr.puts('The database options are not set')
|
25
|
+
validity = false
|
26
|
+
end
|
27
|
+
|
28
|
+
validity
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class WorkerConfiguration < Configuration
|
33
|
+
attr_accessor :working_dir, :worker_type, :slave_processes
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
super()
|
37
|
+
@slave_processes = Facter.processorcount.to_i
|
38
|
+
@worker_type = 'default'
|
39
|
+
@type = :worker
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_validity
|
43
|
+
validity = super
|
44
|
+
|
45
|
+
unless working_dir and worker_type
|
46
|
+
$stderr.puts('The worker options are not set')
|
47
|
+
validity = false
|
48
|
+
end
|
49
|
+
|
50
|
+
validity
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module RRRSpec
|
2
|
+
module Server
|
3
|
+
class Dispatcher
|
4
|
+
def self.work
|
5
|
+
assigned = Hash.new { |hash,key| hash[key] = [] }
|
6
|
+
unemployed = Hash.new { |hash,key| hash[key] = [] }
|
7
|
+
Worker.list.each do |worker|
|
8
|
+
unless worker.exist?
|
9
|
+
worker.evict
|
10
|
+
else
|
11
|
+
taskset = worker.current_taskset
|
12
|
+
if taskset
|
13
|
+
assigned[taskset.key] << worker
|
14
|
+
else
|
15
|
+
unemployed[worker.worker_type] << worker
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveTaskset.list.each do |taskset|
|
21
|
+
should_mark_running = false
|
22
|
+
case taskset.status
|
23
|
+
when 'succeeded', 'cancelled', 'failed'
|
24
|
+
next
|
25
|
+
when nil
|
26
|
+
should_mark_running = true
|
27
|
+
end
|
28
|
+
|
29
|
+
# Cache the values
|
30
|
+
max_workers = taskset.max_workers
|
31
|
+
worker_type = taskset.worker_type
|
32
|
+
while max_workers > assigned[taskset.key].size
|
33
|
+
break if unemployed[worker_type].empty?
|
34
|
+
worker = unemployed[worker_type].pop
|
35
|
+
if should_mark_running
|
36
|
+
taskset.update_status('running')
|
37
|
+
should_mark_running = false
|
38
|
+
end
|
39
|
+
worker.enqueue_taskset(taskset)
|
40
|
+
assigned[taskset.key] << worker
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.work_loop
|
46
|
+
loop do
|
47
|
+
DispatcherQueue.wait
|
48
|
+
work
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|