perfectqueue 0.7.32 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/ChangeLog +0 -62
- data/Gemfile +3 -0
- data/README.md +239 -0
- data/Rakefile +19 -0
- data/lib/perfectqueue.rb +68 -4
- data/lib/perfectqueue/application.rb +30 -0
- data/lib/perfectqueue/application/base.rb +27 -0
- data/lib/perfectqueue/application/dispatch.rb +73 -0
- data/lib/perfectqueue/application/router.rb +69 -0
- data/lib/perfectqueue/backend.rb +44 -47
- data/lib/perfectqueue/backend/rdb_compat.rb +298 -0
- data/lib/perfectqueue/blocking_flag.rb +84 -0
- data/lib/perfectqueue/client.rb +117 -0
- data/lib/perfectqueue/command/perfectqueue.rb +108 -323
- data/lib/perfectqueue/daemons_logger.rb +80 -0
- data/lib/perfectqueue/engine.rb +85 -123
- data/lib/perfectqueue/error.rb +53 -0
- data/lib/perfectqueue/model.rb +37 -0
- data/lib/perfectqueue/multiprocess.rb +31 -0
- data/lib/perfectqueue/multiprocess/child_process.rb +108 -0
- data/lib/perfectqueue/multiprocess/child_process_monitor.rb +109 -0
- data/lib/perfectqueue/multiprocess/fork_processor.rb +164 -0
- data/lib/perfectqueue/multiprocess/thread_processor.rb +123 -0
- data/lib/perfectqueue/queue.rb +58 -0
- data/lib/perfectqueue/runner.rb +39 -0
- data/lib/perfectqueue/signal_queue.rb +112 -0
- data/lib/perfectqueue/task.rb +103 -0
- data/lib/perfectqueue/task_metadata.rb +98 -0
- data/lib/perfectqueue/task_monitor.rb +189 -0
- data/lib/perfectqueue/task_status.rb +27 -0
- data/lib/perfectqueue/version.rb +1 -3
- data/lib/perfectqueue/worker.rb +114 -196
- data/perfectqueue.gemspec +24 -0
- data/spec/queue_spec.rb +234 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/worker_spec.rb +81 -0
- metadata +93 -40
- checksums.yaml +0 -7
- data/README.rdoc +0 -224
- data/lib/perfectqueue/backend/null.rb +0 -33
- data/lib/perfectqueue/backend/rdb.rb +0 -181
- data/lib/perfectqueue/backend/simpledb.rb +0 -139
- data/test/backend_test.rb +0 -259
- data/test/cat.sh +0 -2
- data/test/echo.sh +0 -4
- data/test/exec_test.rb +0 -61
- data/test/fail.sh +0 -2
- data/test/huge.sh +0 -2
- data/test/stress.rb +0 -99
- data/test/success.sh +0 -2
- data/test/test_helper.rb +0 -19
data/.gitignore
ADDED
data/ChangeLog
CHANGED
@@ -1,65 +1,3 @@
|
|
1
|
-
== 2016-07-22 version 0.7.32
|
2
|
-
|
3
|
-
DON'T USE THIS VERSION UNLESS YOU KNOW WHAT THIS IS!
|
4
|
-
This version is for migration v07 to v0.8.48.
|
5
|
-
|
6
|
-
* Use PerfectQueue v0.8.48's timeout model (#41)
|
7
|
-
|
8
|
-
|
9
|
-
== 2014-10-08 version 0.7.31
|
10
|
-
|
11
|
-
* Upgraded sequal v3.48.0
|
12
|
-
|
13
|
-
|
14
|
-
== 2014-04-24 version 0.7.30
|
15
|
-
|
16
|
-
* RDBBackend: fixed NoMethodError for @url
|
17
|
-
|
18
|
-
|
19
|
-
== 2014-04-24 version 0.7.29
|
20
|
-
|
21
|
-
* RDBBackend: add MySQL's SSL support
|
22
|
-
|
23
|
-
|
24
|
-
== 2014-02-18 version 0.7.28
|
25
|
-
|
26
|
-
* RDBBackend: wrap blob data in Sequel::SQL::Blob
|
27
|
-
|
28
|
-
|
29
|
-
== 2013-06-16 version 0.7.27
|
30
|
-
|
31
|
-
* RDBBackend: #issue should not ignore erros excepting duplicate entry error
|
32
|
-
|
33
|
-
|
34
|
-
== 2013-06-14 version 0.7.26
|
35
|
-
|
36
|
-
* RDBBackend: #issue should not ignore erros excepting duplicate entry error
|
37
|
-
|
38
|
-
|
39
|
-
== 2012-09-19 version 0.7.25
|
40
|
-
|
41
|
-
* removed depenedency on aws-sdk which is optional
|
42
|
-
|
43
|
-
|
44
|
-
== 2012-08-10 version 0.7.24
|
45
|
-
|
46
|
-
* Kill running task if an unexpected error occured during extending timeout
|
47
|
-
|
48
|
-
|
49
|
-
== 2012-08-03 version 0.7.23
|
50
|
-
|
51
|
-
* Use free slot ratio instead of free slot amount to decide task priorities
|
52
|
-
|
53
|
-
|
54
|
-
== 2012-07-19 version 0.7.22
|
55
|
-
|
56
|
-
* Fixed RDBBackend SQL
|
57
|
-
|
58
|
-
|
59
|
-
== 2012-07-19 version 0.7.21
|
60
|
-
|
61
|
-
* Added max_running column for fair resource restriction
|
62
|
-
|
63
1
|
|
64
2
|
== 2012-01-24 version 0.7.20
|
65
3
|
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
# PerfectQueue
|
2
|
+
|
3
|
+
PerfectQueue is a highly available distributed queue built on top of RDBMS.
|
4
|
+
It provides at-least-once semantics; Even if a worker node fails during processing a task, the task is retried by another worker.
|
5
|
+
PerfectQueue provides similar API to Amazon SQS. But unlike Amazon SQS, PerfectQueue never delivers finished tasks.
|
6
|
+
|
7
|
+
Since PerfectQueue also prevents storing a same task twice by using unique task identifier, client applications can retry to submit tasks until it succeeds.
|
8
|
+
|
9
|
+
All you have to consider is implementing idempotent worker programs. PerfectQueue manages the other problems.
|
10
|
+
|
11
|
+
## API overview
|
12
|
+
|
13
|
+
```
|
14
|
+
# open a queue
|
15
|
+
PerfectQueue.open(config, &block) #=> #<Queue>
|
16
|
+
|
17
|
+
# submit a task
|
18
|
+
Queue#submit(task_id, type, data, options={})
|
19
|
+
|
20
|
+
# poll a task
|
21
|
+
# (you don't have to use this method directly. see following sections)
|
22
|
+
Queue#poll #=> #<AcquiredTask>
|
23
|
+
|
24
|
+
# get data associated with a task
|
25
|
+
AcquiredTask#data #=> #<Hash>
|
26
|
+
|
27
|
+
# finish a task
|
28
|
+
AcquiredTask#finish!
|
29
|
+
|
30
|
+
# retry a task
|
31
|
+
AcquiredTask#retry!
|
32
|
+
|
33
|
+
# create a task reference
|
34
|
+
Queue#[](key) #=> #<Task>
|
35
|
+
|
36
|
+
# chack the existance of the task
|
37
|
+
Task#exists?
|
38
|
+
|
39
|
+
# request to cancel a task
|
40
|
+
# (actual behavior depends on the worker program)
|
41
|
+
Task#cancel_request!
|
42
|
+
|
43
|
+
# force finish a task
|
44
|
+
# be aware that worker programs can't detect it
|
45
|
+
Task#force_finish!
|
46
|
+
```
|
47
|
+
|
48
|
+
### Error classes
|
49
|
+
|
50
|
+
```
|
51
|
+
TaskError
|
52
|
+
|
53
|
+
##
|
54
|
+
# Workers may get these errors:
|
55
|
+
#
|
56
|
+
|
57
|
+
CancelRequestedError < TaskError
|
58
|
+
|
59
|
+
AlreadyFinishedError < TaskError
|
60
|
+
|
61
|
+
PreemptedError < TaskError
|
62
|
+
|
63
|
+
ProcessStopError < RuntimeError
|
64
|
+
|
65
|
+
ImmediateProcessStopError < ProcessStopError
|
66
|
+
|
67
|
+
GracefulProcessStopError < ProcessStopError
|
68
|
+
|
69
|
+
##
|
70
|
+
# Client or other situation:
|
71
|
+
#
|
72
|
+
|
73
|
+
ConfigError < RuntimeError
|
74
|
+
|
75
|
+
NotFoundError < TaskError
|
76
|
+
|
77
|
+
AlreadyExistsError < TaskError
|
78
|
+
|
79
|
+
NotSupportedError < TaskError
|
80
|
+
```
|
81
|
+
|
82
|
+
|
83
|
+
### Example
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# submit tasks
|
87
|
+
PerfectQueue.open(config) {|queue|
|
88
|
+
data = {'key'=>"value"}
|
89
|
+
queue.submit("task-id", "type1", data)
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
|
94
|
+
## Writing a worker application
|
95
|
+
|
96
|
+
### 1. Implement PerfectQueue::Application::Base
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class TestHandler < PerfectQueue::Application::Base
|
100
|
+
# implement run method
|
101
|
+
def run
|
102
|
+
# do something ...
|
103
|
+
puts "acquired task: #{task.inspect}"
|
104
|
+
|
105
|
+
# call task.finish!, task.retry! or task.release!
|
106
|
+
task.finish!
|
107
|
+
end
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
### 2. Implement PerfectQueue::Application::Dispatch
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
class Dispatch < PerfectQueue::Application::Dispatch
|
115
|
+
# describe routing
|
116
|
+
route "type1" => TestHandler
|
117
|
+
route /^regexp-.*$/ => :TestHandler # String or Regexp => Class or Symbol
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
### 3. Run the worker
|
122
|
+
|
123
|
+
In a launcher script or rake file:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
system('perfectqueue run -I. -rapp/workers/dispatch Dispatch')
|
127
|
+
```
|
128
|
+
|
129
|
+
or:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
require 'perfectqueue'
|
133
|
+
require 'app/workers/dispatch'
|
134
|
+
|
135
|
+
PerfectQueue::Worker.run(Dispatch) {
|
136
|
+
# this method is called when the worker process is restarted
|
137
|
+
raw = File.read('config/perfectqueue.yml')
|
138
|
+
yml = YAJL.load(raw)
|
139
|
+
yml[ENV['RAILS_ENV'] || 'development']
|
140
|
+
}
|
141
|
+
```
|
142
|
+
|
143
|
+
### Signal handlers
|
144
|
+
|
145
|
+
- **TERM**,**INT:** graceful shutdown
|
146
|
+
- **QUIT:** immediate shutdown
|
147
|
+
- **USR1:** graceful restart
|
148
|
+
- **HUP:** immediate restart
|
149
|
+
- **WINCH:** immediate binary replace
|
150
|
+
- **CONT:** graceful binary replace
|
151
|
+
- **USR2:** reopen log files
|
152
|
+
|
153
|
+
## Configuration
|
154
|
+
|
155
|
+
- **type:** backend type (required; see following sections)
|
156
|
+
- **log:** log file path (default: use stderr)
|
157
|
+
- **processors:** number of child processes (default: 1)
|
158
|
+
- **processor_type:** type of processor ('process' or 'thread') (default: 'process')
|
159
|
+
- **poll_interval:** interval to poll tasks in seconds (default: 1.0 sec)
|
160
|
+
- **retention_time:** duration to retain finished tasks (default: 300 sec)
|
161
|
+
- **task_heartbeat_interval:** interval to send heartbeat requests (default: 2 sec)
|
162
|
+
- **alive_time:** duration to continue a heartbeat request (default: 300 sec)
|
163
|
+
- **retry_wait:** duration to retry a retried task (default: 300 sec)
|
164
|
+
- **child_kill_interval:** interval to send signals to a child process (default: 2.0 sec)
|
165
|
+
- **child_graceful_kill_limit:** threshold time to switch SIGTERM to SIGKILL (default: never)
|
166
|
+
- **child_heartbeat_interval:** interval to send heartbeat packets to a child process (default: 2 sec)
|
167
|
+
- **child_heartbeat_limit:** threshold time to detect freeze of a child process (default: 10.0 sec)
|
168
|
+
|
169
|
+
## Backend types
|
170
|
+
|
171
|
+
### rdb\_compat
|
172
|
+
|
173
|
+
additional configuration:
|
174
|
+
|
175
|
+
- **url:** URL to the RDBMS (example: 'mysql://user:password@host:port/database')
|
176
|
+
- **table:** name of the table to use
|
177
|
+
|
178
|
+
### rdb
|
179
|
+
|
180
|
+
Not implemented yet.
|
181
|
+
|
182
|
+
|
183
|
+
## Command line management tool
|
184
|
+
|
185
|
+
```
|
186
|
+
Usage: perfectqueue [options] <command>
|
187
|
+
|
188
|
+
commands:
|
189
|
+
list Show list of tasks
|
190
|
+
submit <key> <type> <data> Submit a new task
|
191
|
+
cancel_request <key> Cancel request
|
192
|
+
force_finish <key> Force finish a task
|
193
|
+
run <class> Run a worker process
|
194
|
+
init Initialize a backend database
|
195
|
+
|
196
|
+
options:
|
197
|
+
-e, --environment ENV Framework environment (default: development)
|
198
|
+
-c, --config PATH.yml Path to a configuration file (default: config/perfectqueue.yml)
|
199
|
+
|
200
|
+
options for submit:
|
201
|
+
-u, --user USER Set user
|
202
|
+
-t, --time UNIXTIME Set time to run the task
|
203
|
+
|
204
|
+
options for run:
|
205
|
+
-I, --include PATH Add $LOAD_PATH directory
|
206
|
+
-r, --require PATH Require files before starting
|
207
|
+
```
|
208
|
+
|
209
|
+
|
210
|
+
### initializing a database
|
211
|
+
|
212
|
+
# assume that the config/perfectqueue.yml exists
|
213
|
+
$ perfectqueue init
|
214
|
+
|
215
|
+
### submitting a task
|
216
|
+
|
217
|
+
$ perfectqueue submit k1 user_task '{"uid":1}' -u user_1
|
218
|
+
|
219
|
+
### listing tasks
|
220
|
+
|
221
|
+
$ perfectqueue list
|
222
|
+
key type user status created_at timeout data
|
223
|
+
k1 user_task user_1 waiting 2012-05-18 13:05:31 -0700 2012-05-18 14:27:36 -0700 {"uid"=>1, "type"=>"user_task"}
|
224
|
+
k2 user_task user_2 waiting 2012-05-18 13:35:33 -0700 2012-05-18 14:35:33 -0700 {"uid"=>2, "type"=>"user_task"}
|
225
|
+
k3 system_task waiting 2012-05-18 14:04:02 -0700 2012-05-22 15:04:02 -0700 {"task_id"=>32, "type"=>"system_task"}
|
226
|
+
3 entries.
|
227
|
+
|
228
|
+
### cancel a tasks
|
229
|
+
|
230
|
+
$ perfectqueue cancel_request k1
|
231
|
+
|
232
|
+
### force finish a tasks
|
233
|
+
|
234
|
+
$ perfectqueue cancel_request k2
|
235
|
+
|
236
|
+
### running a worker
|
237
|
+
|
238
|
+
$ perfectqueue run -I. -Ilib -rconfig/boot.rb -rapps/workers/task_dispatch.rb TaskDispatch
|
239
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
9
|
+
t.pattern = 'spec/**/*_spec.rb'
|
10
|
+
t.verbose = true
|
11
|
+
end
|
12
|
+
|
13
|
+
task :coverage do |t|
|
14
|
+
ENV['SIMPLE_COV'] = '1'
|
15
|
+
Rake::Task["spec"].invoke
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => :build
|
19
|
+
|
data/lib/perfectqueue.rb
CHANGED
@@ -1,4 +1,68 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
#
|
2
|
+
# PerfectQueue
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module PerfectQueue
|
20
|
+
require 'json'
|
21
|
+
require 'thread' # Mutex, CoditionVariable
|
22
|
+
|
23
|
+
{
|
24
|
+
:Application => 'perfectqueue/application',
|
25
|
+
:Backend => 'perfectqueue/backend',
|
26
|
+
:BackendHelper => 'perfectqueue/backend',
|
27
|
+
:BlockingFlag => 'perfectqueue/blocking_flag',
|
28
|
+
:Client => 'perfectqueue/client',
|
29
|
+
:DaemonsLogger => 'perfectqueue/daemons_logger',
|
30
|
+
:Engine => 'perfectqueue/engine',
|
31
|
+
:Model => 'perfectqueue/model',
|
32
|
+
:Queue => 'perfectqueue/queue',
|
33
|
+
:Runner => 'perfectqueue/runner',
|
34
|
+
:Task => 'perfectqueue/task',
|
35
|
+
:TaskWithMetadata => 'perfectqueue/task',
|
36
|
+
:AcquiredTask => 'perfectqueue/task',
|
37
|
+
:TaskMetadata => 'perfectqueue/task_metadata',
|
38
|
+
:TaskMonitor => 'perfectqueue/task_monitor',
|
39
|
+
:TaskMetadataAccessors => 'perfectqueue/task_metadata',
|
40
|
+
:TaskStatus => 'perfectqueue/task_status',
|
41
|
+
:Worker => 'perfectqueue/worker',
|
42
|
+
:SignalQueue => 'perfectqueue/signal_queue',
|
43
|
+
}.each_pair {|k,v|
|
44
|
+
autoload k, File.expand_path(v, File.dirname(__FILE__))
|
45
|
+
}
|
46
|
+
[
|
47
|
+
'perfectqueue/multiprocess',
|
48
|
+
'perfectqueue/error',
|
49
|
+
].each {|v|
|
50
|
+
require File.expand_path(v, File.dirname(__FILE__))
|
51
|
+
}
|
52
|
+
|
53
|
+
def self.open(config, &block)
|
54
|
+
c = Client.new(config)
|
55
|
+
begin
|
56
|
+
q = Queue.new(c)
|
57
|
+
if block
|
58
|
+
block.call(q)
|
59
|
+
else
|
60
|
+
c = nil
|
61
|
+
return q
|
62
|
+
end
|
63
|
+
ensure
|
64
|
+
c.close if c
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#
|
2
|
+
# PerfectQueue
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module PerfectQueue
|
20
|
+
module Application
|
21
|
+
{
|
22
|
+
:Dispatch => 'application/dispatch',
|
23
|
+
:Router => 'application/router',
|
24
|
+
:Base => 'application/base',
|
25
|
+
}.each_pair {|k,v|
|
26
|
+
autoload k, File.expand_path(v, File.dirname(__FILE__))
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# PerfectQueue
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module PerfectQueue
|
20
|
+
module Application
|
21
|
+
|
22
|
+
class Base < Runner
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|