resque-pool 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +16 -1
- data/README.md +39 -50
- data/Rakefile +14 -0
- data/config/cucumber.yml +3 -0
- data/examples/Gemfile +1 -0
- data/examples/Gemfile.lock +67 -0
- data/examples/chef_cookbook/recipes/default.rb +1 -1
- data/examples/log/resque-pool.stdout.log.2 +53 -0
- data/features/basic_daemon_config.feature +68 -0
- data/features/step_definitions/daemon_steps.rb +29 -0
- data/features/step_definitions/resque-pool_steps.rb +147 -0
- data/features/support/aruba_daemon_support.rb +60 -0
- data/features/support/env.rb +1 -0
- data/lib/resque/pool/cli.rb +40 -20
- data/lib/resque/pool/logging.rb +40 -1
- data/lib/resque/pool/tasks.rb +2 -2
- data/lib/resque/pool/version.rb +1 -1
- data/lib/resque/pool.rb +43 -21
- data/resque-pool.gemspec +10 -5
- data/spec/resque_pool_spec.rb +8 -1
- data/spec/spec_helper.rb +1 -1
- data/tmp/aruba/Rakefile +1 -0
- data/tmp/aruba/config/resque-pool.yml +3 -0
- metadata +81 -31
- data/.gitignore +0 -5
- data/Gemfile.lock +0 -51
- data/config/resque-pool.yml +0 -7
data/Changelog.md
CHANGED
@@ -1,4 +1,19 @@
|
|
1
|
-
##
|
1
|
+
## unreleased
|
2
|
+
|
3
|
+
* new feature: sending `HUP` to pool manager will reload the logfiles and
|
4
|
+
gracefully restart all workers.
|
5
|
+
* enhancement: logging now includes timestamp, process "name" (worker or
|
6
|
+
manager), and PID.
|
7
|
+
* enhancement: can be used with no config file or empty config file (not all
|
8
|
+
*that* useful, but it's better than unceromoniously dieing!)
|
9
|
+
* bugfix: pidfile will be cleaned up on startup, e.g. if old process was
|
10
|
+
kill-9'd (Jason Haruska)
|
11
|
+
* bugfix: TERM/INT are no longer ignored when HUP is waiting on children
|
12
|
+
* bugfix: `resque-pool -c config.yml` command line option was broken
|
13
|
+
* development: simple cucumber features for core functionality.
|
14
|
+
* upstream: depends on resque ~> 1.13
|
15
|
+
|
16
|
+
## 0.1.0 (2011-01-18)
|
2
17
|
|
3
18
|
* new feature: `resque-pool` command line interface
|
4
19
|
* this replaces need for a special startup script.
|
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
Resque Pool
|
2
2
|
===========
|
3
3
|
|
4
|
-
Resque pool is a simple library for managing a pool of
|
5
|
-
a config file, it
|
6
|
-
number of workers for
|
4
|
+
Resque pool is a simple library for managing a pool of
|
5
|
+
[resque](http://github.com/defunkt/resque) workers. Given a a config file, it
|
6
|
+
manages your workers for you, starting up the appropriate number of workers for
|
7
|
+
each worker type.
|
7
8
|
|
8
9
|
Benefits
|
9
10
|
---------
|
@@ -16,17 +17,18 @@ Benefits
|
|
16
17
|
when you are managing many workers.
|
17
18
|
* Faster startup - when you start many workers at once, they would normally
|
18
19
|
compete for CPU as they load their environments. Resque-pool can load the
|
19
|
-
environment once and
|
20
|
+
environment once and fork all of the workers almost instantly.
|
20
21
|
|
21
22
|
How to use
|
22
23
|
-----------
|
23
24
|
|
24
25
|
### YAML file config
|
25
26
|
|
26
|
-
Create a `config/resque-pool.yml` with your worker
|
27
|
-
supports both using root level defaults as well as
|
28
|
-
overrides (`RACK_ENV`, `RAILS_ENV`, and `RESQUE_ENV`
|
29
|
-
be used to determine environment). For example in
|
27
|
+
Create a `config/resque-pool.yml` (or `resque-pool.yml`) with your worker
|
28
|
+
counts. The YAML file supports both using root level defaults as well as
|
29
|
+
environment specific overrides (`RACK_ENV`, `RAILS_ENV`, and `RESQUE_ENV`
|
30
|
+
environment variables can be used to determine environment). For example in
|
31
|
+
`config/resque-pool.yml`:
|
30
32
|
|
31
33
|
foo: 1
|
32
34
|
bar: 2
|
@@ -37,10 +39,11 @@ be used to determine environment). For example in `config/resque-pool.yml`:
|
|
37
39
|
|
38
40
|
### Rake task config
|
39
41
|
|
40
|
-
Require the rake tasks (`resque/pool/tasks`) in your
|
41
|
-
Resque as necessary, and configure
|
42
|
-
|
43
|
-
|
42
|
+
Require the rake tasks (`resque/pool/tasks`) in your `Rakefile`, load your
|
43
|
+
application environment, configure Resque as necessary, and configure
|
44
|
+
`resque:pool:setup` to disconnect all open files and sockets in the pool
|
45
|
+
manager and reconnect in the workers. For example, with rails you should put
|
46
|
+
the following into `lib/tasks/resque.rake`:
|
44
47
|
|
45
48
|
require 'resque/pool/tasks'
|
46
49
|
# this task will get called before resque:pool:setup
|
@@ -61,7 +64,7 @@ rails you should put the following into `lib/tasks/resque.rake`:
|
|
61
64
|
|
62
65
|
Then you can start the queues via:
|
63
66
|
|
64
|
-
resque-pool --environment production
|
67
|
+
resque-pool --daemon --environment production
|
65
68
|
|
66
69
|
This will start up seven worker processes, one exclusively for the foo queue,
|
67
70
|
two exclusively for the bar queue, and four workers looking at all queues in
|
@@ -80,20 +83,19 @@ three levels: a single pool manager, many worker parents, and one worker child
|
|
80
83
|
per worker (when the actual job is being processed). For example, `ps -ef f |
|
81
84
|
grep [r]esque` (in Linux) might return something like the following:
|
82
85
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
want to configure. The `RAILS_ENV` can be specified via `--environment`. See
|
86
|
+
resque 13858 1 0 13:44 ? S 0:02 resque-pool-manager: managing [13867, 13875, 13871, 13872, 13868, 13870, 13876]
|
87
|
+
resque 13867 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for foo
|
88
|
+
resque 13868 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for bar
|
89
|
+
resque 13870 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for bar
|
90
|
+
resque 13871 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for foo,bar,baz
|
91
|
+
resque 13872 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Forked 7481 at 1280343254
|
92
|
+
resque 7481 13872 0 14:54 ? S 0:00 \_ resque-1.9.9: Processing foo since 1280343254
|
93
|
+
resque 13875 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for foo,bar,baz
|
94
|
+
resque 13876 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Forked 7485 at 1280343255
|
95
|
+
resque 7485 13876 0 14:54 ? S 0:00 \_ resque-1.9.9: Processing bar since 1280343254
|
96
|
+
|
97
|
+
Running as a daemon will default to placing the pidfile and logfiles in the
|
98
|
+
conventional rails locations, although you can configure that. See
|
97
99
|
`resque-pool --help` for more options.
|
98
100
|
|
99
101
|
SIGNALS
|
@@ -101,24 +103,23 @@ SIGNALS
|
|
101
103
|
|
102
104
|
The pool manager responds to the following signals:
|
103
105
|
|
104
|
-
* `HUP` - reload the config file,
|
106
|
+
* `HUP` - reload the config file, reload logfiles, restart all workers.
|
105
107
|
* `QUIT` - send `QUIT` to each worker parent and shutdown the manager after all workers are done.
|
106
108
|
* `INT` - send `QUIT` to each worker parent and immediately shutdown manager
|
107
109
|
* `TERM` - send `TERM` to each worker parent and immediately shutdown manager
|
108
110
|
* `WINCH` - send `QUIT` to each worker, but keep manager running (send `HUP` to reload config and restart workers)
|
109
|
-
* `USR1`/`USR2`/`CONT` -
|
111
|
+
* `USR1`/`USR2`/`CONT` - pass the signal on to all worker parents (see Resque docs).
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
+
Use `HUP` to help logrotate run smoothly and to change the number of workers
|
114
|
+
per worker type.
|
113
115
|
|
114
116
|
Other Features
|
115
117
|
--------------
|
116
118
|
|
117
|
-
An example chef
|
118
|
-
provide a `/data/#{app_name}/shared/config/resque-pool.yml` on your
|
119
|
-
|
120
|
-
|
121
|
-
`examples/chef_cookbook/templates/default`.
|
119
|
+
An example chef cookbook is provided (and should Just Work at Engine Yard as
|
120
|
+
is; just provide a `/data/#{app_name}/shared/config/resque-pool.yml` on your
|
121
|
+
utility instances). Even if you don't use chef you can use the example init.d
|
122
|
+
and monitrc erb templates in `examples/chef_cookbook/templates/default`.
|
122
123
|
|
123
124
|
You can also start a pool manager via `rake resque:pool` or from a plain old
|
124
125
|
ruby script by calling `Resque::Pool.run`.
|
@@ -132,20 +133,7 @@ with the `--config` command line option.
|
|
132
133
|
TODO
|
133
134
|
-----
|
134
135
|
|
135
|
-
|
136
|
-
* config file split by hostname
|
137
|
-
* rename to `resque-squad`?
|
138
|
-
* cmd line option for non-rake loading
|
139
|
-
* cmd line option for preloading ruby file
|
140
|
-
* provide Unix style log formatter (compatible with $stdout/$stderr)
|
141
|
-
* recover gracefully from a malformed config file (on startup and HUP)
|
142
|
-
* change procline for malformed config file, graceful shutdown... and other states?
|
143
|
-
* figure out a good automated way to test this (cucumber or rspec?)
|
144
|
-
* clean up the code (I stole most of it from unicorn, and it's still a bit
|
145
|
-
bastardized); excessive use of vim foldmarkers are a code smell!
|
146
|
-
* rdoc
|
147
|
-
* integrate with or incorporate resque-batchworker features? (v2.0)
|
148
|
-
* integrate with resque-scheduler? (v2.0)
|
136
|
+
See [the TODO list](https://github.com/nevans/resque-pool/issues) at github issues.
|
149
137
|
|
150
138
|
Contributors
|
151
139
|
-------------
|
@@ -154,3 +142,4 @@ Contributors
|
|
154
142
|
* Stephen Celis (increased gemspec sanity)
|
155
143
|
* Vincent Agnello, Robert Kamunyori, Paul Kauders; for pairing with me at
|
156
144
|
B'more on Rails Open Source Hack Nights. :)
|
145
|
+
|
data/Rakefile
CHANGED
@@ -1,3 +1,17 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
# for loading the example config file in config/resque-pool.yml
|
3
5
|
require 'resque/pool/tasks'
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.rspec_opts = ["-c", "-f progress"]
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'cucumber/rake/task'
|
13
|
+
Cucumber::Rake::Task.new(:features) do |c|
|
14
|
+
c.profile = "rake"
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => [:spec, :features]
|
data/config/cucumber.yml
ADDED
data/examples/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gemspec :path => ".."
|
@@ -0,0 +1,67 @@
|
|
1
|
+
PATH
|
2
|
+
remote: /home/nick/src/active/resque-pool
|
3
|
+
specs:
|
4
|
+
resque-pool (0.1.0.dev)
|
5
|
+
rake
|
6
|
+
resque (~> 1.13)
|
7
|
+
trollop (~> 1.16)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
specs:
|
11
|
+
SystemTimer (1.2.2)
|
12
|
+
aruba (0.3.2)
|
13
|
+
childprocess (~> 0.1.6)
|
14
|
+
cucumber (~> 0.10.0)
|
15
|
+
rspec (~> 2.3.0)
|
16
|
+
builder (3.0.0)
|
17
|
+
childprocess (0.1.7)
|
18
|
+
ffi (~> 0.6.3)
|
19
|
+
cucumber (0.10.0)
|
20
|
+
builder (>= 2.1.2)
|
21
|
+
diff-lcs (~> 1.1.2)
|
22
|
+
gherkin (~> 2.3.2)
|
23
|
+
json (~> 1.4.6)
|
24
|
+
term-ansicolor (~> 1.0.5)
|
25
|
+
diff-lcs (1.1.2)
|
26
|
+
ffi (0.6.3)
|
27
|
+
rake (>= 0.8.7)
|
28
|
+
gherkin (2.3.3)
|
29
|
+
json (~> 1.4.6)
|
30
|
+
json (1.4.6)
|
31
|
+
rack (1.2.1)
|
32
|
+
rake (0.8.7)
|
33
|
+
redis (2.1.1)
|
34
|
+
redis-namespace (0.10.0)
|
35
|
+
redis (< 3.0.0)
|
36
|
+
resque (1.13.0)
|
37
|
+
json (~> 1.4.6)
|
38
|
+
redis-namespace (>= 0.10.0)
|
39
|
+
sinatra (>= 0.9.2)
|
40
|
+
vegas (~> 0.1.2)
|
41
|
+
rspec (2.3.0)
|
42
|
+
rspec-core (~> 2.3.0)
|
43
|
+
rspec-expectations (~> 2.3.0)
|
44
|
+
rspec-mocks (~> 2.3.0)
|
45
|
+
rspec-core (2.3.1)
|
46
|
+
rspec-expectations (2.3.0)
|
47
|
+
diff-lcs (~> 1.1.2)
|
48
|
+
rspec-mocks (2.3.0)
|
49
|
+
sinatra (1.1.3)
|
50
|
+
rack (~> 1.1)
|
51
|
+
tilt (>= 1.2.2, < 2.0)
|
52
|
+
term-ansicolor (1.0.5)
|
53
|
+
tilt (1.2.2)
|
54
|
+
trollop (1.16.2)
|
55
|
+
vegas (0.1.8)
|
56
|
+
rack (>= 1.0.0)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
SystemTimer
|
63
|
+
aruba (~> 0.3.2)
|
64
|
+
bundler (~> 1.0)
|
65
|
+
cucumber (~> 0.10.0)
|
66
|
+
resque-pool!
|
67
|
+
rspec (~> 2.3.0)
|
@@ -30,7 +30,7 @@ if roles.include?(node[:instance_role])
|
|
30
30
|
execute "enable-resque" do
|
31
31
|
command "rc-update add #{app}_resque default"
|
32
32
|
action :run
|
33
|
-
not_if "rc-update show | grep -q '^ *#{app}_resque |.*default"
|
33
|
+
not_if "rc-update show | grep -q '^ *#{app}_resque |.*default'"
|
34
34
|
end
|
35
35
|
|
36
36
|
execute "start-resque" do
|
@@ -0,0 +1,53 @@
|
|
1
|
+
resque-pool-manager[15490]: Reopened logfile: log/resque-pool.stdout.log
|
2
|
+
resque-pool-manager[15490]: trying to reopen a log... File
|
3
|
+
resque-pool-manager[15490]: orig_st: #<File::Stat dev=0x804, ino=3409002, mode=0100644, nlink=1, uid=1000, gid=1000, rdev=0x0, size=1063, blksize=4096, blocks=8, atime=Wed Feb 23 12:20:58 -0500 2011, mtime=Tue Mar 15 21:33:44 -0400 2011, ctime=Tue Mar 15 21:33:44 -0400 2011>
|
4
|
+
resque-pool-manager[15490]: cur_stat: #<File::Stat dev=0x804, ino=3409002, mode=0100644, nlink=1, uid=1000, gid=1000, rdev=0x0, size=1063, blksize=4096, blocks=8, atime=Wed Feb 23 12:20:58 -0500 2011, mtime=Tue Mar 15 21:33:44 -0400 2011, ctime=Tue Mar 15 21:33:44 -0400 2011>
|
5
|
+
resque-pool-manager[15490]: Reopen logs: close old children which have old logfiles open
|
6
|
+
resque-pool-manager[15490]: Reopen logs: new children will inherit new logfiles
|
7
|
+
resque-pool-manager[15490]: Reaped resque worker[16074] (status: 0) queues: bar
|
8
|
+
resque-pool-manager[15490]: Reaped resque worker[16076] (status: 0) queues: foo
|
9
|
+
resque-pool-worker[16638]: Starting worker arrakis:16638:bar
|
10
|
+
resque-pool-worker[16640]: Starting worker arrakis:16640:foo
|
11
|
+
resque-pool-manager[15490]: Reaped resque worker[16083] (status: 0) queues: bar
|
12
|
+
resque-pool-manager[15490]: Reaped resque worker[16073] (status: 0) queues: foo,bar,baz
|
13
|
+
resque-pool-worker[16650]: Starting worker arrakis:16650:bar
|
14
|
+
resque-pool-worker[16652]: Starting worker arrakis:16652:foo,bar,baz
|
15
|
+
resque-pool-manager[15490]: Reaped resque worker[16078] (status: 0) queues: foo,bar,baz
|
16
|
+
resque-pool-worker[16659]: Starting worker arrakis:16659:foo,bar,baz
|
17
|
+
resque-pool-manager[15490]: Reaped resque worker[16084] (status: 0) queues: foo,bar,baz
|
18
|
+
resque-pool-manager[15490]: Reaped resque worker[16086] (status: 0) queues: foo,bar,baz
|
19
|
+
resque-pool-worker[16669]: Starting worker arrakis:16669:foo,bar,baz
|
20
|
+
resque-pool-worker[16664]: Starting worker arrakis:16664:foo,bar,baz
|
21
|
+
resque-pool-manager[15490]: TERM: immediate shutdown (and immediate worker shutdown)
|
22
|
+
resque-pool-manager[15490]: manager finished
|
23
|
+
resque-pool-manager[24768]: Resque Pool running in development environment
|
24
|
+
(in /home/nick/src/active/resque-pool/examples)
|
25
|
+
resque-pool-manager[24768]: started manager
|
26
|
+
resque-pool-manager[24768]: Pool contains worker PIDs: [24790, 24788, 24789, 24786, 24787, 24794, 24793]
|
27
|
+
resque-pool-worker[24787]: Starting worker arrakis:24787:foo,bar,baz
|
28
|
+
resque-pool-worker[24788]: Starting worker arrakis:24788:foo,bar,baz
|
29
|
+
resque-pool-worker[24786]: Starting worker arrakis:24786:foo,bar,baz
|
30
|
+
resque-pool-worker[24789]: Starting worker arrakis:24789:foo,bar,baz
|
31
|
+
resque-pool-worker[24794]: Starting worker arrakis:24794:bar
|
32
|
+
resque-pool-worker[24793]: Starting worker arrakis:24793:bar
|
33
|
+
resque-pool-worker[24790]: Starting worker arrakis:24790:foo
|
34
|
+
resque-pool-manager[24768]: HUP: reload config file and reload logfiles
|
35
|
+
resque-pool-manager[24768]: Flushing logs
|
36
|
+
resque-pool-manager[24768]: HUP: close old children which have old logfiles open
|
37
|
+
resque-pool-manager[24768]: HUP: new children will inherit new logfiles
|
38
|
+
resque-pool-manager[24768]: Reaped resque worker[24787] (status: 0) queues: foo,bar,baz
|
39
|
+
resque-pool-manager[24768]: Reaped resque worker[24794] (status: 0) queues: bar
|
40
|
+
resque-pool-manager[24768]: Reaped resque worker[24786] (status: 0) queues: foo,bar,baz
|
41
|
+
resque-pool-worker[25311]: Starting worker arrakis:25311:foo,bar,baz
|
42
|
+
resque-pool-worker[25312]: Starting worker arrakis:25312:bar
|
43
|
+
resque-pool-worker[25315]: Starting worker arrakis:25315:foo,bar,baz
|
44
|
+
resque-pool-manager[24768]: Reaped resque worker[24790] (status: 0) queues: foo
|
45
|
+
resque-pool-manager[24768]: Reaped resque worker[24788] (status: 0) queues: foo,bar,baz
|
46
|
+
resque-pool-manager[24768]: Reaped resque worker[24789] (status: 0) queues: foo,bar,baz
|
47
|
+
resque-pool-manager[24768]: Reaped resque worker[24793] (status: 0) queues: bar
|
48
|
+
resque-pool-worker[25326]: Starting worker arrakis:25326:foo
|
49
|
+
resque-pool-worker[25327]: Starting worker arrakis:25327:foo,bar,baz
|
50
|
+
resque-pool-worker[25328]: Starting worker arrakis:25328:foo,bar,baz
|
51
|
+
resque-pool-worker[25330]: Starting worker arrakis:25330:bar
|
52
|
+
resque-pool-manager[24768]: HUP: reload config file and reload logfiles
|
53
|
+
resque-pool-manager[24768]: Flushing logs
|
@@ -0,0 +1,68 @@
|
|
1
|
+
Feature: Basic resque-pool daemon configuration and operation
|
2
|
+
To easily manage a pool of resque workers, resque-pool provides a daemon with
|
3
|
+
simple configuration. Static configuration is handled in the
|
4
|
+
config/config.yml file and dynamic configuration is handled in the Rakefile.
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given a file named "Rakefile" with:
|
8
|
+
"""
|
9
|
+
require 'resque/pool/tasks'
|
10
|
+
"""
|
11
|
+
|
12
|
+
Scenario: no config file
|
13
|
+
When I run the pool manager as "resque-pool"
|
14
|
+
Then the pool manager should report that it has started up
|
15
|
+
And the pool manager should report that the pool is empty
|
16
|
+
And the pool manager should have no child processes
|
17
|
+
When I send the pool manager the "QUIT" signal
|
18
|
+
Then the pool manager should finish
|
19
|
+
And the pool manager should report that it is finished
|
20
|
+
|
21
|
+
@slow_exit
|
22
|
+
Scenario: basic config file
|
23
|
+
Given a file named "config/resque-pool.yml" with:
|
24
|
+
"""
|
25
|
+
foo: 1
|
26
|
+
bar: 2
|
27
|
+
"bar,baz": 3
|
28
|
+
"""
|
29
|
+
When I run the pool manager as "resque-pool"
|
30
|
+
Then the pool manager should report that it has started up
|
31
|
+
And the pool manager should report that 6 workers are in the pool
|
32
|
+
And the pool manager should have 1 "foo" worker child processes
|
33
|
+
And the pool manager should have 2 "bar" worker child processes
|
34
|
+
And the pool manager should have 3 "bar,baz" worker child processes
|
35
|
+
When I send the pool manager the "QUIT" signal
|
36
|
+
Then the resque workers should all shutdown
|
37
|
+
And the pool manager should finish
|
38
|
+
And the pool manager should report that a "foo" worker has been reaped
|
39
|
+
And the pool manager should report that a "bar" worker has been reaped
|
40
|
+
And the pool manager should report that a "bar,baz" worker has been reaped
|
41
|
+
And the pool manager should report that it is finished
|
42
|
+
|
43
|
+
Scenario: daemonized
|
44
|
+
Given a directory named "log"
|
45
|
+
And a directory named "tmp/pids"
|
46
|
+
And a file named "config/resque-pool.yml" with:
|
47
|
+
"""
|
48
|
+
foo: 2
|
49
|
+
bar: 4
|
50
|
+
"baz,quux": 4
|
51
|
+
"""
|
52
|
+
When I run the pool manager as "resque-pool -d"
|
53
|
+
Then the pool manager should record its pid in "tmp/pids/resque-pool.pid"
|
54
|
+
And the pool manager should daemonize
|
55
|
+
And a file named "log/resque-pool.stdout.log" should exist
|
56
|
+
And a file named "log/resque-pool.stderr.log" should exist
|
57
|
+
And the pool manager should log that it has started up
|
58
|
+
And the pool manager should log that 10 workers are in the pool
|
59
|
+
And the pool manager should have 2 "foo" worker child processes
|
60
|
+
And the pool manager should have 4 "bar" worker child processes
|
61
|
+
And the pool manager should have 4 "baz,quux" worker child processes
|
62
|
+
When I send the pool manager the "QUIT" signal
|
63
|
+
Then the resque workers should all shutdown
|
64
|
+
And the pool manager daemon should finish
|
65
|
+
And the pool manager should log that a "foo" worker has been reaped
|
66
|
+
And the pool manager should log that a "bar" worker has been reaped
|
67
|
+
And the pool manager should log that a "baz,quux" worker has been reaped
|
68
|
+
And the pool manager should log that it is finished
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# syntactic sugar, and separate ivar. daemons aren't interactive
|
2
|
+
When /^I run "([^"]*)" in the background$/ do |cmd|
|
3
|
+
run_background(unescape(cmd))
|
4
|
+
end
|
5
|
+
|
6
|
+
Then /^the (output|logfiles) should contain the following lines \(with interpolated \$PID\):$/ do |output_logfiles, partial_output|
|
7
|
+
interpolate_background_pid(partial_output).split("\n").each do |line|
|
8
|
+
output_or_log(output_logfiles).should include(line)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I send "([^"]*)" the "([^"]*)" signal$/ do |cmd, signal|
|
13
|
+
send_signal(cmd, signal)
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^the "([^"]*)" process should finish$/ do |cmd|
|
17
|
+
# doesn't actually stop... just polls for exit
|
18
|
+
processes[cmd].stop
|
19
|
+
end
|
20
|
+
|
21
|
+
Before("@slow_exit") do
|
22
|
+
@aruba_timeout_seconds = 10
|
23
|
+
end
|
24
|
+
|
25
|
+
After do
|
26
|
+
kill_all_processes!
|
27
|
+
# now kill the daemon!
|
28
|
+
`pkill -9 -f resque-pool`
|
29
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
def process_should_exist(pid)
|
2
|
+
lambda { Process.kill(0, pid) }.should_not raise_error(Errno::ESRCH)
|
3
|
+
end
|
4
|
+
|
5
|
+
def process_should_not_exist(pid)
|
6
|
+
lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH)
|
7
|
+
end
|
8
|
+
|
9
|
+
def grab_worker_pids(count, str)
|
10
|
+
announce "TODO: check output_or_log for #{count} worker started messages"
|
11
|
+
pid_regex = (1..count).map { '(\d+)' }.join ', '
|
12
|
+
full_regex = /resque-pool-manager\[\d+\]: Pool contains worker PIDs: \[#{pid_regex}\]/m
|
13
|
+
str.should =~ full_regex
|
14
|
+
@worker_pids = full_regex.match(str).captures.map {|pid| pid.to_i }
|
15
|
+
end
|
16
|
+
|
17
|
+
def output_or_logfiles_string(report_log)
|
18
|
+
case report_log
|
19
|
+
when "report", "output"
|
20
|
+
"output"
|
21
|
+
when "log", "logfiles"
|
22
|
+
"logfiles"
|
23
|
+
else
|
24
|
+
raise ArgumentError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def output_or_log(report_log)
|
29
|
+
case report_log
|
30
|
+
when "report", "output"
|
31
|
+
all_output
|
32
|
+
when "log", "logfiles"
|
33
|
+
in_current_dir do
|
34
|
+
File.read("log/resque-pool.stdout.log") << File.read("log/resque-pool.stderr.log")
|
35
|
+
end
|
36
|
+
else
|
37
|
+
raise ArgumentError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def children_of(ppid)
|
42
|
+
ps = `ps -eo ppid,pid,cmd | grep '^ *#{ppid} '`
|
43
|
+
ps.split(/\s*\n/).map do |line|
|
44
|
+
_, pid, cmd = line.strip.split(/\s+/, 3)
|
45
|
+
[pid, cmd]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
When /^I run the pool manager as "([^"]*)"$/ do |cmd|
|
50
|
+
@pool_manager_process = run_background(unescape(cmd))
|
51
|
+
end
|
52
|
+
|
53
|
+
When /^I send the pool manager the "([^"]*)" signal$/ do |signal|
|
54
|
+
Process.kill signal, background_pid
|
55
|
+
output_logfiles = @pid_from_pidfile ? "logfiles" : "output"
|
56
|
+
case signal
|
57
|
+
when "QUIT"
|
58
|
+
keep_trying do
|
59
|
+
Then "the #{output_logfiles} should contain the following lines (with interpolated $PID):", <<-EOF
|
60
|
+
resque-pool-manager[$PID]: QUIT: graceful shutdown, waiting for children
|
61
|
+
EOF
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise ArgumentError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Then /^the pool manager should record its pid in "([^"]*)"$/ do |pidfile|
|
69
|
+
in_current_dir do
|
70
|
+
keep_trying do
|
71
|
+
File.should be_file(pidfile)
|
72
|
+
@pid_from_pidfile = File.read(pidfile).to_i
|
73
|
+
@pid_from_pidfile.should_not == 0
|
74
|
+
process_should_exist(@pid_from_pidfile)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
Then /^the pool manager should daemonize$/ do
|
80
|
+
@pool_manager_process.stop
|
81
|
+
end
|
82
|
+
|
83
|
+
Then /^the pool manager daemon should finish$/ do
|
84
|
+
keep_trying do
|
85
|
+
process_should_not_exist(@pid_from_pidfile)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# nomenclature: "report" => output to stdout/stderr
|
90
|
+
# "log" => output to default logfile
|
91
|
+
|
92
|
+
Then /^the pool manager should (report|log) that it has started up$/ do |report_log|
|
93
|
+
keep_trying do
|
94
|
+
Then "the #{output_or_logfiles_string(report_log)} should contain the following lines (with interpolated $PID):", <<-EOF
|
95
|
+
resque-pool-manager[$PID]: Resque Pool running in development environment
|
96
|
+
resque-pool-manager[$PID]: started manager
|
97
|
+
EOF
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
Then /^the pool manager should (report|log) that the pool is empty$/ do |report_log|
|
102
|
+
Then "the #{output_or_logfiles_string(report_log)} should contain the following lines (with interpolated $PID):", <<-EOF
|
103
|
+
resque-pool-manager[$PID]: Pool is empty
|
104
|
+
EOF
|
105
|
+
end
|
106
|
+
|
107
|
+
Then /^the pool manager should (report|log) that (\d+) workers are in the pool$/ do |report_log, count|
|
108
|
+
grab_worker_pids Integer(count), output_or_log(report_log)
|
109
|
+
end
|
110
|
+
|
111
|
+
Then /^the resque workers should all shutdown$/ do
|
112
|
+
@worker_pids.each do |pid|
|
113
|
+
keep_trying do
|
114
|
+
process_should_not_exist(pid)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
Then "the pool manager should have no child processes" do
|
120
|
+
children_of(background_pid).should have(:no).keys
|
121
|
+
end
|
122
|
+
|
123
|
+
Then /^the pool manager should have (\d+) "([^"]*)" worker child processes$/ do |count, queues|
|
124
|
+
children_of(background_pid).select do |pid, cmd|
|
125
|
+
cmd =~ /^resque-\d+.\d+.\d+: Waiting for #{queues}$/
|
126
|
+
end.should have(Integer(count)).members
|
127
|
+
end
|
128
|
+
|
129
|
+
Then "the pool manager should finish" do
|
130
|
+
# assuming there will not be multiple processes running
|
131
|
+
processes.each { |cmd, p| p.stop }
|
132
|
+
end
|
133
|
+
|
134
|
+
Then /^the pool manager should (report|log) that it is finished$/ do |report_log|
|
135
|
+
Then "the #{output_or_logfiles_string(report_log)} should contain the following lines (with interpolated $PID):", <<-EOF
|
136
|
+
resque-pool-manager[$PID]: manager finished
|
137
|
+
EOF
|
138
|
+
end
|
139
|
+
|
140
|
+
Then /^the pool manager should (report|log) that a "([^"]*)" worker has been reaped$/ do |report_log, worker_type|
|
141
|
+
And 'the '+ output_or_logfiles_string(report_log) +' should match /Reaped resque worker\[\d+\] \(status: 0\) queues: '+ worker_type + '/'
|
142
|
+
end
|
143
|
+
|
144
|
+
Then /^the logfiles should match \/([^\/]*)\/$/ do |partial_output|
|
145
|
+
output_or_log("log").should =~ /#{partial_output}/
|
146
|
+
end
|
147
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'aruba/api'
|
2
|
+
require 'aruba/process'
|
3
|
+
|
4
|
+
module Aruba
|
5
|
+
|
6
|
+
module Api
|
7
|
+
|
8
|
+
# this is a horrible hack, to make sure that it's done what it needs to do
|
9
|
+
# before we do our next step
|
10
|
+
def keep_trying(timeout=10, tries=0)
|
11
|
+
announce "Try: #{tries}" if @announce_env
|
12
|
+
yield
|
13
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
14
|
+
if tries < timeout
|
15
|
+
sleep 1
|
16
|
+
tries += 1
|
17
|
+
retry
|
18
|
+
else
|
19
|
+
raise
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def run_background(cmd)
|
24
|
+
@background = run(cmd)
|
25
|
+
end
|
26
|
+
|
27
|
+
def send_signal(cmd, signal)
|
28
|
+
announce_or_puts "$ kill -#{signal} #{processes[cmd].pid}" if @announce_env
|
29
|
+
processes[cmd].send_signal signal
|
30
|
+
end
|
31
|
+
|
32
|
+
def background_pid
|
33
|
+
@pid_from_pidfile || @background.pid
|
34
|
+
end
|
35
|
+
|
36
|
+
def interpolate_background_pid(string)
|
37
|
+
interpolated = string.gsub('$PID', background_pid.to_s)
|
38
|
+
announce_or_puts interpolated if @announce_env
|
39
|
+
interpolated
|
40
|
+
end
|
41
|
+
|
42
|
+
def kill_all_processes!
|
43
|
+
stop_processes!
|
44
|
+
rescue
|
45
|
+
processes.each {|cmd,process| send_signal(cmd, 'KILL') }
|
46
|
+
raise
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class Process
|
52
|
+
def pid
|
53
|
+
@process.pid
|
54
|
+
end
|
55
|
+
def send_signal signal
|
56
|
+
@process.send :send_signal, signal
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'aruba/cucumber'
|
data/lib/resque/pool/cli.rb
CHANGED
@@ -4,12 +4,13 @@ require 'resque/pool'
|
|
4
4
|
module Resque
|
5
5
|
class Pool
|
6
6
|
module CLI
|
7
|
+
extend Logging
|
7
8
|
extend self
|
8
9
|
|
9
10
|
def run
|
10
11
|
opts = parse_options
|
11
12
|
daemonize if opts[:daemon]
|
12
|
-
|
13
|
+
manage_pidfile opts[:pidfile]
|
13
14
|
redirect opts
|
14
15
|
setup_environment opts
|
15
16
|
start_pool
|
@@ -28,12 +29,12 @@ Usage:
|
|
28
29
|
resque-pool [options]
|
29
30
|
where [options] are:
|
30
31
|
EOS
|
31
|
-
opt :config, "Alternate path to config file",
|
32
|
-
opt :daemon, "Run as a background daemon", :default => false,
|
33
|
-
opt :stdout, "Redirect stdout to logfile", :type => String,
|
34
|
-
opt :stderr, "Redirect stderr to logfile", :type => String,
|
32
|
+
opt :config, "Alternate path to config file", :type => String, :short => "-c"
|
33
|
+
opt :daemon, "Run as a background daemon", :default => false, :short => "-d"
|
34
|
+
opt :stdout, "Redirect stdout to logfile", :type => String, :short => '-o'
|
35
|
+
opt :stderr, "Redirect stderr to logfile", :type => String, :short => '-e'
|
35
36
|
opt :nosync, "Don't sync logfiles on every write"
|
36
|
-
opt :pidfile, "PID file location", :type => String,
|
37
|
+
opt :pidfile, "PID file location", :type => String, :short => "-p"
|
37
38
|
opt :environment, "Set RAILS_ENV/RACK_ENV/RESQUE_ENV", :type => String, :short => "-E"
|
38
39
|
end
|
39
40
|
if opts[:daemon]
|
@@ -52,33 +53,52 @@ where [options] are:
|
|
52
53
|
exit unless pid.nil?
|
53
54
|
end
|
54
55
|
|
55
|
-
def
|
56
|
+
def manage_pidfile(pidfile)
|
57
|
+
return unless pidfile
|
56
58
|
pid = Process.pid
|
57
|
-
if pidfile
|
58
|
-
if
|
59
|
-
raise "Pidfile already exists at #{pidfile}
|
59
|
+
if File.exist? pidfile
|
60
|
+
if process_still_running? pidfile
|
61
|
+
raise "Pidfile already exists at #{pidfile} and process is still running."
|
62
|
+
else
|
63
|
+
File.delete pidfile
|
60
64
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
end
|
66
|
+
File.open pidfile, "w" do |f|
|
67
|
+
f.write pid
|
68
|
+
end
|
69
|
+
at_exit do
|
70
|
+
if Process.pid == pid
|
71
|
+
File.delete pidfile
|
68
72
|
end
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
76
|
+
def process_still_running?(pidfile)
|
77
|
+
old_pid = open(pidfile).read.strip.to_i
|
78
|
+
Process.kill 0, old_pid
|
79
|
+
true
|
80
|
+
rescue Errno::ESRCH
|
81
|
+
false
|
82
|
+
rescue Errno::EPERM
|
83
|
+
true
|
84
|
+
rescue ::Exception
|
85
|
+
$stderr.puts "While checking if PID #{old_pid} is running, unexpected #{e.class}: #{e}"
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
72
89
|
def redirect(opts)
|
73
90
|
$stdin.reopen '/dev/null' if opts[:daemon]
|
74
|
-
|
75
|
-
|
91
|
+
# need to reopen as File, or else Resque::Pool::Logging.reopen_logs! won't work
|
92
|
+
out = File.new(opts[:stdout], "a") if opts[:stdout] && !opts[:stdout].empty?
|
93
|
+
err = File.new(opts[:stderr], "a") if opts[:stderr] && !opts[:stderr].empty?
|
94
|
+
$stdout.reopen out if out
|
95
|
+
$stderr.reopen err if err
|
76
96
|
$stdout.sync = $stderr.sync = true unless opts[:nosync]
|
77
97
|
end
|
78
98
|
|
79
99
|
def setup_environment(opts)
|
80
100
|
ENV["RACK_ENV"] = ENV["RAILS_ENV"] = ENV["RESQUE_ENV"] = opts[:environment] if opts[:environment]
|
81
|
-
|
101
|
+
log "Resque Pool running in #{ENV["RAILS_ENV"] || "development"} environment"
|
82
102
|
ENV["RESQUE_POOL_CONFIG"] = opts[:config] if opts[:config]
|
83
103
|
end
|
84
104
|
|
data/lib/resque/pool/logging.rb
CHANGED
@@ -1,6 +1,38 @@
|
|
1
1
|
module Resque
|
2
2
|
class Pool
|
3
3
|
module Logging
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# more than a little bit complicated...
|
7
|
+
# copied this from Unicorn.
|
8
|
+
def self.reopen_logs!
|
9
|
+
log "Flushing logs"
|
10
|
+
[$stdout, $stderr].each do |fd|
|
11
|
+
if fd.instance_of? File
|
12
|
+
# skip if the file is the exact same inode and device
|
13
|
+
orig_st = fd.stat
|
14
|
+
begin
|
15
|
+
cur_st = File.stat(fd.path)
|
16
|
+
next if orig_st.ino == cur_st.ino && orig_st.dev == cur_st.dev
|
17
|
+
rescue Errno::ENOENT
|
18
|
+
end
|
19
|
+
# match up the encoding
|
20
|
+
open_arg = 'a'
|
21
|
+
if fd.respond_to?(:external_encoding) && enc = fd.external_encoding
|
22
|
+
open_arg << ":#{enc.to_s}"
|
23
|
+
enc = fd.internal_encoding and open_arg << ":#{enc.to_s}"
|
24
|
+
end
|
25
|
+
# match up buffering (does reopen reset this?)
|
26
|
+
sync = fd.sync
|
27
|
+
# sync to disk
|
28
|
+
fd.fsync
|
29
|
+
# reopen, and set ruby buffering appropriately
|
30
|
+
fd.reopen fd.path, open_arg
|
31
|
+
fd.sync = sync
|
32
|
+
log "Reopened logfile: #{fd.path}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
4
36
|
|
5
37
|
# Given a string, sets the procline ($0)
|
6
38
|
# Procline is always in the format of:
|
@@ -11,7 +43,14 @@ module Resque
|
|
11
43
|
|
12
44
|
# TODO: make this use an actual logger
|
13
45
|
def log(message)
|
14
|
-
puts message
|
46
|
+
puts "resque-pool-manager[#{Process.pid}]: #{message}"
|
47
|
+
#$stdout.fsync
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO: make this use an actual logger
|
51
|
+
def log_worker(message)
|
52
|
+
puts "resque-pool-worker[#{Process.pid}]: #{message}"
|
53
|
+
#$stdout.fsync
|
15
54
|
end
|
16
55
|
|
17
56
|
end
|
data/lib/resque/pool/tasks.rb
CHANGED
@@ -4,11 +4,11 @@ require 'resque/pool'
|
|
4
4
|
|
5
5
|
namespace :resque do
|
6
6
|
|
7
|
-
|
7
|
+
# resque worker config (not pool related). e.g. hoptoad, rails environment
|
8
8
|
task :setup
|
9
9
|
|
10
10
|
namespace :pool do
|
11
|
-
|
11
|
+
# resque pool config. e.g. after_prefork connection handling
|
12
12
|
task :setup
|
13
13
|
end
|
14
14
|
|
data/lib/resque/pool/version.rb
CHANGED
data/lib/resque/pool.rb
CHANGED
@@ -8,16 +8,14 @@ require 'yaml'
|
|
8
8
|
|
9
9
|
module Resque
|
10
10
|
class Pool
|
11
|
-
include Logging
|
12
|
-
attr_reader :config
|
13
|
-
attr_reader :workers
|
14
|
-
|
15
|
-
# CONSTANTS {{{
|
16
11
|
SIG_QUEUE_MAX_SIZE = 5
|
17
12
|
DEFAULT_WORKER_INTERVAL = 5
|
18
13
|
QUEUE_SIGS = [ :QUIT, :INT, :TERM, :USR1, :USR2, :CONT, :HUP, :WINCH, ]
|
19
|
-
CHUNK_SIZE=(16 * 1024)
|
20
|
-
|
14
|
+
CHUNK_SIZE = (16 * 1024)
|
15
|
+
|
16
|
+
include Logging
|
17
|
+
attr_reader :config
|
18
|
+
attr_reader :workers
|
21
19
|
|
22
20
|
def initialize(config)
|
23
21
|
init_config(config)
|
@@ -70,13 +68,14 @@ module Resque
|
|
70
68
|
# }}}
|
71
69
|
# Config: load config and config file {{{
|
72
70
|
|
71
|
+
def config_file
|
72
|
+
@config_file || (!@config && ::Resque::Pool.choose_config_file)
|
73
|
+
end
|
74
|
+
|
73
75
|
def init_config(config)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
if config.kind_of? String
|
79
|
-
@config_file = config.to_s
|
76
|
+
case config
|
77
|
+
when String, nil
|
78
|
+
@config_file = config
|
80
79
|
else
|
81
80
|
@config = config.dup
|
82
81
|
end
|
@@ -84,7 +83,11 @@ module Resque
|
|
84
83
|
end
|
85
84
|
|
86
85
|
def load_config
|
87
|
-
|
86
|
+
if config_file
|
87
|
+
@config = YAML.load_file(config_file)
|
88
|
+
else
|
89
|
+
@config ||= {}
|
90
|
+
end
|
88
91
|
environment and @config[environment] and config.merge!(@config[environment])
|
89
92
|
config.delete_if {|key, value| value.is_a? Hash }
|
90
93
|
end
|
@@ -124,9 +127,14 @@ module Resque
|
|
124
127
|
end
|
125
128
|
end
|
126
129
|
|
130
|
+
class QuitNowException < Exception; end
|
127
131
|
# defer a signal for later processing in #join (master process)
|
128
132
|
def trap_deferred(signal)
|
129
133
|
trap(signal) do |sig_nr|
|
134
|
+
if @waiting_for_reaper && [:INT, :TERM].include?(signal)
|
135
|
+
log "Recieved #{signal}: short circuiting QUIT waitpid"
|
136
|
+
raise QuitNowException
|
137
|
+
end
|
130
138
|
if sig_queue.size < SIG_QUEUE_MAX_SIZE
|
131
139
|
sig_queue << signal
|
132
140
|
awaken_master
|
@@ -146,8 +154,12 @@ module Resque
|
|
146
154
|
log "#{signal}: sending to all workers"
|
147
155
|
signal_all_workers(signal)
|
148
156
|
when :HUP
|
149
|
-
log "HUP: reload config file"
|
157
|
+
log "HUP: reload config file and reload logfiles"
|
150
158
|
load_config
|
159
|
+
Logging.reopen_logs!
|
160
|
+
log "HUP: gracefully shutdown old children (which have old logfiles open)"
|
161
|
+
signal_all_workers(:QUIT)
|
162
|
+
log "HUP: new children will inherit new logfiles"
|
151
163
|
maintain_worker_count
|
152
164
|
when :WINCH
|
153
165
|
log "WINCH: gracefully stopping all workers"
|
@@ -178,11 +190,19 @@ module Resque
|
|
178
190
|
init_sig_handlers!
|
179
191
|
maintain_worker_count
|
180
192
|
procline("(started)")
|
181
|
-
log "
|
182
|
-
|
193
|
+
log "started manager"
|
194
|
+
report_worker_pool_pids
|
183
195
|
self
|
184
196
|
end
|
185
197
|
|
198
|
+
def report_worker_pool_pids
|
199
|
+
if workers.empty?
|
200
|
+
log "Pool is empty"
|
201
|
+
else
|
202
|
+
log "Pool contains worker PIDs: #{all_pids.inspect}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
186
206
|
def join
|
187
207
|
loop do
|
188
208
|
reap_all_workers
|
@@ -195,7 +215,7 @@ module Resque
|
|
195
215
|
end
|
196
216
|
procline("(shutting down)")
|
197
217
|
#stop # gracefully shutdown all workers on our way out
|
198
|
-
log "
|
218
|
+
log "manager finished"
|
199
219
|
#unlink_pid_safe(pid) if pid
|
200
220
|
end
|
201
221
|
|
@@ -212,15 +232,17 @@ module Resque
|
|
212
232
|
# worker process management {{{
|
213
233
|
|
214
234
|
def reap_all_workers(waitpid_flags=Process::WNOHANG)
|
235
|
+
@waiting_for_reaper = waitpid_flags == 0
|
215
236
|
begin
|
216
237
|
loop do
|
238
|
+
# -1, wait for any child process
|
217
239
|
wpid, status = Process.waitpid2(-1, waitpid_flags)
|
218
240
|
wpid or break
|
219
241
|
worker = delete_worker(wpid)
|
220
242
|
# TODO: close any file descriptors connected to worker, if any
|
221
|
-
log "
|
243
|
+
log "Reaped resque worker[#{status.pid}] (status: #{status.exitstatus}) queues: #{worker.queues.join(",")}"
|
222
244
|
end
|
223
|
-
rescue Errno::ECHILD
|
245
|
+
rescue Errno::ECHILD, QuitNowException
|
224
246
|
end
|
225
247
|
end
|
226
248
|
|
@@ -285,7 +307,7 @@ module Resque
|
|
285
307
|
def spawn_worker!(queues)
|
286
308
|
worker = create_worker(queues)
|
287
309
|
pid = fork do
|
288
|
-
|
310
|
+
log_worker "Starting worker #{worker}"
|
289
311
|
call_after_prefork!
|
290
312
|
reset_sig_handlers!
|
291
313
|
#self_pipe.each {|io| io.close }
|
data/resque-pool.gemspec
CHANGED
@@ -15,15 +15,20 @@ Gem::Specification.new do |s|
|
|
15
15
|
saving memory (w/REE) and monitoring their uptime
|
16
16
|
EOF
|
17
17
|
|
18
|
-
s.add_dependency "resque",
|
18
|
+
s.add_dependency "resque", "~> 1.13"
|
19
19
|
s.add_dependency "trollop", "~> 1.16"
|
20
20
|
s.add_dependency "rake"
|
21
|
-
s.add_development_dependency "rspec"
|
21
|
+
s.add_development_dependency "rspec", "~> 2.3.0"
|
22
|
+
s.add_development_dependency "cucumber", "~> 0.10.0"
|
23
|
+
s.add_development_dependency "aruba", "~> 0.3.2"
|
22
24
|
s.add_development_dependency "SystemTimer" # to silence redis gem's warning
|
23
25
|
s.add_development_dependency "bundler", "~> 1.0"
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
# hidden files are automatically ignored by Dir.glob
|
28
|
+
ignore_patterns = %w[**/*.gem **/*.pid **/*.log pkg Gemfile.lock]
|
29
|
+
ignore_files = ignore_patterns.inject([]) {|a,p| a + Dir[p] }
|
30
|
+
s.files = Dir["**/*"] - ignore_files
|
31
|
+
s.test_files = Dir.glob("{spec,features}/**/*.{rb,yml,feature}")
|
32
|
+
s.executables = 'resque-pool'
|
28
33
|
s.require_paths = ["lib"]
|
29
34
|
end
|
data/spec/resque_pool_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec/spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
RSpec.configure do |config|
|
4
4
|
config.after {
|
5
5
|
Object.send(:remove_const, :RAILS_ENV) if defined? RAILS_ENV
|
6
6
|
ENV.delete 'RACK_ENV'
|
@@ -80,6 +80,13 @@ describe Resque::Pool, "when loading the pool configuration from a Hash" do
|
|
80
80
|
|
81
81
|
end
|
82
82
|
|
83
|
+
describe Resque::Pool, "given no configuration" do
|
84
|
+
subject { Resque::Pool.new(nil) }
|
85
|
+
it "should have no worker types" do
|
86
|
+
subject.config.should == {}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
83
90
|
describe Resque::Pool, "when loading the pool configuration from a file" do
|
84
91
|
|
85
92
|
subject { Resque::Pool.new("spec/resque-pool.yml") }
|
data/spec/spec_helper.rb
CHANGED
data/tmp/aruba/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'resque/pool/tasks'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- nicholas a. evans
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-03-15 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -26,11 +26,11 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 21
|
30
30
|
segments:
|
31
31
|
- 1
|
32
|
-
-
|
33
|
-
version: "1.
|
32
|
+
- 13
|
33
|
+
version: "1.13"
|
34
34
|
type: :runtime
|
35
35
|
version_requirements: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -68,18 +68,52 @@ dependencies:
|
|
68
68
|
requirement: &id004 !ruby/object:Gem::Requirement
|
69
69
|
none: false
|
70
70
|
requirements:
|
71
|
-
- -
|
71
|
+
- - ~>
|
72
72
|
- !ruby/object:Gem::Version
|
73
73
|
hash: 3
|
74
74
|
segments:
|
75
|
+
- 2
|
76
|
+
- 3
|
75
77
|
- 0
|
76
|
-
version:
|
78
|
+
version: 2.3.0
|
77
79
|
type: :development
|
78
80
|
version_requirements: *id004
|
79
81
|
- !ruby/object:Gem::Dependency
|
80
|
-
name:
|
82
|
+
name: cucumber
|
81
83
|
prerelease: false
|
82
84
|
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 55
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
- 10
|
93
|
+
- 0
|
94
|
+
version: 0.10.0
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: aruba
|
99
|
+
prerelease: false
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ~>
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 23
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
- 3
|
109
|
+
- 2
|
110
|
+
version: 0.3.2
|
111
|
+
type: :development
|
112
|
+
version_requirements: *id006
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: SystemTimer
|
115
|
+
prerelease: false
|
116
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
83
117
|
none: false
|
84
118
|
requirements:
|
85
119
|
- - ">="
|
@@ -89,11 +123,11 @@ dependencies:
|
|
89
123
|
- 0
|
90
124
|
version: "0"
|
91
125
|
type: :development
|
92
|
-
version_requirements: *
|
126
|
+
version_requirements: *id007
|
93
127
|
- !ruby/object:Gem::Dependency
|
94
128
|
name: bundler
|
95
129
|
prerelease: false
|
96
|
-
requirement: &
|
130
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
97
131
|
none: false
|
98
132
|
requirements:
|
99
133
|
- - ~>
|
@@ -104,7 +138,7 @@ dependencies:
|
|
104
138
|
- 0
|
105
139
|
version: "1.0"
|
106
140
|
type: :development
|
107
|
-
version_requirements: *
|
141
|
+
version_requirements: *id008
|
108
142
|
description: " quickly and easily fork a pool of resque workers,\n saving memory (w/REE) and monitoring their uptime\n"
|
109
143
|
email:
|
110
144
|
- nick@ekenosen.net
|
@@ -115,33 +149,41 @@ extensions: []
|
|
115
149
|
extra_rdoc_files: []
|
116
150
|
|
117
151
|
files:
|
118
|
-
- .gitignore
|
119
|
-
- Changelog.md
|
120
|
-
- Gemfile
|
121
|
-
- Gemfile.lock
|
122
152
|
- LICENSE.txt
|
153
|
+
- features/basic_daemon_config.feature
|
154
|
+
- features/support/aruba_daemon_support.rb
|
155
|
+
- features/support/env.rb
|
156
|
+
- features/step_definitions/resque-pool_steps.rb
|
157
|
+
- features/step_definitions/daemon_steps.rb
|
158
|
+
- bin/resque-pool
|
123
159
|
- README.md
|
160
|
+
- spec/spec_helper.rb
|
161
|
+
- spec/resque_pool_spec.rb
|
162
|
+
- spec/resque-pool.yml
|
163
|
+
- spec/mock_config.rb
|
164
|
+
- Changelog.md
|
124
165
|
- Rakefile
|
125
|
-
-
|
126
|
-
- config/alternate.yml
|
127
|
-
- config/resque-pool.yml
|
128
|
-
- examples/Rakefile
|
166
|
+
- examples/Gemfile.lock
|
129
167
|
- examples/chef_cookbook/recipes/default.rb
|
130
168
|
- examples/chef_cookbook/templates/default/initd.erb
|
131
169
|
- examples/chef_cookbook/templates/default/monitrc.erb
|
132
170
|
- examples/rails-resque.rake
|
133
171
|
- examples/resque-pool.yml
|
172
|
+
- examples/Rakefile
|
173
|
+
- examples/log/resque-pool.stdout.log.2
|
174
|
+
- examples/Gemfile
|
175
|
+
- resque-pool.gemspec
|
176
|
+
- tmp/aruba/Rakefile
|
177
|
+
- tmp/aruba/config/resque-pool.yml
|
134
178
|
- lib/resque/pool.rb
|
135
|
-
- lib/resque/pool/cli.rb
|
136
|
-
- lib/resque/pool/logging.rb
|
137
|
-
- lib/resque/pool/pooled_worker.rb
|
138
179
|
- lib/resque/pool/tasks.rb
|
180
|
+
- lib/resque/pool/cli.rb
|
139
181
|
- lib/resque/pool/version.rb
|
140
|
-
- resque
|
141
|
-
-
|
142
|
-
-
|
143
|
-
-
|
144
|
-
-
|
182
|
+
- lib/resque/pool/pooled_worker.rb
|
183
|
+
- lib/resque/pool/logging.rb
|
184
|
+
- config/cucumber.yml
|
185
|
+
- config/alternate.yml
|
186
|
+
- Gemfile
|
145
187
|
has_rdoc: true
|
146
188
|
homepage: http://github.com/nevans/resque-pool
|
147
189
|
licenses: []
|
@@ -176,5 +218,13 @@ rubygems_version: 1.3.7
|
|
176
218
|
signing_key:
|
177
219
|
specification_version: 3
|
178
220
|
summary: quickly and easily fork a pool of resque workers
|
179
|
-
test_files:
|
180
|
-
|
221
|
+
test_files:
|
222
|
+
- spec/spec_helper.rb
|
223
|
+
- spec/resque_pool_spec.rb
|
224
|
+
- spec/mock_config.rb
|
225
|
+
- spec/resque-pool.yml
|
226
|
+
- features/support/aruba_daemon_support.rb
|
227
|
+
- features/support/env.rb
|
228
|
+
- features/step_definitions/resque-pool_steps.rb
|
229
|
+
- features/step_definitions/daemon_steps.rb
|
230
|
+
- features/basic_daemon_config.feature
|
data/Gemfile.lock
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
resque-pool (0.0.12.0.alpha)
|
5
|
-
rake
|
6
|
-
resque (~> 1.10)
|
7
|
-
trollop (~> 1.16)
|
8
|
-
|
9
|
-
GEM
|
10
|
-
remote: http://rubygems.org/
|
11
|
-
specs:
|
12
|
-
SystemTimer (1.2.1)
|
13
|
-
diff-lcs (1.1.2)
|
14
|
-
json (1.4.6)
|
15
|
-
rack (1.2.1)
|
16
|
-
rake (0.8.7)
|
17
|
-
redis (2.1.1)
|
18
|
-
redis-namespace (0.8.0)
|
19
|
-
redis (< 3.0.0)
|
20
|
-
resque (1.10.0)
|
21
|
-
json (~> 1.4.6)
|
22
|
-
redis-namespace (~> 0.8.0)
|
23
|
-
sinatra (>= 0.9.2)
|
24
|
-
vegas (~> 0.1.2)
|
25
|
-
rspec (2.4.0)
|
26
|
-
rspec-core (~> 2.4.0)
|
27
|
-
rspec-expectations (~> 2.4.0)
|
28
|
-
rspec-mocks (~> 2.4.0)
|
29
|
-
rspec-core (2.4.0)
|
30
|
-
rspec-expectations (2.4.0)
|
31
|
-
diff-lcs (~> 1.1.2)
|
32
|
-
rspec-mocks (2.4.0)
|
33
|
-
sinatra (1.1.2)
|
34
|
-
rack (~> 1.1)
|
35
|
-
tilt (~> 1.2)
|
36
|
-
tilt (1.2.1)
|
37
|
-
trollop (1.16.2)
|
38
|
-
vegas (0.1.8)
|
39
|
-
rack (>= 1.0.0)
|
40
|
-
|
41
|
-
PLATFORMS
|
42
|
-
ruby
|
43
|
-
|
44
|
-
DEPENDENCIES
|
45
|
-
SystemTimer
|
46
|
-
bundler (~> 1.0)
|
47
|
-
rake
|
48
|
-
resque (~> 1.10)
|
49
|
-
resque-pool!
|
50
|
-
rspec
|
51
|
-
trollop (~> 1.16)
|