perfectqueue 0.8.44.1 → 0.8.45
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/ChangeLog +5 -2
- data/lib/perfectqueue/application/decider.rb +2 -2
- data/lib/perfectqueue/application/router.rb +1 -1
- data/lib/perfectqueue/backend/rdb_compat.rb +40 -58
- data/lib/perfectqueue/engine.rb +2 -5
- data/lib/perfectqueue/multiprocess/thread_processor.rb +1 -1
- data/lib/perfectqueue/task.rb +2 -1
- data/lib/perfectqueue/task_metadata.rb +2 -4
- data/lib/perfectqueue/version.rb +1 -1
- data/perfectqueue.gemspec +1 -2
- data/spec/application/base_spec.rb +81 -0
- data/spec/application/decider_spec.rb +56 -0
- data/spec/application/dispatch_spec.rb +22 -0
- data/spec/application/router_spec.rb +48 -0
- data/spec/backend_spec.rb +9 -0
- data/spec/blocking_flag_spec.rb +103 -0
- data/spec/client_spec.rb +28 -0
- data/spec/daemons_logger_spec.rb +35 -0
- data/spec/engine_spec.rb +159 -0
- data/spec/multiprocess/child_process_monitor_spec.rb +300 -0
- data/spec/multiprocess/child_process_spec.rb +160 -0
- data/spec/multiprocess/fork_processor_spec.rb +170 -0
- data/spec/multiprocess/thread_processor_spec.rb +52 -0
- data/spec/queue_spec.rb +73 -68
- data/spec/rdb_compat_backend_spec.rb +481 -19
- data/spec/runner_spec.rb +32 -0
- data/spec/signal_thread_spec.rb +43 -0
- data/spec/stress.rb +1 -1
- data/spec/supervisor_spec.rb +188 -33
- data/spec/task_metadata_spec.rb +69 -0
- data/spec/task_monitor_spec.rb +42 -0
- data/spec/task_spec.rb +70 -0
- metadata +40 -19
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectQueue::Runner do
|
4
|
+
describe '#new' do
|
5
|
+
it 'creates with task' do
|
6
|
+
expect(PerfectQueue::Runner.new(double('task'))).to be_a(PerfectQueue::Runner)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#task' do
|
11
|
+
let (:task) { double('task') }
|
12
|
+
let (:runner) { PerfectQueue::Runner.new(task) }
|
13
|
+
it 'returns given task' do
|
14
|
+
expect(runner.task).to eq(task)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#queue' do
|
19
|
+
let (:runner) { PerfectQueue::Runner.new(double('task', client: 1)) }
|
20
|
+
it 'returns a queue' do
|
21
|
+
queue = runner.queue
|
22
|
+
expect(queue).to be_a(PerfectQueue::Queue)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#kill' do
|
27
|
+
let (:runner) { PerfectQueue::Runner.new(double('task')) }
|
28
|
+
it 'always returns nil' do
|
29
|
+
expect(runner.kill(nil)).to be_nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SignalThread do
|
4
|
+
before(:each) do
|
5
|
+
@th = SignalThread.new
|
6
|
+
end
|
7
|
+
|
8
|
+
after(:each) do
|
9
|
+
@th.trap('SIGUSR1')
|
10
|
+
@th.stop
|
11
|
+
@th.join
|
12
|
+
end
|
13
|
+
|
14
|
+
context('trap') do
|
15
|
+
it 'ignores Signal' do
|
16
|
+
old = @th.trap('SIGUSR1', 'IGNORE')
|
17
|
+
expect(old).to be_nil
|
18
|
+
expect(@th.handlers).to eq({})
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'traps Signal' do
|
22
|
+
flag = false
|
23
|
+
pr = proc{flag=1;raise}
|
24
|
+
expect(@th.trap('SIGUSR1', &pr)).to be_nil
|
25
|
+
expect(@th.handlers).to eq({USR1: pr})
|
26
|
+
allow(STDERR).to receive(:write).at_least(:once)
|
27
|
+
Process.kill(:USR1, Process.pid)
|
28
|
+
Thread.pass until flag
|
29
|
+
expect(flag).to eq(1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'queues signal' do
|
34
|
+
flag = false
|
35
|
+
pr = proc{flag=1;raise}
|
36
|
+
allow(@th).to receive(:enqueue){flag=2;raise}
|
37
|
+
expect(@th.trap('SIGUSR1', &pr)).to be_nil
|
38
|
+
allow(STDERR).to receive(:write).at_least(:once)
|
39
|
+
Process.kill(:USR1, Process.pid)
|
40
|
+
Thread.pass until flag
|
41
|
+
expect(flag).to eq(2)
|
42
|
+
end
|
43
|
+
end
|
data/spec/stress.rb
CHANGED
@@ -21,7 +21,7 @@ describe Queue do
|
|
21
21
|
loop_num.times do |i|
|
22
22
|
queue.submit("#{thread_id}-#{i}", "type01", {}, :now=>now-10)
|
23
23
|
task = queue.poll(:now=>now, :alive_time=>60)
|
24
|
-
task.
|
24
|
+
expect(task).not_to eq(nil)
|
25
25
|
task.heartbeat!(:now=>now, :alive_time=>70)
|
26
26
|
task.finish!(:now=>now, :retention_time=>80)
|
27
27
|
end
|
data/spec/supervisor_spec.rb
CHANGED
@@ -2,14 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
class TestHandler < PerfectQueue::Application::Base
|
4
4
|
def run
|
5
|
-
puts "TestHandler: #{task}"
|
5
|
+
#puts "TestHandler: #{task}"
|
6
6
|
if task.data['raise_error']
|
7
7
|
raise "expected error test"
|
8
8
|
end
|
9
9
|
if num = task.data['sleep']
|
10
10
|
sleep num
|
11
11
|
end
|
12
|
-
puts "Task finished"
|
12
|
+
#puts "Task finished"
|
13
13
|
end
|
14
14
|
|
15
15
|
def kill(reason)
|
@@ -30,47 +30,202 @@ end
|
|
30
30
|
|
31
31
|
describe Supervisor do
|
32
32
|
include QueueTest
|
33
|
-
|
33
|
+
let (:logger) { double('logger').as_null_object }
|
34
34
|
before do
|
35
|
-
|
36
|
-
@thread = Thread.new {
|
37
|
-
@sv.run
|
38
|
-
}
|
35
|
+
object_double('PerfectQueue::DaemonsLogger', new: logger).as_stubbed_const
|
39
36
|
end
|
40
37
|
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
context 'normal routing' do
|
39
|
+
before do
|
40
|
+
@sv = Supervisor.new(TestApp, queue_config)
|
41
|
+
@thread = Thread.new {
|
42
|
+
@sv.run
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
after do
|
47
|
+
@sv.stop(true)
|
48
|
+
@thread.join
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'route' do
|
52
|
+
expect_any_instance_of(TestHandler).to receive(:run).once
|
53
|
+
expect_any_instance_of(RegexpHandler).to receive(:run).once
|
54
|
+
queue.submit('task01', 'test', {})
|
55
|
+
queue.submit('task02', 'reg01', {})
|
56
|
+
sleep 2
|
57
|
+
end
|
44
58
|
end
|
45
59
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
60
|
+
context 'listen_debug_server' do
|
61
|
+
after do
|
62
|
+
@sv.stop(true)
|
63
|
+
@thread.join
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'listen_debug_server with UNIX Socket' do
|
67
|
+
Tempfile.open('supervisor') do |f|
|
68
|
+
config = queue_config.dup
|
69
|
+
config[:debug] = f.path
|
70
|
+
@sv = Supervisor.new(TestApp, config)
|
71
|
+
@thread = Thread.new {
|
72
|
+
@sv.run
|
73
|
+
}
|
74
|
+
sleep 2
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'listen_debug_server with TCP with address:port' do
|
79
|
+
config = queue_config.dup
|
80
|
+
config[:debug] = '127.0.0.1:0'
|
81
|
+
@sv = Supervisor.new(TestApp, config)
|
82
|
+
@thread = Thread.new {
|
83
|
+
@sv.run
|
84
|
+
}
|
85
|
+
sleep 2
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'listen_debug_server with TCP with port' do
|
89
|
+
config = queue_config.dup
|
90
|
+
config[:debug] = '0'
|
91
|
+
@sv = Supervisor.new(TestApp, config)
|
92
|
+
@thread = Thread.new {
|
93
|
+
@sv.run
|
94
|
+
}
|
95
|
+
sleep 2
|
96
|
+
end
|
52
97
|
end
|
53
98
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
99
|
+
context 'replace' do
|
100
|
+
before do
|
101
|
+
@sv = Supervisor.new(TestApp, queue_config)
|
102
|
+
@thread = Thread.new {
|
103
|
+
@sv.run
|
104
|
+
}
|
105
|
+
Thread.pass until @sv.engine
|
106
|
+
end
|
107
|
+
|
108
|
+
after do
|
109
|
+
@sv.stop(true)
|
110
|
+
@thread.join
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'replaces immediately' do
|
114
|
+
@sv.replace(true, ':')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'replaces not immediately' do
|
118
|
+
@sv.replace(false, ':')
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'fails to replace' do
|
122
|
+
Thread.pass until @sv.engine
|
123
|
+
allow(@sv.engine).to receive(:replace) { raise }
|
124
|
+
@sv.replace(false, ':')
|
125
|
+
end
|
59
126
|
end
|
60
127
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
128
|
+
context 'signal handling' do
|
129
|
+
before do
|
130
|
+
@sv = Supervisor.new(TestApp, queue_config)
|
131
|
+
@thread = Thread.new {
|
132
|
+
@sv.run
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
after do
|
137
|
+
@sv.stop(true)
|
138
|
+
@thread.join
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'handles TERM signal' do
|
142
|
+
Thread.pass until @sv.engine
|
143
|
+
Process.kill(:TERM, Process.pid)
|
144
|
+
expect(@thread.join(3)).to eq(@thread)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'handles INT signal' do
|
148
|
+
Thread.pass until @sv.engine
|
149
|
+
Process.kill(:INT, Process.pid)
|
150
|
+
expect(@thread.join(3)).to eq(@thread)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'handles QUIT signal' do
|
154
|
+
Thread.pass until @sv.engine
|
155
|
+
Process.kill(:QUIT, Process.pid)
|
156
|
+
#puts "finish expected..."
|
157
|
+
expect(@thread.join(3)).to eq(@thread)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'handles USR1 signal' do
|
161
|
+
Thread.pass until @sv.engine
|
162
|
+
processors = @sv.engine.processors
|
163
|
+
Process.kill(:USR1, Process.pid)
|
164
|
+
expect(@sv.engine.processors).to eq(processors)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'handles HUP signal' do
|
168
|
+
Thread.pass until @sv.engine
|
169
|
+
processors = @sv.engine.processors
|
170
|
+
Process.kill(:HUP, Process.pid)
|
171
|
+
expect(@sv.engine.processors).to eq(processors)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'handles USR2 signal' do
|
175
|
+
Thread.pass until @sv.engine
|
176
|
+
allow(logger).to receive(:reopen!)
|
177
|
+
Process.kill(:USR2, Process.pid)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'kill reason' do
|
181
|
+
expect_any_instance_of(TestHandler).to receive(:kill).once #.with(kind_of(PerfectQueue::CancelRequestedError)) # FIXME 'with' dead locks
|
182
|
+
queue.submit('task01', 'test', {'sleep'=>4})
|
183
|
+
sleep 2
|
184
|
+
Process.kill(:TERM, Process.pid)
|
185
|
+
expect(@thread.join(3)).to eq(@thread)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '.run' do
|
190
|
+
let (:runner) { double('runner') }
|
191
|
+
let (:config) { double('config') }
|
192
|
+
before (:each) do
|
193
|
+
allow(Supervisor).to receive(:new) \
|
194
|
+
.with(runner, config) do |*args, &block|
|
195
|
+
expect(block).to be_a(Proc)
|
196
|
+
double('supervisor', run: nil)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
it 'calls Supervisor.new.run' do
|
200
|
+
expect(Supervisor.run(runner, config){ }).to be_nil
|
201
|
+
end
|
66
202
|
end
|
67
203
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
@thread.join
|
204
|
+
describe '#run' do
|
205
|
+
let (:supervisor) { Supervisor.new(double('runner')){raise} }
|
206
|
+
it 'rescues exception' do
|
207
|
+
expect(supervisor.run).to be_nil
|
208
|
+
end
|
74
209
|
end
|
75
|
-
end
|
76
210
|
|
211
|
+
describe '#stop' do
|
212
|
+
let (:supervisor) { Supervisor.new(double('runner')){} }
|
213
|
+
it 'return nil without engine' do
|
214
|
+
expect(supervisor.run).to be_nil
|
215
|
+
end
|
216
|
+
it 'rescues exception' do
|
217
|
+
supervisor.instance_variable_set(:@engine, true) # dummy
|
218
|
+
expect(supervisor.stop(true)).to be false
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe '#restart' do
|
223
|
+
let (:supervisor) { Supervisor.new(double('runner')){} }
|
224
|
+
it 'return nil without engine' do
|
225
|
+
expect(supervisor.run).to be_nil
|
226
|
+
end
|
227
|
+
it 'rescues exception' do
|
228
|
+
expect(supervisor.restart(true)).to be false
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectQueue::TaskMetadata do
|
4
|
+
let (:attributes){ double('attributes', delete: nil) }
|
5
|
+
describe '#task' do
|
6
|
+
it 'returns a task' do
|
7
|
+
client = double('client')
|
8
|
+
key = double('key')
|
9
|
+
tm = TaskMetadata.new(client, key, attributes)
|
10
|
+
task = tm.task
|
11
|
+
expect(task).to be_a(Task)
|
12
|
+
expect(task.client).to eq(client)
|
13
|
+
expect(task.key).to eq(key)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#inspect' do
|
18
|
+
it 'returns inspected string' do
|
19
|
+
client = double('client')
|
20
|
+
key = double('key')
|
21
|
+
tm = TaskMetadata.new(client, key, attributes)
|
22
|
+
expect(tm.inspect).to eq("#<PerfectQueue::TaskMetadata @key=#{key.inspect} @attributes=#{attributes.inspect}>")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'running?' do
|
27
|
+
it 'returns true on running' do
|
28
|
+
tm = TaskMetadata.new(double, double, status: TaskStatus::RUNNING)
|
29
|
+
expect(tm.running?).to be true
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns false on finished' do
|
33
|
+
tm = TaskMetadata.new(double, double, status: TaskStatus::FINISHED)
|
34
|
+
expect(tm.running?).to be false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'message' do
|
39
|
+
it 'returns given message' do
|
40
|
+
message = double('message')
|
41
|
+
tm = TaskMetadata.new(double, double, message: message)
|
42
|
+
expect(tm.message).to eq(message)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'user' do
|
47
|
+
it 'returns given user' do
|
48
|
+
user = double('user')
|
49
|
+
tm = TaskMetadata.new(double, double, user: user)
|
50
|
+
expect(tm.user).to eq(user)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'created_at' do
|
55
|
+
it 'returns a time of given created_at' do
|
56
|
+
epoch = 42
|
57
|
+
tm = TaskMetadata.new(double, double, created_at: epoch)
|
58
|
+
expect(tm.created_at).to eq(Time.at(epoch))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'timeout' do
|
63
|
+
it 'returns a time of given timeout' do
|
64
|
+
epoch = 72
|
65
|
+
tm = TaskMetadata.new(double, double, timeout: epoch)
|
66
|
+
expect(tm.timeout).to eq(Time.at(epoch))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectQueue::TaskMonitor do
|
4
|
+
describe '#kill_task' do
|
5
|
+
it 'rescues exception' do
|
6
|
+
tm = PerfectQueue::TaskMonitor.new(logger: double('logger').as_null_object)
|
7
|
+
task = double('task')
|
8
|
+
reason = double('reason')
|
9
|
+
allow(task).to receive_message_chain(:runner, :kill) \
|
10
|
+
.with(no_args).with(reason){raise}
|
11
|
+
tm.instance_variable_set(:@task, task)
|
12
|
+
expect{tm.kill_task(reason)}.to raise_error(RuntimeError)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#external_task_heartbeat' do
|
17
|
+
it 'rescues exception' do
|
18
|
+
tm = PerfectQueue::TaskMonitor.new(logger: double('logger').as_null_object)
|
19
|
+
task = double('task')
|
20
|
+
reason = double('reason')
|
21
|
+
allow(task).to receive_message_chain(:runner, :kill) \
|
22
|
+
.with(no_args).with(reason){raise}
|
23
|
+
epoch = double('epoch')
|
24
|
+
allow(Time).to receive_message_chain(:now, :to_i){epoch}
|
25
|
+
ret = double('ret')
|
26
|
+
tm.instance_variable_set(:@task, task)
|
27
|
+
expect(tm.external_task_heartbeat(task){ret}).to eq(ret)
|
28
|
+
expect(tm.instance_variable_get(:@last_task_heartbeat)).to eq(epoch)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#run' do
|
33
|
+
it 'rescues exception' do
|
34
|
+
config = {logger: double('logger').as_null_object}
|
35
|
+
force_stop = double('force_stop')
|
36
|
+
expect(force_stop).to receive(:call).with(no_args).exactly(:once)
|
37
|
+
tm = PerfectQueue::TaskMonitor.new(config, nil, force_stop)
|
38
|
+
allow(Time).to receive(:now){raise}
|
39
|
+
tm.run
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|