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.
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
@@ -0,0 +1,44 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
2
+
3
+ require 'perfectqueue'
4
+
5
+ if ENV['SIMPLE_COV']
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ add_filter 'spec/'
9
+ add_filter 'pkg/'
10
+ add_filter 'vendor/'
11
+ end
12
+ end
13
+
14
+ require 'fileutils'
15
+
16
+ def test_queue_config
17
+ {:type=>'rdb_compat', :url=>'sqlite://spec/test.db', :table=>'test_tasks', :processor_type=>'thread'}
18
+ end
19
+
20
+ def create_test_queue
21
+ FileUtils.rm_f 'spec/test.db'
22
+ queue = PerfectQueue.open(test_queue_config)
23
+
24
+ sql = %[
25
+ CREATE TABLE IF NOT EXISTS `test_tasks` (
26
+ id VARCHAR(256) NOT NULL,
27
+ timeout INT NOT NULL,
28
+ data BLOB NOT NULL,
29
+ created_at INT,
30
+ resource VARCHAR(256),
31
+ PRIMARY KEY (id)
32
+ );]
33
+
34
+ queue.client.backend.db.run sql
35
+
36
+ return queue
37
+ end
38
+
39
+ def get_test_queue
40
+ PerfectQueue.open(test_queue_config)
41
+ end
42
+
43
+ include PerfectQueue
44
+
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ class TestHandler < PerfectQueue::Application::Base
4
+ def run
5
+ puts "TestHandler: #{task}"
6
+ if task.data['raise_error']
7
+ raise "expected error test"
8
+ end
9
+ if num = task.data['sleep']
10
+ sleep num
11
+ end
12
+ puts "Task finished"
13
+ end
14
+
15
+ def kill(reason)
16
+ puts "kill: #{reason.class}: #{reason}"
17
+ end
18
+ end
19
+
20
+ class RegexpHandler < PerfectQueue::Application::Base
21
+ def run
22
+ puts "RegexpHandler: #{task}"
23
+ end
24
+ end
25
+
26
+ class TestApp < PerfectQueue::Application::Dispatch
27
+ route 'test' => TestHandler
28
+ route /reg.*/ => RegexpHandler
29
+ end
30
+
31
+ describe Worker do
32
+ before do
33
+ create_test_queue.close
34
+ @worker = Worker.new(TestApp, test_queue_config)
35
+ @thread = Thread.new {
36
+ @worker.run
37
+ }
38
+ end
39
+
40
+ after do
41
+ @worker.stop(true)
42
+ @thread.join
43
+ end
44
+
45
+ def submit(*args)
46
+ queue = get_test_queue
47
+ queue.submit(*args)
48
+ queue.close
49
+ end
50
+
51
+ it 'route' do
52
+ TestHandler.any_instance.should_receive(:run).once
53
+ RegexpHandler.any_instance.should_receive(:run).once
54
+ submit('task01', 'test', {})
55
+ submit('task02', 'reg01', {})
56
+ sleep 1
57
+ end
58
+
59
+ it 'term signal' do
60
+ sleep 1
61
+ Process.kill(:TERM, Process.pid)
62
+ puts "finish expected..."
63
+ @thread.join
64
+ end
65
+
66
+ it 'quit signal' do
67
+ sleep 1
68
+ Process.kill(:QUIT, Process.pid)
69
+ puts "finish expected..."
70
+ @thread.join
71
+ end
72
+
73
+ it 'kill reason' do
74
+ TestHandler.any_instance.should_receive(:kill).once #.with(kind_of(PerfectQueue::CancelRequestedError)) # FIXME 'with' dead locks
75
+ submit('task01', 'test', {'sleep'=>4})
76
+ sleep 2
77
+ Process.kill(:TERM, Process.pid)
78
+ @thread.join
79
+ end
80
+ end
81
+
metadata CHANGED
@@ -1,89 +1,142 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perfectqueue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.32
4
+ version: 0.8.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Sadayuki Furuhashi
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2016-07-22 00:00:00.000000000 Z
12
+ date: 2012-05-23 00:00:00.000000000Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: sequel
15
- requirement: !ruby/object:Gem::Requirement
16
+ requirement: &70295369852020 !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - "~>"
19
+ - - ~>
18
20
  - !ruby/object:Gem::Version
19
- version: 3.48.0
21
+ version: 3.26.0
20
22
  type: :runtime
21
23
  prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
24
+ version_requirements: *70295369852020
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70295369850460 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.9.2
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70295369850460
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70295369849140 !ruby/object:Gem::Requirement
39
+ none: false
23
40
  requirements:
24
- - - "~>"
41
+ - - ~>
25
42
  - !ruby/object:Gem::Version
