perfectqueue 0.7.32 → 0.8.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.
- 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
|
+
|