perfectqueue 0.8.54 → 0.9.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/.travis.yml +3 -7
- data/ChangeLog +6 -19
- data/README.md +0 -11
- data/circle.yml +7 -0
- data/lib/perfectqueue/backend/rdb.rb +8 -19
- data/lib/perfectqueue/backend/rdb_compat.rb +59 -84
- data/lib/perfectqueue/client.rb +0 -5
- data/lib/perfectqueue/command/perfectqueue.rb +0 -11
- data/lib/perfectqueue/multiprocess/child_process_monitor.rb +2 -2
- data/lib/perfectqueue/multiprocess/thread_processor.rb +1 -1
- data/lib/perfectqueue/task.rb +0 -4
- data/lib/perfectqueue/task_metadata.rb +0 -4
- data/lib/perfectqueue/task_monitor.rb +1 -8
- data/lib/perfectqueue/task_status.rb +0 -1
- data/lib/perfectqueue/version.rb +1 -1
- data/lib/perfectqueue/worker.rb +2 -4
- data/spec/queue_spec.rb +0 -28
- data/spec/rdb_backend_spec.rb +6 -16
- data/spec/rdb_compat_backend_spec.rb +43 -100
- data/spec/spec_helper.rb +2 -3
- data/spec/supervisor_spec.rb +0 -8
- data/spec/task_monitor_spec.rb +19 -16
- data/spec/worker_spec.rb +222 -0
- metadata +6 -4
- data/.circleci/config.yml +0 -19
@@ -8,7 +8,6 @@ op.banner += %[ <command>
|
|
8
8
|
commands:
|
9
9
|
list Show list of tasks
|
10
10
|
submit <key> <type> <data> Submit a new task
|
11
|
-
cancel_request <key> Cancel request
|
12
11
|
force_finish <key> Force finish a task
|
13
12
|
run <class> Run a worker process
|
14
13
|
init Initialize a backend database
|
@@ -76,11 +75,6 @@ begin
|
|
76
75
|
cmd = :list
|
77
76
|
usage nil unless ARGV.length == 0
|
78
77
|
|
79
|
-
when 'cancel_request' ,'cancel'
|
80
|
-
cmd = :cancel
|
81
|
-
usage nil unless ARGV.length == 1
|
82
|
-
key = ARGV[0]
|
83
|
-
|
84
78
|
when 'force_finish' ,'finish'
|
85
79
|
cmd = :finish
|
86
80
|
usage nil unless ARGV.length == 1
|
@@ -141,11 +135,6 @@ when :list
|
|
141
135
|
}
|
142
136
|
puts "#{n} entries."
|
143
137
|
|
144
|
-
when :cancel
|
145
|
-
PerfectQueue.open(config_load_proc.call) {|queue|
|
146
|
-
queue[key].cancel_request!
|
147
|
-
}
|
148
|
-
|
149
138
|
when :finish
|
150
139
|
PerfectQueue.open(config_load_proc.call) {|queue|
|
151
140
|
queue[key].force_finish!
|
@@ -102,8 +102,8 @@ module PerfectQueue
|
|
102
102
|
def send_signal(sig)
|
103
103
|
begin
|
104
104
|
Process.kill(sig, @pid)
|
105
|
-
rescue Errno::ESRCH, Errno::EPERM
|
106
|
-
|
105
|
+
rescue Errno::ESRCH, Errno::EPERM
|
106
|
+
# TODO log?
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
@@ -121,7 +121,7 @@ module PerfectQueue
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def process(task)
|
124
|
-
@log.info "acquired task task=#{task.key} id=#{@processor_id}"
|
124
|
+
@log.info "acquired task task=#{task.key} id=#{@processor_id}: #{task.inspect}"
|
125
125
|
begin
|
126
126
|
r = @runner.new(task)
|
127
127
|
@tm.set_task(task, r)
|
data/lib/perfectqueue/task.rb
CHANGED
@@ -147,7 +147,7 @@ module PerfectQueue
|
|
147
147
|
def task_heartbeat
|
148
148
|
@task.heartbeat!
|
149
149
|
rescue
|
150
|
-
# finished,
|
150
|
+
# finished, preempted, etc.
|
151
151
|
kill_task($!)
|
152
152
|
end
|
153
153
|
end
|
@@ -178,13 +178,6 @@ module PerfectQueue
|
|
178
178
|
}
|
179
179
|
end
|
180
180
|
|
181
|
-
def cancel_request!(*args, &block)
|
182
|
-
@log.info "cancel request task=#{self.key}" if @log
|
183
|
-
@task_monitor.task_finished(self) {
|
184
|
-
super(*args, &block)
|
185
|
-
}
|
186
|
-
end
|
187
|
-
|
188
181
|
def update_data!(hash)
|
189
182
|
@log.info "update data #{hash.inspect} task=#{self.key}" if @log
|
190
183
|
@task_monitor.external_task_heartbeat(self) {
|
data/lib/perfectqueue/version.rb
CHANGED
data/lib/perfectqueue/worker.rb
CHANGED
@@ -28,7 +28,6 @@ module PerfectQueue
|
|
28
28
|
config = block.call
|
29
29
|
|
30
30
|
@config = config
|
31
|
-
@log = config[:logger] || Logger.new(STDERR)
|
32
31
|
@runner = runner
|
33
32
|
|
34
33
|
@detach_wait = config[:detach_wait] || config['detach_wait'] || 10.0
|
@@ -67,11 +66,9 @@ module PerfectQueue
|
|
67
66
|
|
68
67
|
else
|
69
68
|
# child process finished unexpectedly
|
70
|
-
@log.info "Child process finished unexpectedly pid=#{pid}"
|
71
69
|
end
|
72
70
|
|
73
|
-
rescue Errno::ECHILD
|
74
|
-
@log.info "#{e.class}: #{e.message}\n#{e.backtrace}"
|
71
|
+
rescue Errno::ECHILD
|
75
72
|
end
|
76
73
|
end
|
77
74
|
|
@@ -133,3 +130,4 @@ module PerfectQueue
|
|
133
130
|
end
|
134
131
|
|
135
132
|
end
|
133
|
+
|
data/spec/queue_spec.rb
CHANGED
@@ -20,8 +20,6 @@ describe Queue do
|
|
20
20
|
queue.submit('task01', 'type1', {}, :now=>now+1)
|
21
21
|
}.to raise_error AlreadyExistsError
|
22
22
|
|
23
|
-
queue['task01'].cancel_request!(:now=>now+2)
|
24
|
-
|
25
23
|
expect {
|
26
24
|
allow(STDERR).to receive(:puts)
|
27
25
|
queue.submit('task01', 'type1', {}, :now=>now+10)
|
@@ -161,7 +159,6 @@ describe Queue do
|
|
161
159
|
#queue['task01'].metadata.finished?.should == false
|
162
160
|
#queue['task01'].metadata.running?.should == false
|
163
161
|
#queue['task01'].metadata.waiting?.should == true
|
164
|
-
#queue['task01'].metadata.cancel_requested?.should == false
|
165
162
|
|
166
163
|
task01 = queue.poll(:now=>now+10, :alive_time=>10)
|
167
164
|
expect(task01.key).to eq('task01')
|
@@ -169,37 +166,12 @@ describe Queue do
|
|
169
166
|
expect(queue['task01'].metadata.finished?).to eq(false)
|
170
167
|
expect(queue['task01'].metadata.running?).to eq(true)
|
171
168
|
expect(queue['task01'].metadata.waiting?).to eq(false)
|
172
|
-
expect(queue['task01'].metadata.cancel_requested?).to eq(false)
|
173
|
-
|
174
|
-
task01.cancel_request!
|
175
|
-
|
176
|
-
# status of cancel_requested running tasks is cancel_requested
|
177
|
-
expect(queue['task01'].metadata.finished?).to eq(false)
|
178
|
-
expect(queue['task01'].metadata.running?).to eq(false)
|
179
|
-
expect(queue['task01'].metadata.waiting?).to eq(false)
|
180
|
-
expect(queue['task01'].metadata.cancel_requested?).to eq(true)
|
181
169
|
|
182
170
|
task01.finish!
|
183
171
|
|
184
172
|
expect(queue['task01'].metadata.finished?).to eq(true)
|
185
173
|
expect(queue['task01'].metadata.running?).to eq(false)
|
186
174
|
expect(queue['task01'].metadata.waiting?).to eq(false)
|
187
|
-
expect(queue['task01'].metadata.cancel_requested?).to eq(false)
|
188
|
-
end
|
189
|
-
|
190
|
-
it 'fail canceling finished task' do
|
191
|
-
now = Time.now.to_i
|
192
|
-
queue.submit('task01', 'type1', {"a"=>1}, :now=>now+0)
|
193
|
-
|
194
|
-
task01 = queue.poll(:now=>now+10, :alive_time=>10)
|
195
|
-
expect(task01.key).to eq('task01')
|
196
|
-
|
197
|
-
task01.finish!
|
198
|
-
|
199
|
-
expect {
|
200
|
-
allow(STDERR).to receive(:puts)
|
201
|
-
queue['task01'].cancel_request!
|
202
|
-
}.to raise_error AlreadyFinishedError
|
203
175
|
end
|
204
176
|
|
205
177
|
it 'retention_time' do
|
data/spec/rdb_backend_spec.rb
CHANGED
@@ -21,11 +21,15 @@ describe Backend::RDBBackend do
|
|
21
21
|
|
22
22
|
context '#submit' do
|
23
23
|
it 'adds task' do
|
24
|
-
db.submit('key', '{"foo":"bar"}')
|
24
|
+
expect(db.submit('key', '{"foo":"bar"}')).to be true
|
25
25
|
row = db.db.fetch("SELECT * FROM `#{table}` WHERE id=? LIMIT 1", 'key').first
|
26
26
|
expect(row[:created_at]).not_to be_nil
|
27
27
|
expect(row[:data]).to eq('{"foo":"bar"}')
|
28
28
|
end
|
29
|
+
it 'returns nil for a duplicated task' do
|
30
|
+
expect(db.submit('key', '{"foo":"bar"}')).to be true
|
31
|
+
expect(db.submit('key', '{"foo":"bar"}')).to be_nil
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
context '#cancel' do
|
@@ -55,7 +59,7 @@ describe Backend::RDBBackend do
|
|
55
59
|
end
|
56
60
|
context 'error' do
|
57
61
|
it 'returns block result' do
|
58
|
-
expect(RuntimeError).to receive(:new).exactly(
|
62
|
+
expect(RuntimeError).to receive(:new).exactly(Backend::RDBBackend::MAX_RETRY).and_call_original
|
59
63
|
allow(STDERR).to receive(:puts)
|
60
64
|
allow(db).to receive(:sleep)
|
61
65
|
expect do
|
@@ -65,19 +69,5 @@ describe Backend::RDBBackend do
|
|
65
69
|
end.to raise_error(RuntimeError)
|
66
70
|
end
|
67
71
|
end
|
68
|
-
context 'cannot connect' do
|
69
|
-
let (:uri){ 'mysql2://root:@nonexistent/perfectqueue_test' }
|
70
|
-
let (:db) do
|
71
|
-
Backend::RDBBackend.new(uri, table)
|
72
|
-
end
|
73
|
-
it 'raises Sequel::DatabaseConnectionError' do
|
74
|
-
allow(STDERR).to receive(:puts)
|
75
|
-
slept = 0
|
76
|
-
expect(db).to receive(:sleep).exactly(9).times{|n| slept += n }
|
77
|
-
expect(db.db).to receive(:connect).exactly(10).times.and_call_original
|
78
|
-
expect{ db.__send__(:connect){ db.db.run('SELECT 1;') } }.to raise_error(Sequel::DatabaseConnectionError)
|
79
|
-
expect(slept).to be < 30
|
80
|
-
end
|
81
|
-
end
|
82
72
|
end
|
83
73
|
end
|
@@ -166,11 +166,15 @@ describe Backend::RDBCompatBackend do
|
|
166
166
|
end
|
167
167
|
|
168
168
|
context '#submit' do
|
169
|
-
it '
|
170
|
-
db.submit('key', 'test', nil, {})
|
169
|
+
it 'returns true' do
|
170
|
+
expect(db.submit('key', 'test', nil, {})).to be_an_instance_of(Task)
|
171
|
+
end
|
172
|
+
it 'returns true (gzip)' do
|
173
|
+
expect(db.submit('key', 'test', nil, {compression: 'gzip'})).to be_an_instance_of(Task)
|
171
174
|
end
|
172
|
-
it '
|
173
|
-
db.submit('key', 'test', nil, {
|
175
|
+
it 'returns nil if duplication' do
|
176
|
+
expect(db.submit('key', 'test', nil, {})).to be_an_instance_of(Task)
|
177
|
+
expect{db.submit('key', 'test', nil, {})}.to raise_error(IdempotentAlreadyExistsError)
|
174
178
|
end
|
175
179
|
end
|
176
180
|
|
@@ -195,85 +199,52 @@ describe Backend::RDBCompatBackend do
|
|
195
199
|
expect(ary[0]).to be_an_instance_of(AcquiredTask)
|
196
200
|
end
|
197
201
|
end
|
198
|
-
context '
|
199
|
-
let :
|
200
|
-
|
201
|
-
db.submit('key1', 'test1', nil, {now: t0})
|
202
|
-
db.submit('key2', 'test2', nil, {now: t0})
|
203
|
-
db.submit('key3', 'test3', nil, {now: t0})
|
202
|
+
context 'disable_resource_limit' do
|
203
|
+
let (:config) do
|
204
|
+
{url: 'mysql2://root:@localhost/perfectqueue_test', table: table, disable_resource_limit: true}
|
204
205
|
end
|
205
|
-
|
206
|
-
|
207
|
-
expect(now0).to receive(:to_time).exactly(3).times.and_call_original
|
208
|
-
db.list({}) do |task|
|
209
|
-
expect(task.timeout).to eq now0.to_time
|
210
|
-
end
|
211
|
-
ary = db.acquire(alive_time, max_acquire, {now: now})
|
212
|
-
expect(ary).to be_an_instance_of(Array)
|
213
|
-
expect(ary.size).to eq(3)
|
214
|
-
expect(ary[0]).to be_an_instance_of(AcquiredTask)
|
215
|
-
expect(ary[1]).to be_an_instance_of(AcquiredTask)
|
216
|
-
expect(ary[2]).to be_an_instance_of(AcquiredTask)
|
217
|
-
|
218
|
-
now1 = Time.at(now + alive_time)
|
219
|
-
expect(now1).to receive(:to_time).exactly(3).times.and_call_original
|
220
|
-
db.list({}){|task| expect(task.timeout).to eq now1.to_time }
|
206
|
+
before do
|
207
|
+
db.submit(key, 'test', nil, {})
|
221
208
|
end
|
222
|
-
it 'returns
|
223
|
-
db.instance_variable_set(:@prefetch_break_types, 'test2')
|
209
|
+
it 'returns a task' do
|
224
210
|
ary = db.acquire(alive_time, max_acquire, {})
|
225
211
|
expect(ary).to be_an_instance_of(Array)
|
226
|
-
expect(ary.size).to eq(
|
212
|
+
expect(ary.size).to eq(1)
|
227
213
|
expect(ary[0]).to be_an_instance_of(AcquiredTask)
|
228
|
-
expect(ary[1]).to be_an_instance_of(AcquiredTask)
|
229
214
|
end
|
230
215
|
end
|
231
|
-
context '
|
232
|
-
let :t0 do now -
|
216
|
+
context 'some tasks' do
|
217
|
+
let :t0 do now - 300 end
|
218
|
+
let :t1 do now - 200 end
|
219
|
+
let :t2 do now - 100 end
|
233
220
|
before do
|
234
221
|
db.submit('key1', 'test1', nil, {now: t0})
|
235
222
|
db.submit('key2', 'test2', nil, {now: t0})
|
236
|
-
db.submit('key3', 'test3', nil, {now:
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
223
|
+
db.submit('key3', 'test3', nil, {now: t1})
|
224
|
+
db.submit('key4', 'test4', nil, {now: t2})
|
225
|
+
db.submit('key5', 'test5', nil, {now: t2})
|
226
|
+
end
|
227
|
+
it 'returns 5 tasks' do
|
228
|
+
ary = []
|
229
|
+
db.list({}){|task| ary << task }
|
230
|
+
expect(ary[0].timeout.to_i).to eq t0
|
231
|
+
expect(ary[1].timeout.to_i).to eq t0
|
232
|
+
expect(ary[2].timeout.to_i).to eq t1
|
233
|
+
expect(ary[3].timeout.to_i).to eq t2
|
234
|
+
expect(ary[4].timeout.to_i).to eq t2
|
246
235
|
|
247
|
-
ary = db.acquire(alive_time, max_acquire, {})
|
248
|
-
expect(ary).to
|
249
|
-
|
250
|
-
|
251
|
-
|
236
|
+
ary = db.acquire(alive_time, max_acquire, {now: now})
|
237
|
+
expect(ary).to be_an_instance_of(Array)
|
238
|
+
expect(ary.size).to eq(5)
|
239
|
+
expect(ary[0]).to be_an_instance_of(AcquiredTask)
|
240
|
+
expect(ary[1]).to be_an_instance_of(AcquiredTask)
|
241
|
+
expect(ary[2]).to be_an_instance_of(AcquiredTask)
|
242
|
+
expect(ary[3]).to be_an_instance_of(AcquiredTask)
|
243
|
+
expect(ary[4]).to be_an_instance_of(AcquiredTask)
|
252
244
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
let (:retention_time) { 42 }
|
257
|
-
let (:delete_timeout){ now + retention_time }
|
258
|
-
let (:options){ {now: now} }
|
259
|
-
context 'have a task' do
|
260
|
-
before do
|
261
|
-
db.submit(key, 'test', nil, {})
|
262
|
-
end
|
263
|
-
it 'returns nil' do
|
264
|
-
expect(db.cancel_request(key, options)).to be_nil
|
265
|
-
row = db.db.fetch("SELECT created_at FROM `#{table}` WHERE id=? LIMIT 1", key).first
|
266
|
-
expect(row[:created_at]).to eq 0
|
267
|
-
end
|
268
|
-
end
|
269
|
-
context 'already finished' do
|
270
|
-
before do
|
271
|
-
expect(STDERR).to receive(:puts)
|
272
|
-
db.submit(key, 'test', nil, {})
|
273
|
-
db.finish(task_token, retention_time, options)
|
274
|
-
end
|
275
|
-
it 'raises AlreadyFinishedError' do
|
276
|
-
expect{db.cancel_request(key, options)}.to raise_error(AlreadyFinishedError)
|
245
|
+
now1 = Time.at(now + alive_time)
|
246
|
+
expect(now1).to receive(:to_time).exactly(5).times.and_call_original
|
247
|
+
db.list({}){|task| expect(task.timeout).to eq now1.to_time }
|
277
248
|
end
|
278
249
|
end
|
279
250
|
end
|
@@ -348,15 +319,6 @@ describe Backend::RDBCompatBackend do
|
|
348
319
|
expect{db.heartbeat(task_token, 0, {})}.to raise_error(PreemptedError)
|
349
320
|
end
|
350
321
|
end
|
351
|
-
context 'canceled task' do
|
352
|
-
before do
|
353
|
-
db.submit(key, 'test', nil, {})
|
354
|
-
db.cancel_request(key, options)
|
355
|
-
end
|
356
|
-
it 'returns nil' do
|
357
|
-
expect( db.heartbeat(task_token, 0, {}) ).to be_nil
|
358
|
-
end
|
359
|
-
end
|
360
322
|
end
|
361
323
|
|
362
324
|
context '#connect' do
|
@@ -367,7 +329,7 @@ describe Backend::RDBCompatBackend do
|
|
367
329
|
end
|
368
330
|
context 'error' do
|
369
331
|
it 'returns block result' do
|
370
|
-
expect(RuntimeError).to receive(:new).exactly(
|
332
|
+
expect(RuntimeError).to receive(:new).exactly(Backend::RDBCompatBackend::MAX_RETRY).and_call_original
|
371
333
|
allow(STDERR).to receive(:puts)
|
372
334
|
allow(db).to receive(:sleep)
|
373
335
|
expect do
|
@@ -376,18 +338,6 @@ describe Backend::RDBCompatBackend do
|
|
376
338
|
end
|
377
339
|
end.to raise_error(RuntimeError)
|
378
340
|
end
|
379
|
-
context 'cannot connect' do
|
380
|
-
let (:config){ {url: 'mysql2://root:@nonexistent/perfectqueue_test', table: table} }
|
381
|
-
it 'raises Sequel::DatabaseConnectionError' do
|
382
|
-
allow(STDERR).to receive(:puts)
|
383
|
-
d = Backend::RDBCompatBackend.new(client, config)
|
384
|
-
slept = 0
|
385
|
-
expect(d).to receive(:sleep).exactly(9).times{|n| slept += n }
|
386
|
-
expect(d.db).to receive(:connect).exactly(10).times.and_call_original
|
387
|
-
expect{ d.__send__(:connect){ d.db.run('SELECT 1;') } }.to raise_error(Sequel::DatabaseConnectionError)
|
388
|
-
expect(slept).to eq(18)
|
389
|
-
end
|
390
|
-
end
|
391
341
|
end
|
392
342
|
end
|
393
343
|
|
@@ -541,13 +491,6 @@ describe Backend::RDBCompatBackend do
|
|
541
491
|
end
|
542
492
|
end
|
543
493
|
end
|
544
|
-
context 'created_at is 0' do
|
545
|
-
it 'status is :cancel_requested' do
|
546
|
-
data[:created_at] = 0
|
547
|
-
r = db.__send__(:create_attributes, now, row)
|
548
|
-
expect(r[:status]).to eq TaskStatus::CANCEL_REQUESTED
|
549
|
-
end
|
550
|
-
end
|
551
494
|
context 'created_at > 0' do
|
552
495
|
context 'timeout' do
|
553
496
|
it 'status is :waiting' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
|
2
2
|
|
3
|
-
require 'perfectqueue'
|
4
|
-
|
5
3
|
if ENV['SIMPLE_COV']
|
6
4
|
require 'simplecov'
|
7
5
|
SimpleCov.start do
|
8
|
-
add_filter 'spec/'
|
9
6
|
add_filter 'pkg/'
|
10
7
|
add_filter 'vendor/'
|
11
8
|
end
|
12
9
|
end
|
13
10
|
|
11
|
+
require 'perfectqueue'
|
12
|
+
|
14
13
|
if ENV["CI"]
|
15
14
|
require 'coveralls'
|
16
15
|
Coveralls.wear!
|