26
- version: 3.48.0
27
- description:
43
+ version: 2.10.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70295369849140
47
+ - !ruby/object:Gem::Dependency
48
+ name: simplecov
49
+ requirement: &70295369848300 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.5.4
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70295369848300
58
+ - !ruby/object:Gem::Dependency
59
+ name: sqlite3
60
+ requirement: &70295369847400 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.3.3
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70295369847400
69
+ description: Highly available distributed cron built on RDBMS
28
70
  email: frsyuki@gmail.com
29
71
  executables:
30
72
  - perfectqueue
31
73
  extensions: []
32
- extra_rdoc_files:
33
- - ChangeLog
34
- - README.rdoc
74
+ extra_rdoc_files: []
35
75
  files:
76
+ - .gitignore
36
77
  - ChangeLog
37
- - README.rdoc
78
+ - Gemfile
79
+ - README.md
80
+ - Rakefile
38
81
  - bin/perfectqueue
39
82
  - lib/perfectqueue.rb
83
+ - lib/perfectqueue/application.rb
84
+ - lib/perfectqueue/application/base.rb
85
+ - lib/perfectqueue/application/dispatch.rb
86
+ - lib/perfectqueue/application/router.rb
40
87
  - lib/perfectqueue/backend.rb
41
- - lib/perfectqueue/backend/null.rb
42
- - lib/perfectqueue/backend/rdb.rb
43
- - lib/perfectqueue/backend/simpledb.rb
88
+ - lib/perfectqueue/backend/rdb_compat.rb
89
+ - lib/perfectqueue/blocking_flag.rb
90
+ - lib/perfectqueue/client.rb
44
91
  - lib/perfectqueue/command/perfectqueue.rb
92
+ - lib/perfectqueue/daemons_logger.rb
45
93
  - lib/perfectqueue/engine.rb
94
+ - lib/perfectqueue/error.rb
95
+ - lib/perfectqueue/model.rb
96
+ - lib/perfectqueue/multiprocess.rb
97
+ - lib/perfectqueue/multiprocess/child_process.rb
98
+ - lib/perfectqueue/multiprocess/child_process_monitor.rb
99
+ - lib/perfectqueue/multiprocess/fork_processor.rb
100
+ - lib/perfectqueue/multiprocess/thread_processor.rb
101
+ - lib/perfectqueue/queue.rb
102
+ - lib/perfectqueue/runner.rb
103
+ - lib/perfectqueue/signal_queue.rb
104
+ - lib/perfectqueue/task.rb
105
+ - lib/perfectqueue/task_metadata.rb
106
+ - lib/perfectqueue/task_monitor.rb
107
+ - lib/perfectqueue/task_status.rb
46
108
  - lib/perfectqueue/version.rb
47
109
  - lib/perfectqueue/worker.rb
48
- - test/backend_test.rb
49
- - test/cat.sh
50
- - test/echo.sh
51
- - test/exec_test.rb
52
- - test/fail.sh
53
- - test/huge.sh
54
- - test/stress.rb
55
- - test/success.sh
56
- - test/test_helper.rb
110
+ - perfectqueue.gemspec
111
+ - spec/queue_spec.rb
112
+ - spec/spec_helper.rb
113
+ - spec/worker_spec.rb
57
114
  homepage: https://github.com/treasure-data/perfectqueue
58
115
  licenses: []
59
- metadata: {}
60
116
  post_install_message:
61
117
  rdoc_options: []
62
118
  require_paths:
63
119
  - lib
64
120
  required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
65
122
  requirements:
66
- - - ">="
123
+ - - ! '>='
67
124
  - !ruby/object:Gem::Version
68
125
  version: '0'
69
126
  required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
70
128
  requirements:
71
- - - ">="
129
+ - - ! '>='
72
130
  - !ruby/object:Gem::Version
73
131
  version: '0'
74
132
  requirements: []
75
133
  rubyforge_project:
76
- rubygems_version: 2.5.1
134
+ rubygems_version: 1.8.12
77
135
  signing_key:
78
- specification_version: 4
79
- summary: Highly available distributed queue built on RDBMS or SimpleDB
136
+ specification_version: 3
137
+ summary: Highly available distributed cron built on RDBMS
80
138
  test_files:
