arsenicum 0.2.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/arsenicum/cli.rb +2 -0
- data/lib/arsenicum/core/io_helper.rb +56 -19
- data/lib/arsenicum/core/worker.rb +143 -62
- data/lib/arsenicum/logger.rb +2 -2
- data/lib/arsenicum/main.rb +14 -1
- data/lib/arsenicum/queue.rb +13 -3
- data/lib/arsenicum/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 227759f26fc6a576603f6ea5454a801b952c61e5
|
4
|
+
data.tar.gz: c6ce1168091c355e7a2258a1f181c28c18899121
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de846aa8417608539d5d28a85cb66a8c3f6737d25300474d6adf6fa339185de70b9a115c8ec509ce864b1dfa013da41f5e253055de17eb0a9792b1cda5b9e59c
|
7
|
+
data.tar.gz: 928123fa45f80afb3ae8f98117b3b00da665f42017e77f46d80279f7aba28c801d770e23f8768a365eb61ea668e0d94c0d7c97f6d23143b8efde10ba6b5d31b3
|
data/lib/arsenicum/cli.rb
CHANGED
@@ -1,34 +1,71 @@
|
|
1
1
|
module Arsenicum::Core::IOHelper
|
2
|
-
|
3
|
-
|
4
|
-
string_to_write.force_encoding 'BINARY'
|
2
|
+
TYPE_INT = "\xa0".force_encoding(Encoding::BINARY).freeze
|
3
|
+
TYPE_STRING = "\xfc".force_encoding(Encoding::BINARY).freeze
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def write_message(io, *items)
|
6
|
+
buffer = StringIO.new
|
7
|
+
buffer.set_encoding Encoding::BINARY
|
8
|
+
buffer.seek 4# length of integer.
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
items.each do |item|
|
11
|
+
case item
|
12
|
+
when Fixnum
|
13
|
+
buffer.write TYPE_INT
|
14
|
+
buffer.write [item].pack('N')
|
15
|
+
when String, Symbol
|
16
|
+
item = item.to_s.force_encoding Encoding::BINARY
|
17
|
+
buffer.write TYPE_STRING
|
18
|
+
length = item.length
|
19
|
+
buffer.write int2bin(length)
|
20
|
+
buffer.write item
|
21
|
+
end
|
22
|
+
end
|
23
|
+
buffer.seek 0
|
24
|
+
buffer.write int2bin(buffer.length - 4)
|
14
25
|
|
15
|
-
|
26
|
+
io.write buffer.string
|
16
27
|
end
|
17
28
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
29
|
+
def read_message(io, encoding: Encoding::UTF_8)
|
30
|
+
bytes_for_length = read_from io, 4
|
31
|
+
length = bin2int(bytes_for_length)
|
32
|
+
return [] if length == 0
|
33
|
+
|
34
|
+
bytes = read_from(io, length)
|
35
|
+
ptr = 0
|
22
36
|
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
result = []
|
38
|
+
while ptr < bytes.length
|
39
|
+
type_byte = bytes[ptr]
|
40
|
+
ptr += 1
|
41
|
+
case type_byte
|
42
|
+
when TYPE_INT
|
43
|
+
result << bin2int(bytes[ptr...ptr + 4])
|
44
|
+
ptr += 4
|
45
|
+
when TYPE_STRING
|
46
|
+
length = bin2int(bytes[ptr...ptr + 4])
|
47
|
+
ptr += 4
|
48
|
+
next result << '' if length == 0
|
49
|
+
result << bytes[ptr...ptr + length].force_encoding(encoding)
|
50
|
+
ptr += length
|
51
|
+
end
|
52
|
+
end
|
53
|
+
result
|
26
54
|
end
|
27
55
|
|
28
56
|
private
|
29
57
|
def read_from(io, length)
|
30
58
|
bytes = io.read length
|
31
59
|
raise Arsenicum::IO::EOFException unless bytes
|
32
|
-
bytes
|
60
|
+
bytes.force_encoding Encoding::BINARY
|
33
61
|
end
|
62
|
+
|
63
|
+
def int2bin(number)
|
64
|
+
[number].pack('N')
|
65
|
+
end
|
66
|
+
|
67
|
+
def bin2int(bytes)
|
68
|
+
bytes.unpack('N').first
|
69
|
+
end
|
70
|
+
|
34
71
|
end
|
@@ -4,8 +4,19 @@ class Arsenicum::Core::Worker
|
|
4
4
|
include Arsenicum::Core::Commands
|
5
5
|
include Arsenicum::Core::IOHelper
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
RESULT_SUCCESS = 0
|
8
|
+
RESULT_FAILURE = 0x80
|
9
|
+
|
10
|
+
CONTROL_STOP = 0xFF
|
11
|
+
CONTROL_PING = 0x30
|
12
|
+
|
13
|
+
attr_reader :pid,
|
14
|
+
:in_parent, :out_parent, :in_child, :out_child,
|
15
|
+
:ctrl_in_parent, :ctrl_out_parent, :ctrl_in_child, :ctrl_out_child,
|
16
|
+
:work_at,
|
17
|
+
:thread,
|
18
|
+
:active, :broker, :serializer, :formatter, :index,
|
19
|
+
:state
|
9
20
|
alias_method :active?, :active
|
10
21
|
|
11
22
|
def initialize(broker, index, worker_configuration)
|
@@ -14,55 +25,21 @@ class Arsenicum::Core::Worker
|
|
14
25
|
@serializer = worker_configuration[:serializer]
|
15
26
|
@formatter = worker_configuration[:formatter]
|
16
27
|
@thread = InvokerThread.new(self)
|
28
|
+
@work_at = :parent
|
29
|
+
@state = :parent
|
17
30
|
end
|
18
31
|
|
19
32
|
def run
|
20
|
-
(@in_parent, @out_child)
|
21
|
-
(@in_child, @out_parent)
|
33
|
+
(@in_parent, @out_child) = open_binary_pipes
|
34
|
+
(@in_child, @out_parent) = open_binary_pipes
|
35
|
+
(@ctrl_in_parent, @ctrl_out_child) = open_binary_pipes
|
36
|
+
(@ctrl_in_child, @ctrl_out_parent) = open_binary_pipes
|
22
37
|
|
23
|
-
@pid = fork
|
24
|
-
|
25
|
-
[in_parent, out_parent].each(&:close)
|
38
|
+
@pid = fork &method(:run_in_child)
|
39
|
+
return unless @pid
|
26
40
|
|
27
|
-
begin
|
28
|
-
loop do
|
29
|
-
begin
|
30
|
-
Arsenicum::Logger.debug {log_message_for '[child]to read initial command'}
|
31
|
-
command = read_code in_child
|
32
|
-
Arsenicum::Logger.debug {log_message_for '[child]Command: 0x%02x' % command}
|
33
|
-
break if command == COMMAND_STOP
|
34
|
-
task_id_string = read_string in_child
|
35
|
-
content = read_string in_child
|
36
|
-
rescue Arsenicum::IO::EOFException
|
37
|
-
# Interrupted request: No required GC.
|
38
|
-
break
|
39
|
-
end
|
40
|
-
|
41
|
-
task_id = task_id_string.to_sym
|
42
|
-
task = broker[task_id]
|
43
|
-
|
44
|
-
parameters = deserialize content
|
45
|
-
|
46
|
-
begin
|
47
|
-
Arsenicum::Logger.info {log_message_for "[child]Task start"}
|
48
|
-
Arsenicum::Logger.info {log_message_for "[child]Parameters: #{parameters.inspect}"
|
49
|
-
task.run *parameters
|
50
|
-
Arsenicum::Logger.info {log_message_for "[child]Task success"}
|
51
|
-
write_code out_child, 0
|
52
|
-
rescue Exception => e
|
53
|
-
Arsenicum::Logger.error {log_message_for "[child]Task Failure with #{e.class.name}#{":#{e.message}" if e.message}"}
|
54
|
-
write_code out_child, 1
|
55
|
-
write_string out_child, Marshal.dump(e)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
ensure
|
59
|
-
[in_child, out_child].each do |io|
|
60
|
-
begin io.close rescue nil end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
41
|
@active = true
|
65
|
-
[in_child, out_child].each(&:close)
|
42
|
+
[in_child, out_child, ctrl_in_child, ctrl_out_child].each(&:close)
|
66
43
|
pid
|
67
44
|
end
|
68
45
|
|
@@ -72,32 +49,104 @@ class Arsenicum::Core::Worker
|
|
72
49
|
end
|
73
50
|
end
|
74
51
|
|
52
|
+
def ask(task_id, *parameters)
|
53
|
+
write_message out_parent, task_id, serialize(parameters)
|
54
|
+
loop do
|
55
|
+
rs, = select([in_parent], [], [], 5)
|
56
|
+
break if rs
|
57
|
+
sleep 0.5
|
58
|
+
end
|
59
|
+
|
60
|
+
result, marshaled_exception = read_message in_parent
|
61
|
+
return if result == RESULT_SUCCESS
|
62
|
+
raise Marshal.load(marshaled_exception)
|
63
|
+
end
|
64
|
+
|
75
65
|
def ask_async(success_handler, failure_handler, task_id, *parameters)
|
76
66
|
thread.ask success_handler, failure_handler, task_id, *parameters
|
77
67
|
end
|
78
68
|
|
79
|
-
def
|
80
|
-
|
69
|
+
def stop
|
70
|
+
write_message ctrl_out_parent, COMMAND_STOP
|
71
|
+
Process.waitpid pid
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def run_in_child
|
76
|
+
switch_state :waiting
|
77
|
+
[in_parent, out_parent, ctrl_in_parent, ctrl_out_parent].each(&:close)
|
78
|
+
@work_at = :child
|
81
79
|
|
82
|
-
|
83
|
-
write_string out_parent, task_id.to_s
|
84
|
-
write_string out_parent, serialize(parameter)
|
80
|
+
hook_signal
|
85
81
|
|
86
|
-
|
82
|
+
begin
|
83
|
+
loop do
|
84
|
+
server_loop
|
85
|
+
break unless state == :waiting
|
86
|
+
end
|
87
|
+
ensure
|
88
|
+
[in_child, out_child, ctrl_in_child, ctrl_out_child].each do |io|
|
89
|
+
begin io.close rescue nil end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def switch_state(state)
|
95
|
+
@state = state
|
96
|
+
$0 = process_name
|
97
|
+
end
|
87
98
|
|
88
|
-
|
89
|
-
|
90
|
-
|
99
|
+
def server_loop
|
100
|
+
begin
|
101
|
+
rs, = select [in_child, ctrl_in_child], [], [], 0.5
|
102
|
+
return unless rs
|
103
|
+
rescue Interrupt
|
104
|
+
switch_state :interrupted
|
105
|
+
return
|
106
|
+
end
|
107
|
+
|
108
|
+
rs.first == in_child ? handle_request : handle_control
|
109
|
+
end
|
110
|
+
|
111
|
+
def handle_request
|
112
|
+
switch_state :busy
|
113
|
+
begin
|
114
|
+
task_id_string, content = read_message in_child, encoding: Encoding::UTF_8
|
115
|
+
rescue Arsenicum::IO::EOFException
|
116
|
+
# Interrupted request: No required GC.
|
117
|
+
return
|
118
|
+
end
|
119
|
+
|
120
|
+
task_id = task_id_string.to_sym
|
121
|
+
task = broker[task_id]
|
122
|
+
parameters = content.length == 0 ? [] : deserialize(content)
|
123
|
+
|
124
|
+
begin
|
125
|
+
info message: "Task[#{task_id}] start"
|
126
|
+
info message: "Parameters: #{parameters.inspect}"
|
127
|
+
|
128
|
+
task.run *parameters
|
129
|
+
info message: 'Task success'
|
130
|
+
write_message out_child, RESULT_SUCCESS
|
131
|
+
rescue Exception => e
|
132
|
+
error message: "Task #{task_id} Failed", exception: e
|
133
|
+
write_message out_child, RESULT_FAILURE, Marshal.dump(e)
|
134
|
+
end
|
91
135
|
ensure
|
92
|
-
|
136
|
+
switch_state :waiting
|
93
137
|
end
|
94
138
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
139
|
+
def handle_control
|
140
|
+
begin
|
141
|
+
control, = read_message ctrl_in_child
|
142
|
+
case control
|
143
|
+
when CONTROL_STOP
|
144
|
+
thread.stop
|
145
|
+
switch_state :stopped
|
146
|
+
end
|
147
|
+
end
|
98
148
|
end
|
99
149
|
|
100
|
-
private
|
101
150
|
def serialize(parameter)
|
102
151
|
serializer.serialize(formatter.format(parameter))
|
103
152
|
end
|
@@ -114,14 +163,37 @@ class Arsenicum::Core::Worker
|
|
114
163
|
end
|
115
164
|
end
|
116
165
|
|
166
|
+
def process_name
|
167
|
+
"arsenicum[Worker ##{index}] - #{state}"
|
168
|
+
end
|
169
|
+
|
117
170
|
def return_to_broker
|
118
171
|
broker.get_back_worker self
|
119
172
|
end
|
120
173
|
|
174
|
+
[:debug, :info, :warn, :error, :fatal].each do |level|
|
175
|
+
eval <<-SCRIPT, binding, __FILE__, __LINE__ + 1
|
176
|
+
def #{level}(message: nil, exception: nil)
|
177
|
+
Arsenicum::Logger.#{level} do
|
178
|
+
message = "[Worker #\#{index}][\#{work_at}]\#{message}" if message
|
179
|
+
[message, exception]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
SCRIPT
|
183
|
+
end
|
184
|
+
|
121
185
|
def log_message_for(message)
|
122
186
|
"[Worker ##{object_id}]#{message}"
|
123
187
|
end
|
124
188
|
|
189
|
+
def hook_signal
|
190
|
+
[:USR1, :USR2, ].each do |sig|
|
191
|
+
Signal.trap sig do
|
192
|
+
exit 1
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
125
197
|
class InvokerThread < Thread
|
126
198
|
attr_accessor :task_request
|
127
199
|
private :task_request, :task_request=
|
@@ -138,19 +210,28 @@ class Arsenicum::Core::Worker
|
|
138
210
|
|
139
211
|
begin
|
140
212
|
worker.ask task_id, *parameter
|
213
|
+
info worker, message: "Completed processing: #{task_id}"
|
141
214
|
success_handler.call
|
142
215
|
rescue Exception => e
|
143
|
-
|
216
|
+
error worker, exception: e
|
144
217
|
failure_handler.call e
|
145
218
|
ensure
|
146
219
|
self.task_request = nil
|
220
|
+
return_to_broker
|
147
221
|
end
|
148
222
|
end
|
149
223
|
end
|
150
224
|
end
|
151
225
|
|
152
|
-
|
153
|
-
|
226
|
+
[:debug, :info, :warn, :error, :fatal].each do |level|
|
227
|
+
eval <<-SCRIPT, binding, __FILE__, __LINE__ + 1
|
228
|
+
def #{level}(worker, message: nil, exception: nil)
|
229
|
+
Arsenicum::Logger.#{level} do
|
230
|
+
message = "[Worker #\#{worker.index}][\#{worker.work_at}][thread]\#{message}" if message
|
231
|
+
[message, exception]
|
232
|
+
end
|
233
|
+
end
|
234
|
+
SCRIPT
|
154
235
|
end
|
155
236
|
end
|
156
237
|
end
|
data/lib/arsenicum/logger.rb
CHANGED
@@ -10,9 +10,9 @@ module Arsenicum::Logger
|
|
10
10
|
|
11
11
|
[:debug, :info, :warn, :error, :fatal].each do |method|
|
12
12
|
eval <<-METHOD, binding, __FILE__, __LINE__ + 1
|
13
|
-
def #{method}(
|
13
|
+
def #{method}(&block)
|
14
14
|
return unless logger
|
15
|
-
logger.#{method}
|
15
|
+
logger.#{method} &block
|
16
16
|
end
|
17
17
|
METHOD
|
18
18
|
end
|
data/lib/arsenicum/main.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Arsenicum
|
2
2
|
class Main
|
3
|
+
attr_reader :queues
|
4
|
+
|
3
5
|
def run(config)
|
4
6
|
$0 = 'arsenicum[main]'
|
5
7
|
|
@@ -18,8 +20,12 @@ module Arsenicum
|
|
18
20
|
|
19
21
|
before_boot(config)
|
20
22
|
|
21
|
-
|
23
|
+
@queues = config.queue_configurations.map{|qc|qc.build}
|
24
|
+
threads = @queues.map(&:start_async)
|
22
25
|
begin
|
26
|
+
sleep 10
|
27
|
+
trap_signal
|
28
|
+
|
23
29
|
threads.each(&:join)
|
24
30
|
rescue Interrupt
|
25
31
|
end
|
@@ -44,6 +50,13 @@ module Arsenicum
|
|
44
50
|
Arsenicum::Logger.configure config.logger_config
|
45
51
|
end
|
46
52
|
|
53
|
+
def trap_signal
|
54
|
+
[:TERM, :INT,].each do |sig|
|
55
|
+
queues.each(&:stop)
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
47
60
|
autoload :RailsMain, 'arsenicum/main/rails_main'
|
48
61
|
end
|
49
62
|
end
|
data/lib/arsenicum/queue.rb
CHANGED
@@ -13,18 +13,28 @@ class Arsenicum::Queue
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def start
|
16
|
-
Arsenicum::Logger.info
|
16
|
+
Arsenicum::Logger.info{"[queue]Queue #{name} is now starting"}
|
17
17
|
broker.run
|
18
|
-
Arsenicum::Logger.info
|
18
|
+
Arsenicum::Logger.info{"[queue]Queue #{name} start-up completed"}
|
19
19
|
|
20
20
|
loop do
|
21
|
-
|
21
|
+
begin
|
22
|
+
(message, original_message) = pick
|
23
|
+
rescue => e
|
24
|
+
handle_failure e, original_message
|
25
|
+
next
|
26
|
+
end
|
27
|
+
|
22
28
|
next sleep(0.5) unless message
|
23
29
|
|
24
30
|
broker.delegate message, -> { handle_success(original_message) }, -> e { handle_failure(e, original_message) }
|
25
31
|
end
|
26
32
|
end
|
27
33
|
|
34
|
+
def stop
|
35
|
+
broker.stop
|
36
|
+
end
|
37
|
+
|
28
38
|
def register(task)
|
29
39
|
broker[task.id] = task
|
30
40
|
end
|
data/lib/arsenicum/version.rb
CHANGED