perfectqueue 0.8.5 → 0.8.6
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/ChangeLog +17 -0
- data/lib/perfectqueue/backend/rdb_compat.rb +12 -7
- data/lib/perfectqueue/daemons_logger.rb +1 -1
- data/lib/perfectqueue/signal_queue.rb +1 -1
- data/lib/perfectqueue/task_metadata.rb +2 -0
- data/lib/perfectqueue/version.rb +1 -1
- data/lib/perfectqueue/worker.rb +12 -7
- data/spec/queue_spec.rb +1 -7
- data/spec/rdb_compat_backend_spec.rb +30 -0
- data/spec/spec_helper.rb +30 -24
- data/spec/stress.rb +53 -0
- data/spec/worker_spec.rb +6 -11
- metadata +40 -13
data/ChangeLog
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
|
2
|
+
== 2012-07-23 version 0.8.6
|
3
|
+
|
4
|
+
* rdb_backend supports 'max_running' which is added in v0.7.21
|
5
|
+
* Fixed DaemonsLogger to use File::CREAT flag to open a file
|
6
|
+
* Show version when worker starts
|
7
|
+
|
8
|
+
|
2
9
|
== 2012-07-06 version 0.8.5
|
3
10
|
|
4
11
|
* Introduced PerfectQueue::Application::Decider
|
@@ -34,6 +41,16 @@ in case it was called within 1 second
|
|
34
41
|
* Redesigned API
|
35
42
|
|
36
43
|
|
44
|
+
== 2012-07-19 version 0.7.22
|
45
|
+
|
46
|
+
* Fixed RDBBackend SQL
|
47
|
+
|
48
|
+
|
49
|
+
== 2012-07-19 version 0.7.21
|
50
|
+
|
51
|
+
* Added max_running column for fair resource restriction
|
52
|
+
|
53
|
+
|
37
54
|
== 2012-01-24 version 0.7.20
|
38
55
|
|
39
56
|
* Fixed MonitorThread
|
@@ -43,8 +43,8 @@ module PerfectQueue
|
|
43
43
|
# connection test
|
44
44
|
}
|
45
45
|
|
46
|
-
|
47
|
-
SELECT id, timeout, data, created_at, resource
|
46
|
+
@sql = <<SQL
|
47
|
+
SELECT id, timeout, data, created_at, resource, max_running-running AS runnable
|
48
48
|
FROM `#{@table}`
|
49
49
|
LEFT JOIN (
|
50
50
|
SELECT resource AS res, COUNT(1) AS running
|
@@ -52,8 +52,9 @@ LEFT JOIN (
|
|
52
52
|
WHERE timeout > ? AND created_at IS NOT NULL AND resource IS NOT NULL
|
53
53
|
GROUP BY resource
|
54
54
|
) AS R ON resource = res
|
55
|
-
WHERE timeout <= ? AND (running IS NULL OR running
|
56
|
-
ORDER BY timeout ASC
|
55
|
+
WHERE timeout <= ? AND (max_running-running IS NULL OR max_running-running > 0)
|
56
|
+
ORDER BY runnable IS NOT NULL, runnable DESC, timeout ASC
|
57
|
+
LIMIT #{MAX_SELECT_ROW}
|
57
58
|
SQL
|
58
59
|
|
59
60
|
# sqlite doesn't support SELECT ... FOR UPDATE but
|
@@ -78,6 +79,7 @@ SQL
|
|
78
79
|
data BLOB NOT NULL,
|
79
80
|
created_at INT,
|
80
81
|
resource VARCHAR(256),
|
82
|
+
max_running INT,
|
81
83
|
PRIMARY KEY (id)
|
82
84
|
);]
|
83
85
|
connect {
|
@@ -122,14 +124,17 @@ SQL
|
|
122
124
|
def submit(key, type, data, options)
|
123
125
|
now = (options[:now] || Time.now).to_i
|
124
126
|
run_at = (options[:run_at] || now).to_i
|
125
|
-
user = options[:user]
|
126
|
-
|
127
|
+
user = options[:user].to_s
|
128
|
+
max_running = options[:max_running]
|
127
129
|
data = data ? data.dup : {}
|
128
130
|
data['type'] = type
|
129
131
|
|
130
132
|
connect {
|
131
133
|
begin
|
132
|
-
n = @db[
|
134
|
+
n = @db[
|
135
|
+
"INSERT INTO `#{@table}` (id, timeout, data, created_at, resource, max_running) VALUES (?, ?, ?, ?, ?, ?);",
|
136
|
+
key, run_at, data.to_json, now, user, max_running
|
137
|
+
].insert
|
133
138
|
return Task.new(@client, key)
|
134
139
|
rescue Sequel::DatabaseError
|
135
140
|
raise IdempotentAlreadyExistsError, "task key=#{key} already exists"
|
data/lib/perfectqueue/version.rb
CHANGED
data/lib/perfectqueue/worker.rb
CHANGED
@@ -31,21 +31,20 @@ module PerfectQueue
|
|
31
31
|
@runner = runner
|
32
32
|
block = Proc.new { config } if config
|
33
33
|
@config_load_proc = block
|
34
|
-
@finished = false
|
35
34
|
end
|
36
35
|
|
37
36
|
def run
|
38
|
-
@
|
39
|
-
|
37
|
+
@log.info "PerfectQueue #{VERSION}"
|
38
|
+
|
39
|
+
install_signal_handlers do
|
40
40
|
@engine = Engine.new(@runner, load_config)
|
41
41
|
begin
|
42
42
|
@engine.run
|
43
43
|
ensure
|
44
44
|
@engine.shutdown(true)
|
45
45
|
end
|
46
|
-
ensure
|
47
|
-
@sig.shutdown
|
48
46
|
end
|
47
|
+
|
49
48
|
return nil
|
50
49
|
rescue
|
51
50
|
@log.error "#{$!.class}: #{$!}"
|
@@ -112,8 +111,8 @@ module PerfectQueue
|
|
112
111
|
return config
|
113
112
|
end
|
114
113
|
|
115
|
-
def install_signal_handlers
|
116
|
-
SignalQueue.start do |sig|
|
114
|
+
def install_signal_handlers(&block)
|
115
|
+
sig = SignalQueue.start do |sig|
|
117
116
|
sig.trap :TERM do
|
118
117
|
stop(false)
|
119
118
|
end
|
@@ -147,6 +146,12 @@ module PerfectQueue
|
|
147
146
|
|
148
147
|
trap :CHLD, "SIG_IGN"
|
149
148
|
end
|
149
|
+
|
150
|
+
begin
|
151
|
+
block.call
|
152
|
+
ensure
|
153
|
+
sig.shutdown
|
154
|
+
end
|
150
155
|
end
|
151
156
|
end
|
152
157
|
|
data/spec/queue_spec.rb
CHANGED
@@ -36,5 +36,35 @@ describe Backend::RDBCompatBackend do
|
|
36
36
|
t.type.should == 'query'
|
37
37
|
t.key.should == 'query.379474'
|
38
38
|
end
|
39
|
+
|
40
|
+
it 'resource limit' do
|
41
|
+
time = Time.now.to_i
|
42
|
+
|
43
|
+
3.times do |i|
|
44
|
+
queue.submit("test_#{i}", 'user01', {}, :now=>time-(i+1), :user=>'u1', :max_running=>2)
|
45
|
+
end
|
46
|
+
queue.submit("test_5", 'user02', {}, :now=>time, :user=>'u2', :max_running=>2)
|
47
|
+
|
48
|
+
task1 = queue.poll(:now=>time+10)
|
49
|
+
task1.should_not == nil
|
50
|
+
task1.type.should == 'user01'
|
51
|
+
|
52
|
+
task2 = queue.poll(:now=>time+10)
|
53
|
+
task2.should_not == nil
|
54
|
+
task2.type.should == 'user02'
|
55
|
+
|
56
|
+
task3 = queue.poll(:now=>time+10)
|
57
|
+
task3.should_not == nil
|
58
|
+
task3.type.should == 'user01'
|
59
|
+
|
60
|
+
task4 = queue.poll(:now=>time+10)
|
61
|
+
task4.should == nil
|
62
|
+
|
63
|
+
task1.finish!
|
64
|
+
|
65
|
+
task5 = queue.poll(:now=>time+10)
|
66
|
+
task5.should_not == nil
|
67
|
+
task5.type.should == 'user01'
|
68
|
+
end
|
39
69
|
end
|
40
70
|
|
data/spec/spec_helper.rb
CHANGED
@@ -13,32 +13,38 @@ end
|
|
13
13
|
|
14
14
|
require 'fileutils'
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
16
|
+
module QueueTest
|
17
|
+
def self.included(mod)
|
18
|
+
mod.module_eval do
|
19
|
+
let :database_path do
|
20
|
+
'spec/test.db'
|
21
|
+
end
|
22
|
+
|
23
|
+
let :queue_config do
|
24
|
+
{
|
25
|
+
:type => 'rdb_compat',
|
26
|
+
:url => "sqlite://#{database_path}",
|
27
|
+
:table => 'test_tasks',
|
28
|
+
:processor_type => 'thread'
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
let :queue do
|
33
|
+
PerfectQueue.open(queue_config)
|
34
|
+
end
|
35
|
+
|
36
|
+
before do
|
37
|
+
FileUtils.rm_f database_path
|
38
|
+
queue.client.init_database
|
39
|
+
end
|
40
|
+
|
41
|
+
after do
|
42
|
+
queue.close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
37
46
|
end
|
38
47
|
|
39
|
-
def get_test_queue
|
40
|
-
PerfectQueue.open(test_queue_config)
|
41
|
-
end
|
42
48
|
|
43
49
|
include PerfectQueue
|
44
50
|
|
data/spec/stress.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Queue do
|
4
|
+
include QueueTest
|
5
|
+
|
6
|
+
let :thread_num do
|
7
|
+
5
|
8
|
+
end
|
9
|
+
|
10
|
+
let :loop_num do
|
11
|
+
50
|
12
|
+
end
|
13
|
+
|
14
|
+
let :now do
|
15
|
+
Time.now.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def thread_main
|
19
|
+
thread_id = Thread.current.object_id
|
20
|
+
|
21
|
+
loop_num.times do |i|
|
22
|
+
queue.submit("#{thread_id}-#{i}", "type01", {}, :now=>now-10)
|
23
|
+
task = queue.poll(:now=>now, :alive_time=>60)
|
24
|
+
task.should_not == nil
|
25
|
+
task.heartbeat!(:now=>now, :alive_time=>70)
|
26
|
+
task.finish!(:now=>now, :retention_time=>80)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'stress' do
|
31
|
+
puts "stress test with threads=#{thread_num} * loop_num=#{loop_num} = #{thread_num * loop_num} tasks"
|
32
|
+
|
33
|
+
# initialize queue here
|
34
|
+
queue
|
35
|
+
now
|
36
|
+
|
37
|
+
start_at = Time.now
|
38
|
+
(1..thread_num).map {
|
39
|
+
Thread.new(&method(:thread_main))
|
40
|
+
}.each {|thread|
|
41
|
+
thread.join
|
42
|
+
}
|
43
|
+
finish_at = Time.now
|
44
|
+
|
45
|
+
elapsed = finish_at - start_at
|
46
|
+
task_num = thread_num * loop_num
|
47
|
+
puts "#{elapsed} sec."
|
48
|
+
puts "#{task_num / elapsed} req/sec."
|
49
|
+
puts "#{elapsed / task_num} sec/req."
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
data/spec/worker_spec.rb
CHANGED
@@ -29,9 +29,10 @@ class TestApp < PerfectQueue::Application::Dispatch
|
|
29
29
|
end
|
30
30
|
|
31
31
|
describe Worker do
|
32
|
+
include QueueTest
|
33
|
+
|
32
34
|
before do
|
33
|
-
|
34
|
-
@worker = Worker.new(TestApp, test_queue_config)
|
35
|
+
@worker = Worker.new(TestApp, queue_config)
|
35
36
|
@thread = Thread.new {
|
36
37
|
@worker.run
|
37
38
|
}
|
@@ -42,17 +43,11 @@ describe Worker do
|
|
42
43
|
@thread.join
|
43
44
|
end
|
44
45
|
|
45
|
-
def submit(*args)
|
46
|
-
queue = get_test_queue
|
47
|
-
queue.submit(*args)
|
48
|
-
queue.close
|
49
|
-
end
|
50
|
-
|
51
46
|
it 'route' do
|
52
47
|
TestHandler.any_instance.should_receive(:run).once
|
53
48
|
RegexpHandler.any_instance.should_receive(:run).once
|
54
|
-
submit('task01', 'test', {})
|
55
|
-
submit('task02', 'reg01', {})
|
49
|
+
queue.submit('task01', 'test', {})
|
50
|
+
queue.submit('task02', 'reg01', {})
|
56
51
|
sleep 1
|
57
52
|
end
|
58
53
|
|
@@ -72,7 +67,7 @@ describe Worker do
|
|
72
67
|
|
73
68
|
it 'kill reason' do
|
74
69
|
TestHandler.any_instance.should_receive(:kill).once #.with(kind_of(PerfectQueue::CancelRequestedError)) # FIXME 'with' dead locks
|
75
|
-
submit('task01', 'test', {'sleep'=>4})
|
70
|
+
queue.submit('task01', 'test', {'sleep'=>4})
|
76
71
|
sleep 2
|
77
72
|
Process.kill(:TERM, Process.pid)
|
78
73
|
@thread.join
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perfectqueue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sequel
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 3.26.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.26.0
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rake
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: 0.9.2
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.9.2
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rspec
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ~>
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: 2.10.0
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.10.0
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: simplecov
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ~>
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: 0.5.4
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.5.4
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: sqlite3
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ~>
|
@@ -65,7 +85,12 @@ dependencies:
|
|
65
85
|
version: 1.3.3
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.3.3
|
69
94
|
description: Highly available distributed cron built on RDBMS
|
70
95
|
email: frsyuki@gmail.com
|
71
96
|
executables:
|
@@ -112,6 +137,7 @@ files:
|
|
112
137
|
- spec/queue_spec.rb
|
113
138
|
- spec/rdb_compat_backend_spec.rb
|
114
139
|
- spec/spec_helper.rb
|
140
|
+
- spec/stress.rb
|
115
141
|
- spec/worker_spec.rb
|
116
142
|
homepage: https://github.com/treasure-data/perfectqueue
|
117
143
|
licenses: []
|
@@ -133,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
159
|
version: '0'
|
134
160
|
requirements: []
|
135
161
|
rubyforge_project:
|
136
|
-
rubygems_version: 1.8.
|
162
|
+
rubygems_version: 1.8.23
|
137
163
|
signing_key:
|
138
164
|
specification_version: 3
|
139
165
|
summary: Highly available distributed cron built on RDBMS
|
@@ -141,5 +167,6 @@ test_files:
|
|
141
167
|
- spec/queue_spec.rb
|
142
168
|
- spec/rdb_compat_backend_spec.rb
|
143
169
|
- spec/spec_helper.rb
|
170
|
+
- spec/stress.rb
|
144
171
|
- spec/worker_spec.rb
|
145
172
|
has_rdoc: false
|