arsenicum 0.2.1.1 → 0.3.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 +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