perfectqueue 0.7.32 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitignore +6 -0
  2. data/ChangeLog +0 -62
  3. data/Gemfile +3 -0
  4. data/README.md +239 -0
  5. data/Rakefile +19 -0
  6. data/lib/perfectqueue.rb +68 -4
  7. data/lib/perfectqueue/application.rb +30 -0
  8. data/lib/perfectqueue/application/base.rb +27 -0
  9. data/lib/perfectqueue/application/dispatch.rb +73 -0
  10. data/lib/perfectqueue/application/router.rb +69 -0
  11. data/lib/perfectqueue/backend.rb +44 -47
  12. data/lib/perfectqueue/backend/rdb_compat.rb +298 -0
  13. data/lib/perfectqueue/blocking_flag.rb +84 -0
  14. data/lib/perfectqueue/client.rb +117 -0
  15. data/lib/perfectqueue/command/perfectqueue.rb +108 -323
  16. data/lib/perfectqueue/daemons_logger.rb +80 -0
  17. data/lib/perfectqueue/engine.rb +85 -123
  18. data/lib/perfectqueue/error.rb +53 -0
  19. data/lib/perfectqueue/model.rb +37 -0
  20. data/lib/perfectqueue/multiprocess.rb +31 -0
  21. data/lib/perfectqueue/multiprocess/child_process.rb +108 -0
  22. data/lib/perfectqueue/multiprocess/child_process_monitor.rb +109 -0
  23. data/lib/perfectqueue/multiprocess/fork_processor.rb +164 -0
  24. data/lib/perfectqueue/multiprocess/thread_processor.rb +123 -0
  25. data/lib/perfectqueue/queue.rb +58 -0
  26. data/lib/perfectqueue/runner.rb +39 -0
  27. data/lib/perfectqueue/signal_queue.rb +112 -0
  28. data/lib/perfectqueue/task.rb +103 -0
  29. data/lib/perfectqueue/task_metadata.rb +98 -0
  30. data/lib/perfectqueue/task_monitor.rb +189 -0
  31. data/lib/perfectqueue/task_status.rb +27 -0
  32. data/lib/perfectqueue/version.rb +1 -3
  33. data/lib/perfectqueue/worker.rb +114 -196
  34. data/perfectqueue.gemspec +24 -0
  35. data/spec/queue_spec.rb +234 -0
  36. data/spec/spec_helper.rb +44 -0
  37. data/spec/worker_spec.rb +81 -0
  38. metadata +93 -40
  39. checksums.yaml +0 -7
  40. data/README.rdoc +0 -224
  41. data/lib/perfectqueue/backend/null.rb +0 -33
  42. data/lib/perfectqueue/backend/rdb.rb +0 -181
  43. data/lib/perfectqueue/backend/simpledb.rb +0 -139
  44. data/test/backend_test.rb +0 -259
  45. data/test/cat.sh +0 -2
  46. data/test/echo.sh +0 -4
  47. data/test/exec_test.rb +0 -61
  48. data/test/fail.sh +0 -2
  49. data/test/huge.sh +0 -2
  50. data/test/stress.rb +0 -99
  51. data/test/success.sh +0 -2
  52. data/test/test_helper.rb +0 -19
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .bundle
2
+ Gemfile.lock
3
+ vendor/*
4
+ coverage/*
5
+ pkg/*
6
+ spec/test.db
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
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
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
- require 'perfectqueue/engine'
2
- require 'perfectqueue/worker'
3
- require 'perfectqueue/backend'
4
- require 'perfectqueue/version'
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
+