drbqs 0.0.17 → 0.0.18
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.
- data/docs/FormatExecute.md +44 -2
- data/example/group/execute.rb +19 -0
- data/example/group/server.rb +27 -0
- data/example/group/sum.rb +9 -0
- data/example/mandelbrot/README.md +8 -0
- data/example/mandelbrot/execute.rb +4 -0
- data/lib/drbqs/command_line/command_node.rb +1 -0
- data/lib/drbqs/execute/execute_node.rb +4 -21
- data/lib/drbqs/node/connection.rb +1 -2
- data/lib/drbqs/node/node.rb +163 -102
- data/lib/drbqs/node/state.rb +100 -35
- data/lib/drbqs/node/task_client.rb +46 -33
- data/lib/drbqs/server/message.rb +13 -7
- data/lib/drbqs/server/server.rb +57 -29
- data/lib/drbqs/server/server_hook.rb +19 -5
- data/lib/drbqs/server/test/node.rb +31 -6
- data/lib/drbqs/setting/node.rb +11 -2
- data/lib/drbqs/setting/server.rb +1 -1
- data/lib/drbqs/task/task.rb +26 -6
- data/lib/drbqs/task/task_generator.rb +2 -1
- data/lib/drbqs/utility/temporary.rb +27 -6
- data/lib/drbqs/utility/transfer/transfer_client.rb +10 -12
- data/lib/drbqs/version.rb +1 -1
- data/lib/drbqs/worker.rb +2 -0
- data/lib/drbqs/worker/forked_process.rb +100 -0
- data/lib/drbqs/worker/serialize.rb +66 -0
- data/lib/drbqs/worker/worker.rb +133 -0
- data/lib/drbqs/worker/worker_process_set.rb +219 -0
- data/spec/integration_test/01_basic_usage_spec.rb +3 -2
- data/spec/integration_test/06_node_exit_after_task_spec.rb +3 -2
- data/spec/integration_test/07_command_server_with_node_spec.rb +1 -0
- data/spec/integration_test/08_shutdown_unused_nodes_spec.rb +3 -2
- data/spec/integration_test/10_test_server_spec.rb +2 -2
- data/spec/integration_test/11_special_tasks_spec.rb +61 -0
- data/spec/integration_test/12_multiple_workers_spec.rb +43 -0
- data/spec/integration_test/definition/task_obj_definition.rb +33 -6
- data/spec/node/connection_spec.rb +6 -6
- data/spec/node/node_spec.rb +10 -2
- data/spec/node/state_spec.rb +146 -62
- data/spec/node/task_client_spec.rb +58 -53
- data/spec/server/message_spec.rb +10 -6
- data/spec/server/queue_spec.rb +7 -4
- data/spec/server/server_hook_spec.rb +28 -1
- data/spec/task/task_spec.rb +43 -6
- data/spec/utility/temporary_spec.rb +32 -9
- data/spec/worker/forked_process_spec.rb +66 -0
- data/spec/worker/serialize_spec.rb +73 -0
- data/spec/worker/worker_process_set_spec.rb +104 -0
- data/spec/worker/worker_spec.rb +127 -0
- metadata +34 -19
@@ -26,15 +26,15 @@ module DRbQS
|
|
26
26
|
end
|
27
27
|
private :create_proc_name
|
28
28
|
|
29
|
-
def add(key,
|
29
|
+
def add(key, opts = {}, &block)
|
30
30
|
unless block_given?
|
31
31
|
raise ArgumentError, "The main part of hook must be specified as a block."
|
32
32
|
end
|
33
33
|
if (n = @argument_number[key]) && (block.arity != n)
|
34
34
|
raise ArgumentError, "Invalid argument number of hook of #{key.inspect}."
|
35
35
|
end
|
36
|
-
name
|
37
|
-
@hook[key] << [name, block]
|
36
|
+
name = opts[:name] || create_proc_name(key)
|
37
|
+
@hook[key] << [name, block, opts[:repeat]]
|
38
38
|
name
|
39
39
|
end
|
40
40
|
|
@@ -68,10 +68,24 @@ module DRbQS
|
|
68
68
|
@hook[key].map { |a| a[0] }
|
69
69
|
end
|
70
70
|
|
71
|
+
def delete_unused_hook
|
72
|
+
@hook.keys.each do |key|
|
73
|
+
@hook[key].delete_if do |ary|
|
74
|
+
ary[2] && ary[2] == 0
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
private :delete_unused_hook
|
79
|
+
|
71
80
|
def exec(key, *args, &cond)
|
81
|
+
delete_unused_hook
|
72
82
|
@hook[key].each do |ary|
|
73
|
-
|
74
|
-
|
83
|
+
name, proc, repeat = ary
|
84
|
+
if !cond || cond.call(name)
|
85
|
+
if !repeat || repeat > 0
|
86
|
+
proc.call(*args)
|
87
|
+
ary[2] -= 1 if repeat
|
88
|
+
end
|
75
89
|
else
|
76
90
|
return nil
|
77
91
|
end
|
@@ -6,26 +6,51 @@ module DRbQS
|
|
6
6
|
def initialize(log_level, transfer, queue)
|
7
7
|
super(nil, :log_file => $stdout, :log_level => log_level)
|
8
8
|
DRbQS::Transfer::Client.set(transfer.get_client(true)) if transfer
|
9
|
-
@task_client = DRbQS::Node::TaskClient.new(nil, queue, nil)
|
9
|
+
@task_client = DRbQS::Node::TaskClient.new(nil, queue, nil, [DRbQS::Node::SAME_HOST_GROUP], 1)
|
10
|
+
@special_task_number = 0
|
10
11
|
end
|
11
12
|
|
12
13
|
def server_on_same_host?
|
13
14
|
true
|
14
15
|
end
|
15
16
|
|
17
|
+
def subdirectory_name(task_id)
|
18
|
+
if task_id
|
19
|
+
sprintf("T%08d", task_id)
|
20
|
+
else
|
21
|
+
sprintf("S%08d", (@special_task_number += 1))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
private :subdirectory_name
|
25
|
+
|
26
|
+
def execute_task(task_id, marshal_obj, method_sym, args)
|
27
|
+
DRbQS::Temporary.set_sub_directory(subdirectory_name(task_id))
|
28
|
+
result = DRbQS::Task.execute_task(marshal_obj, method_sym, args)
|
29
|
+
if files = DRbQS::Transfer.dequeue_all
|
30
|
+
transfer_file(files)
|
31
|
+
end
|
32
|
+
if subdir = DRbQS::Temporary.subdirectory
|
33
|
+
FileUtils.rm_r(subdir)
|
34
|
+
end
|
35
|
+
result
|
36
|
+
end
|
37
|
+
private :execute_task
|
38
|
+
|
16
39
|
def calc
|
17
40
|
if ary = @task_client.get_task
|
18
41
|
task_id, marshal_obj, method_sym, args = ary
|
19
|
-
result = execute_task(marshal_obj, method_sym, args)
|
42
|
+
result = execute_task(task_id, marshal_obj, method_sym, args)
|
20
43
|
return [task_id, result]
|
21
44
|
end
|
22
45
|
nil
|
23
46
|
end
|
24
47
|
|
25
|
-
def finalize(
|
26
|
-
if
|
27
|
-
|
28
|
-
|
48
|
+
def finalize(finalization_task_ary)
|
49
|
+
if finalization_task_ary
|
50
|
+
finalization_task_ary.each do |task|
|
51
|
+
args = task.simple_drb_args
|
52
|
+
execute_task(nil, *args)
|
53
|
+
end
|
29
54
|
end
|
30
55
|
clear_node_files
|
31
56
|
end
|
data/lib/drbqs/setting/node.rb
CHANGED
@@ -7,6 +7,7 @@ module DRbQS
|
|
7
7
|
super(:all_keys_defined => true, :log_level => true, :daemon => true) do
|
8
8
|
register_key(:load, :check => [:>, 0], :add => true)
|
9
9
|
register_key(:process, :check => 1, :default => [1])
|
10
|
+
register_key(:group, :add => true)
|
10
11
|
register_key(:loadavg, :check => 1)
|
11
12
|
register_key(:log_prefix, :check => 1, :default => [LOG_PREFIX_DEFAULT])
|
12
13
|
register_key(:log_stdout, :bool => true)
|
@@ -28,6 +29,7 @@ module DRbQS
|
|
28
29
|
def parse!
|
29
30
|
super
|
30
31
|
parse_load
|
32
|
+
parse_group
|
31
33
|
parse_loadavg
|
32
34
|
if !get(:log_stdout)
|
33
35
|
@options[:log_prefix] = get_first(:log_prefix) do |val|
|
@@ -49,7 +51,7 @@ module DRbQS
|
|
49
51
|
private :parse_load
|
50
52
|
|
51
53
|
def parse_loadavg
|
52
|
-
@options[:node_opts]
|
54
|
+
@options[:node_opts] ||= {}
|
53
55
|
if args = get(:loadavg)
|
54
56
|
max_loadavg, sleep_time = args[0].split(':', -1)
|
55
57
|
@options[:node_opts][:max_loadavg] = max_loadavg && max_loadavg.size > 0 ? max_loadavg.to_f : nil
|
@@ -58,6 +60,14 @@ module DRbQS
|
|
58
60
|
end
|
59
61
|
private :parse_loadavg
|
60
62
|
|
63
|
+
def parse_group
|
64
|
+
if args = get(:group)
|
65
|
+
@options[:node_opts] ||= {}
|
66
|
+
@options[:node_opts][:group] = args.map { |a| a.intern }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
private :parse_group
|
70
|
+
|
61
71
|
def exec(io = nil)
|
62
72
|
return true if exec_as_daemon
|
63
73
|
|
@@ -76,7 +86,6 @@ module DRbQS
|
|
76
86
|
|
77
87
|
exec_node = DRbQS::Execution::ExecuteNode.new(@uri, @options[:log_prefix], @options[:log_level], @options[:node_opts])
|
78
88
|
exec_node.execute(@process_num)
|
79
|
-
exec_node.wait
|
80
89
|
true
|
81
90
|
end
|
82
91
|
end
|
data/lib/drbqs/setting/server.rb
CHANGED
@@ -133,7 +133,6 @@ module DRbQS
|
|
133
133
|
end
|
134
134
|
exec_node = DRbQS::Execution::ExecuteNode.new(uri, node_log_file, @options[:log_level])
|
135
135
|
exec_node.execute(@execute_node_number, NODE_INTERVAL_TIME)
|
136
|
-
exec_node.wait
|
137
136
|
end
|
138
137
|
private :execute_node_and_wait
|
139
138
|
|
@@ -149,6 +148,7 @@ module DRbQS
|
|
149
148
|
end
|
150
149
|
uri = current_server_uri
|
151
150
|
wait_server_process(uri, server_pid)
|
151
|
+
$PROGRAM_NAME = "drbqs-node with drbqs-server (PID #{server_pid})"
|
152
152
|
execute_node_and_wait(uri)
|
153
153
|
end
|
154
154
|
private :command_server_with_nodes
|
data/lib/drbqs/task/task.rb
CHANGED
@@ -8,11 +8,13 @@ module DRbQS
|
|
8
8
|
# The tasks defined by this class are sent to nodes and
|
9
9
|
# calculated by the nodes.
|
10
10
|
class Task
|
11
|
+
DEFAULT_GROUP = :default
|
12
|
+
|
11
13
|
attr_reader :obj
|
12
14
|
attr_reader :args
|
13
15
|
attr_reader :method_name
|
14
16
|
attr_reader :hook
|
15
|
-
attr_accessor :note
|
17
|
+
attr_accessor :note, :group
|
16
18
|
|
17
19
|
# Nodes calculate by obj.method_name(*opts[:args]) and send the result to their server.
|
18
20
|
# Then the server executes &hook with a server instance and an object of result.
|
@@ -26,6 +28,7 @@ module DRbQS
|
|
26
28
|
# @option opts [String] :note Note for a task
|
27
29
|
# @option opts [Symbol] :hook Method name for hook
|
28
30
|
# that takes two arguments server and the result object.
|
31
|
+
# @option opts [Symbol] :group Group of nodes to execute the task.
|
29
32
|
# @param [Proc] hook A server execute hook as a callback when the server receive the result
|
30
33
|
# hook take two arguments: a DRbQS::Server object and a result of task.
|
31
34
|
# @note Changes of obj on a node are not sent to a server.
|
@@ -49,10 +52,15 @@ module DRbQS
|
|
49
52
|
end
|
50
53
|
@note = opts[:note]
|
51
54
|
@hook = hook || opts[:hook]
|
55
|
+
@group = opts[:group] || DRbQS::Task::DEFAULT_GROUP
|
56
|
+
end
|
57
|
+
|
58
|
+
def simple_drb_args
|
59
|
+
[@marshal_obj, @method_name, @marshal_args]
|
52
60
|
end
|
53
61
|
|
54
62
|
def drb_args(task_id)
|
55
|
-
[
|
63
|
+
[@group, task_id] + simple_drb_args
|
56
64
|
end
|
57
65
|
|
58
66
|
def exec_hook(server, result)
|
@@ -130,13 +138,13 @@ module DRbQS
|
|
130
138
|
class ContainerWithoutHook < DRbQS::Task::TaskSet::Container
|
131
139
|
def initialize(task_ary)
|
132
140
|
@data = task_ary.map.with_index do |task, i|
|
133
|
-
task.drb_args(i)
|
141
|
+
task.drb_args(i)[2..-1]
|
134
142
|
end
|
135
143
|
end
|
136
144
|
|
137
145
|
def exec
|
138
146
|
@data.map do |ary|
|
139
|
-
DRbQS::Task.execute_task(*ary
|
147
|
+
DRbQS::Task.execute_task(*ary)
|
140
148
|
end
|
141
149
|
end
|
142
150
|
end
|
@@ -147,13 +155,14 @@ module DRbQS
|
|
147
155
|
@original_note = task_ary.map do |task|
|
148
156
|
task.note
|
149
157
|
end.compact!
|
158
|
+
group_sym = get_group_sym(task_ary)
|
150
159
|
if task_ary.all? { |task| !(Proc === task.hook) }
|
151
160
|
container = DRbQS::Task::TaskSet::ContainerTask.new(task_ary)
|
152
|
-
super(container, :exec, hook: :exec_all_hooks, note: note_string)
|
161
|
+
super(container, :exec, hook: :exec_all_hooks, note: note_string, group: group_sym)
|
153
162
|
else
|
154
163
|
container = DRbQS::Task::TaskSet::ContainerWithoutHook.new(task_ary)
|
155
164
|
@original_task = task_ary
|
156
|
-
super(container, :exec, note: note_string) do |srv, result|
|
165
|
+
super(container, :exec, note: note_string, group: group_sym) do |srv, result|
|
157
166
|
result.each_with_index do |res, i|
|
158
167
|
@original_task[i].exec_hook(srv, res)
|
159
168
|
end
|
@@ -161,6 +170,17 @@ module DRbQS
|
|
161
170
|
end
|
162
171
|
end
|
163
172
|
|
173
|
+
def get_group_sym(task_ary)
|
174
|
+
group_names = task_ary.map do |task|
|
175
|
+
task.group
|
176
|
+
end
|
177
|
+
group_names.compact!
|
178
|
+
group_names.uniq!
|
179
|
+
raise ArgumentError, "Can not collect tasks with different groups." if group_names.size > 1
|
180
|
+
group_names[0]
|
181
|
+
end
|
182
|
+
private :get_group_sym
|
183
|
+
|
164
184
|
def note_string
|
165
185
|
str = "TaskSet"
|
166
186
|
unless @original_note.empty?
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module DRbQS
|
2
2
|
class Task
|
3
3
|
class Generator
|
4
|
-
# @param [Hash] data
|
4
|
+
# @param [Hash] data This argument is unnecessary and so deprecated
|
5
|
+
# Names of instance variables and their values,
|
5
6
|
# which can be accessed in {DRbQS::Task::Generator#set}.
|
6
7
|
def initialize(data = {})
|
7
8
|
@registrar = DRbQS::Task::Registrar.new(data)
|
@@ -3,15 +3,36 @@ require 'tmpdir'
|
|
3
3
|
module DRbQS
|
4
4
|
module Temporary
|
5
5
|
@@root = nil
|
6
|
+
@@subdir = nil
|
6
7
|
@@filename = nil
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
unless @@filename
|
9
|
+
def self.set_root_directory
|
10
|
+
if !@@root
|
11
11
|
pid = Process.pid
|
12
12
|
@@root = File.join(Dir.tmpdir, sprintf("drbqs_%s_%d_%d", ENV['USER'], pid, rand(10000)))
|
13
13
|
FileUtils.mkdir_p(@@root, :mode => 0700)
|
14
|
-
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.set_sub_directory(dir)
|
18
|
+
self.set_root_directory
|
19
|
+
@@filename = nil
|
20
|
+
@@subdir = File.join(@@root, dir)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.subdirectory
|
24
|
+
@@subdir && File.exist?(@@subdir) ? @@subdir : nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return FileName object to generate names of temporary files on DRbQS nodes.
|
28
|
+
def self.filename
|
29
|
+
unless @@filename
|
30
|
+
self.set_root_directory
|
31
|
+
if @@subdir
|
32
|
+
@@filename = FileName.new(File.join(@@subdir, sprintf("temp_%d", rand(10000))))
|
33
|
+
else
|
34
|
+
@@filename = FileName.new(File.join(@@root, sprintf("temp_%d", rand(10000))))
|
35
|
+
end
|
15
36
|
end
|
16
37
|
@@filename
|
17
38
|
end
|
@@ -27,7 +48,7 @@ module DRbQS
|
|
27
48
|
if basename
|
28
49
|
File.join(self.directory, basename)
|
29
50
|
else
|
30
|
-
filename.create(:add => :always)
|
51
|
+
filename.create(:add => :always, :directory => :parent)
|
31
52
|
end
|
32
53
|
end
|
33
54
|
|
@@ -35,7 +56,7 @@ module DRbQS
|
|
35
56
|
def self.delete
|
36
57
|
if @@root
|
37
58
|
FileUtils.rm_r(@@root)
|
38
|
-
FileUtils.mkdir_p(@@root)
|
59
|
+
FileUtils.mkdir_p(@@root, :mode => 0700)
|
39
60
|
end
|
40
61
|
end
|
41
62
|
|
@@ -68,19 +68,17 @@ module DRbQS
|
|
68
68
|
@transfer = transfer
|
69
69
|
end
|
70
70
|
|
71
|
-
def transfer_to_server
|
72
|
-
if files
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
raise err_new
|
80
|
-
end
|
81
|
-
else
|
82
|
-
raise "Server does not set transfer settings. Can not send file: #{files.join(", ")}"
|
71
|
+
def transfer_to_server(files)
|
72
|
+
if files && @transfer
|
73
|
+
begin
|
74
|
+
@transfer.transfer(files)
|
75
|
+
rescue Exception => err
|
76
|
+
err_new = err.class.new("#{err.to_s} (#{err.class}); Can not send file: #{files.join(", ")}")
|
77
|
+
err_new.set_backtrace(err.backtrace)
|
78
|
+
raise err_new
|
83
79
|
end
|
80
|
+
else
|
81
|
+
raise "Server does not set transfer settings. Can not send file: #{files.join(", ")}"
|
84
82
|
end
|
85
83
|
end
|
86
84
|
end
|
data/lib/drbqs/version.rb
CHANGED
data/lib/drbqs/worker.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
module DRbQS
|
2
|
+
class Worker
|
3
|
+
class SimpleForkedProcess
|
4
|
+
def initialize(io_r, io_w)
|
5
|
+
@io_r = io_r
|
6
|
+
@io_w = io_w
|
7
|
+
@queue = []
|
8
|
+
@special_task_number = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def calculate(marshal_obj, method_sym, args)
|
12
|
+
DRbQS::Task.execute_task(marshal_obj, method_sym, args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def send_response(obj)
|
16
|
+
@io_w.print DRbQS::Worker::Serialize.dump(obj)
|
17
|
+
@io_w.flush
|
18
|
+
end
|
19
|
+
private :send_response
|
20
|
+
|
21
|
+
def handle_task(obj)
|
22
|
+
task_id, marshal_obj, method_sym, args = obj
|
23
|
+
begin
|
24
|
+
res = calculate(marshal_obj, method_sym, args)
|
25
|
+
if task_id
|
26
|
+
send_response([:result, [task_id, res]])
|
27
|
+
end
|
28
|
+
rescue => err
|
29
|
+
send_response([:node_error, err])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
private :handle_task
|
33
|
+
|
34
|
+
def start
|
35
|
+
unpacker = DRbQS::Worker::Serialize::Unpacker.new
|
36
|
+
loop do
|
37
|
+
if @queue.empty?
|
38
|
+
begin
|
39
|
+
chunk = @io_r.readpartial(READ_BYTE_SIZE)
|
40
|
+
unpacker.feed_each(chunk) do |ary|
|
41
|
+
@queue << ary
|
42
|
+
end
|
43
|
+
rescue EOFError
|
44
|
+
break
|
45
|
+
end
|
46
|
+
else
|
47
|
+
obj = @queue.shift
|
48
|
+
case obj
|
49
|
+
when Array
|
50
|
+
handle_task(obj)
|
51
|
+
when :prepare_to_exit
|
52
|
+
send_response([:finish_preparing_to_exit])
|
53
|
+
when :exit
|
54
|
+
break
|
55
|
+
else
|
56
|
+
send_response([:node_error, "Invalid object from server."])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ForkedProcess < DRbQS::Worker::SimpleForkedProcess
|
64
|
+
def calculate(marshal_obj, method_sym, args)
|
65
|
+
result = super(marshal_obj, method_sym, args)
|
66
|
+
transfer_files = DRbQS::Transfer.dequeue_all
|
67
|
+
{ :result => result, :transfer => transfer_files }
|
68
|
+
end
|
69
|
+
|
70
|
+
def subdirectory_name(task_id)
|
71
|
+
if task_id
|
72
|
+
sprintf("T%08d", task_id)
|
73
|
+
else
|
74
|
+
sprintf("S%08d", (@special_task_number += 1))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
private :subdirectory_name
|
78
|
+
|
79
|
+
def handle_task(obj)
|
80
|
+
task_id, marshal_obj, method_sym, args = obj
|
81
|
+
DRbQS::Temporary.set_sub_directory(subdirectory_name(task_id))
|
82
|
+
begin
|
83
|
+
result_hash = calculate(marshal_obj, method_sym, args)
|
84
|
+
# If task_id is nil then the task is initialization or finalization.
|
85
|
+
# So we do not return results.
|
86
|
+
if task_id
|
87
|
+
if subdir = DRbQS::Temporary.subdirectory
|
88
|
+
result_hash[:tmp] = subdir
|
89
|
+
end
|
90
|
+
result_hash[:id] = task_id
|
91
|
+
send_response([:result, [task_id, result_hash]])
|
92
|
+
end
|
93
|
+
rescue => err
|
94
|
+
send_response([:node_error, err])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
private :handle_task
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|