81
- - test/backend_test.rb
82
- - test/exec_test.rb
83
- - test/stress.rb
84
- - test/test_helper.rb
85
- - test/cat.sh
86
- - test/echo.sh
87
- - test/fail.sh
88
- - test/huge.sh
89
- - test/success.sh
139
+ - spec/queue_spec.rb
140
+ - spec/spec_helper.rb
141
+ - spec/worker_spec.rb
142
+ has_rdoc: false
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: f80914c5d9cacd678348735b7bb64343153ee3c0
4
- data.tar.gz: 5e5c88e7748b4d8ad13e5b65ceb569935434936c
5
- SHA512:
6
- metadata.gz: 0f4d15f8e6ac83846de46a1363a92a260f30568480117f3536a154fdd06ffc874837f11c8df030ccadbcc827562d7448e82ae6463eb9c44dc4e37a50bedbe3d0
7
- data.tar.gz: 3d433c82757132a7866be3cb13ec6d626fe049b760c43ef52e5f55b11b6dce9d6ed35229c6a117a67f2da7623ac8a3cac303c2877ba3d3ff673e7ff818ee5ca0
data/README.rdoc DELETED
@@ -1,224 +0,0 @@
1
- = PerfectQueue
2
-
3
- Highly available distributed queue.
4
-
5
- It provides exactly-once semantics unless backend database fails. Pushed tasks are surely retried by another worker node even if a worker fails. And it never delivers finished/canceled tasks.
6
-
7
- Backend database is pluggable. You can use any databases that supports CAS (compare-and-swap) operation. PerfectQueue supports RDBMS and Amazon SimpleDB for now.
8
-
9
-
10
- == Architecture
11
-
12
- PerfectQueue uses following database schema:
13
-
14
- (
15
- id:string -- unique identifier of the task
16
- data:blob -- additional attributes of the task
17
- created_at:int -- unix time when the task is created (or null for canceled tasks)
18
- timeout:int
19
- )
20
-
21
- 1. list: lists tasks whose timeout column is old enough.
22
- 2. lock: updates timeout column of the first task
23
- 3. run: executes a command
24
- * if the task takes long time, updates the timeout column. this is repeated until the task is finished
25
- * if the task takes more long time, kills the process
26
- 4. remove: if it succeeded, removes the row from the backend database
27
- 5. or leave: if it failed, leave the row and expect to be retried
28
-
29
-
30
- == Usage
31
-
32
- === Submitting a task
33
-
34
- *Using* *command* *line:*
35
-
36
- # RDBMS
37
- $ perfectqueue \
38
- --database mysql://user:password@localhost/mydb \
39
- --table perfectqueue \
40
- --push unique-key-id '{"any":"data"}'
41
-
42
- # SimpleDB
43
- $ perfectqueue \
44
- --simpledb your-simpledb-domain-name \
45
- -k AWS_KEY_ID \
46
- -s AWS_SECRET_KEY \
47
- --push unique-key-id '{"any":"data"}'
48
-
49
- *Using* *PerfectQueue* *library:*
50
-
51
- require 'perfectqueue'
52
-
53
- # RDBMS
54
- require 'perfectqueue/backend/rdb'
55
- queue = PerfectQueue::RDBBackend.new(
56
- 'mysql://user:password@localhost/mydb', table='perfectqueue')
57
-
58
- # SimpleDB
59
- require 'perfectqueue/backend/simpledb'
60
- queue = PerfectQueue::SimpleDBBackend.new(
61
- 'AWS_KEY_ID', 'AWS_SECRET_KEY', 'your-simpledb-domain-name')
62
-
63
- queue.submit('unique-key-id', '{"any":"data"}')
64
-
65
-
66
- Alternatively, you can insert a row into the backend database directly.
67
-
68
- *RDBMS:*
69
-
70
- > CREATE TABLE IF NOT EXISTS perfectqueue (
71
- id VARCHAR(256) NOT NULL,
72
- timeout INT NOT NULL,
73
- data BLOB NOT NULL,
74
- created_at INT,
75
- PRIMARY KEY (id)
76
- );
77
- > SET @now = UNIX_TIMESTAMP();
78
- > INSERT INTO perfectqueue (id, timeout, data, created_at)
79
- VALUES ('unique-task-id', @now, '{"any":"data"}', @now);
80
-
81
- *SimpleDB:*
82
-
83
- require 'aws' # gem install aws-sdk
84
- queue = AWS::SimpleDB.new
85
- domain = queue.domains['your-simpledb-domain-name']
86
-
87
- now = "%08x" % Time.now.to_i
88
- domain.items['unique-task-id'].attributes.replace(
89
- 'timeout'=>now, 'data'=>'{"any":"data"}', 'created_at'=>now,
90
- :unless=>'timeout')
91
-
92
-
93
- === Canceling a queued task
94
-
95
- *Using* *command* *line:*
96
-
97
- $ perfectqueue ... --cancel unique-key-id
98
-
99
- *Using* *PerfectQueue* *library:*
100
-
101
- queue.cancel('unique-key-id')
102
-
103
-
104
- Alternatively, you can delete a row from the backend database directly.
105
-
106
- *RDBMS:*
107
-
108
- > DELETE FROM perfectqueue WHERE id='unique-key-id';
109
-
110
- *SimpleDB:*
111
-
112
- domain.items['unique-task-id'].delete
113
-
114
-
115
- === Running worker node
116
-
117
- Use _perfectqueue_ command to execute a command.
118
-
119
- Usage: perfectqueue [options] [-- <ARGV-for-exec-or-run>]
120
-
121
- --push <ID> <DATA> Push a task to the queue
122
- --list Show queued tasks
123
- --cancel <ID> Cancel a queued task
124
- --configure <PATH.yaml> Write configuration file
125
-
126
- --exec <COMMAND> Execute command
127
- --run <SCRIPT.rb> Run method named 'run' defined in the script
128
-
129
- -f, --file <PATH.yaml> Read configuration file
130
- -C, --run-class Class name for --run (default: ::Run)
131
- -t, --timeout <SEC> Time for another worker to take over a task when this worker goes down (default: 600)
132
- -b, --heartbeat-interval <SEC> Threshold time to extend the timeout (heartbeat interval) (default: timeout * 3/4)
133
- -x, --kill-timeout <SEC> Threshold time to kill a task process (default: timeout * 10)
134
- -X, --kill-interval <SEC> Threshold time to retry killing a task process (default: 60)
135
- -i, --poll-interval <SEC> Polling interval (default: 1)
136
- -r, --retry-wait <SEC> Time to retry a task when it is failed (default: same as timeout)
137
- -e, --expire <SEC> Threshold time to expire a task (default: 345601 (4days))
138
-
139
- --database <URI> Use RDBMS for the backend database (e.g.: mysql://user:password@localhost/mydb)
140
- --table <NAME> backend: name of the table (default: perfectqueue)
141
- --simpledb <DOMAIN> Use Amazon SimpleDB for the backend database (e.g.: --simpledb mydomain -k KEY_ID -s SEC_KEY)
142
- -k, --key-id <ID> AWS Access Key ID
143
- -s, --secret-key <KEY> AWS Secret Access Key
144
-
145
- -w, --worker <NUM> Number of worker threads (default: 1)
146
- -d, --daemon <PIDFILE> Daemonize (default: foreground)
147
- --env K=V Set environment variable
148
- -o, --log <PATH> log file path
149
- -v, --verbose verbose mode
150
-
151
-
152
- ==== --exec
153
-
154
- Execute a command when a task is received. The the data column is passed to the stdin and the id column passed to the last argument. The command have to exit with status code 0 when it succeeded.
155
-
156
- *Example:*
157
-
158
- #!/usr/bin/env ruby
159
-
160
- require 'json'
161
- js = JSON.load(STDIN.read)
162
- puts "received: id=#{ARGV.last} #{js.inspect}"
163
-
164
- #$ perfectqueue --database sqlite://test.db \
165
- --exec ./this_file -- my cmd args
166
-
167
- When the kill timeout (-x, --kill-timeout) is elapsed, SIGTERM signal will be sent to the child process. The signal will be repeated every few seconds (-X, --kill-interval).
168
-
169
-
170
- ==== --run
171
-
172
- This is same as 'exec' except that it creates a instance of a class named 'Run' defined in the file. The class should has 'initialize(task)', 'run' and 'kill' methods. You can get data column and id column of the task from the argument of the initialize method. It is assumed it succeeded if the method doesn't raise any errors.
173
-
174
- *Example:*
175
-
176
- require 'json'
177
-
178
- class Run
179
- def initialize(task)
180
- @task = task
181
- end
182
-
183
- def run
184
- js = JSON.load(@task.data)
185
- puts "received: id=#{@task.id} #{js.inspect}"
186
- end
187
-
188
- def kill
189
- puts "kill!"
190
- end
191
- end
192
-
193
- #$ perfectqueue --database sqlite://test.db \
194
- --run ./this_file.rb -- my cmd args
195
-
196
- When the kill timeout (-x, --klill-timeout) is elapsed, Run#kill method will be called (if it is defined). It will be repeated every few seconds (-X, --kill-retry).
197
-
198
-
199
- ==== --configure
200
-
201
- Write configuration file and exit. Written configuration file can be used with -f option:
202
-
203
- *Example:*
204
-
205
- ## create myqueue.yaml file
206
- $ perfectqueue --database mysql://root:my@localhost/mydb \
207
- --run myrun.rb -- my cmd args \
208
- --configure myqueue.yaml
209
-
210
- ## run perfectqueue using the configuration file
211
- $ perfectqueue -f myqueue.yaml
212
-
213
-
214
- ==== --list
215
-
216
- Show queued tasks.
217
-
218
- *Example:*
219
-
220
- $ perfectqueue --database sqlite://test.db --list
221
- id created_at timeout data
222
- task1 2011-08-23 23:07:45 +0900 2011-08-23 23:07:45 +0900 {"attr1":"val1","attr":"val2"}
223
- 1 entries.
224
-