resque-pool 0.1.0 → 0.2.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/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)
|