resque-pool 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +132 -0
- data/lib/resque/pool.rb +169 -71
- data/lib/resque/pool/logging.rb +19 -0
- data/lib/resque/pool/pooled_worker.rb +65 -0
- data/lib/resque/pool/tasks.rb +2 -7
- metadata +11 -8
data/README.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
Resque Pool
|
2
|
+
===========
|
3
|
+
|
4
|
+
Resque pool is a simple library for managing a pool of resque workers. Given a
|
5
|
+
configuration hash or a config file (resque-pool.yml or
|
6
|
+
config/resque-pool.yml), it will manage your workers for you, starting up the
|
7
|
+
appropriate number of workers for each.
|
8
|
+
|
9
|
+
Benefits
|
10
|
+
---------
|
11
|
+
|
12
|
+
* Less memory consumption - If you are using Ruby Enterprise Edition, or any
|
13
|
+
ruby with copy-on-write safe garbage collection, this will save you a lot of
|
14
|
+
memory when you managing many workers.
|
15
|
+
* Simpler (less) config - If you are using monit or god or an init script to
|
16
|
+
start up your workers, you can simply start up one pool, and it will manage
|
17
|
+
your workers for you.
|
18
|
+
* Faster startup - if you are starting many workers at once, you would normally
|
19
|
+
have them competing for CPU as they load their environments. Resque-pool can
|
20
|
+
load the environment once, and almost immediately fork all of your workers.
|
21
|
+
|
22
|
+
How to use
|
23
|
+
-----------
|
24
|
+
|
25
|
+
To configure resque-pool, you can either set `Resque::Pool.config` to a hash in
|
26
|
+
your `resque:setup` or you can set the same config in either `resque-pool.yml`
|
27
|
+
or `config/resque-pool.yml`. To use resque-pool, require its rake tasks in
|
28
|
+
your rake file, and call the resque:pool task.
|
29
|
+
|
30
|
+
For example, to use resque-pool with rails, in `config/resque-pool.yml`:
|
31
|
+
|
32
|
+
foo: 1
|
33
|
+
bar: 2
|
34
|
+
"foo,bar,baz": 4
|
35
|
+
|
36
|
+
and in `lib/tasks/resque.rake`:
|
37
|
+
|
38
|
+
require 'resque/pool/tasks'
|
39
|
+
namespace :resque do
|
40
|
+
|
41
|
+
# preload the rails environment in the pool master
|
42
|
+
task :setup => :environment
|
43
|
+
# it's better to use a config file, but you can also config here:
|
44
|
+
# Resque::Pool.config = {"foo" => 1, "bar" => 1}
|
45
|
+
|
46
|
+
# close any sockets or files in pool master
|
47
|
+
ActiveRecord::Base.connection.disconnect!
|
48
|
+
|
49
|
+
# and re-open them in the resque worker parent
|
50
|
+
Resque::Pool.after_prefork do |job|
|
51
|
+
ActiveRecord::Base.establish_connection
|
52
|
+
end
|
53
|
+
|
54
|
+
# you could also re-open them in the resque worker child, using
|
55
|
+
# Resque.after_fork, but that probably isn't necessary, and
|
56
|
+
# Resque::Pool.after_prefork should be faster, since it won't run
|
57
|
+
# for every single job.
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Then you can start the queues via:
|
62
|
+
|
63
|
+
rake resque:pool RAILS_ENV=production
|
64
|
+
|
65
|
+
This will start up seven worker processes, one each looking exclusively at each
|
66
|
+
of the foo, bar, and baz queues, and four workers looking at all queues in
|
67
|
+
priority. This is similar to if you ran the following:
|
68
|
+
|
69
|
+
rake resque:worker RAILS_ENV=production QUEUES=foo
|
70
|
+
rake resque:worker RAILS_ENV=production QUEUES=bar
|
71
|
+
rake resque:worker RAILS_ENV=production QUEUES=bar
|
72
|
+
rake resque:worker RAILS_ENV=production QUEUES=foo,bar,baz
|
73
|
+
rake resque:worker RAILS_ENV=production QUEUES=foo,bar,baz
|
74
|
+
rake resque:worker RAILS_ENV=production QUEUES=foo,bar,baz
|
75
|
+
rake resque:worker RAILS_ENV=production QUEUES=foo,bar,baz
|
76
|
+
|
77
|
+
Resque already forks for its own child processes, giving two levels. The pool
|
78
|
+
master will stay around monitoring the resque worker parents, giving three
|
79
|
+
levels:
|
80
|
+
|
81
|
+
* a single pool master
|
82
|
+
* many worker parents
|
83
|
+
* a worker child per worker (when the actual job is being processed)
|
84
|
+
|
85
|
+
For example, `ps -ef f | grep [r]esque` might return something like the
|
86
|
+
following:
|
87
|
+
|
88
|
+
rails 13858 1 0 13:44 ? S 0:02 resque-pool-master: managing [13867, 13875, 13871, 13872, 13868, 13870, 13876]
|
89
|
+
rails 13867 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for foo
|
90
|
+
rails 13868 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for bar
|
91
|
+
rails 13870 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for bar
|
92
|
+
rails 13871 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for foo,bar,baz
|
93
|
+
rails 13872 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Forked 7481 at 1280343254
|
94
|
+
rails 7481 13872 0 14:54 ? S 0:00 \_ resque-1.9.9: Processing foo since 1280343254
|
95
|
+
rails 13875 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Waiting for foo,bar,baz
|
96
|
+
rails 13876 13858 0 13:44 ? S 0:00 \_ resque-1.9.9: Forked 7485 at 1280343255
|
97
|
+
rails 7485 13876 0 14:54 ? S 0:00 \_ resque-1.9.9: Processing bar since 1280343254
|
98
|
+
|
99
|
+
SIGNALS
|
100
|
+
-------
|
101
|
+
|
102
|
+
The pool master responds to the following signals:
|
103
|
+
|
104
|
+
* `HUP` - reload the config file, e.g. to change the number of workers per queue list
|
105
|
+
* `QUIT` - send `QUIT` to each worker parent and shutdown the master after all workers are done.
|
106
|
+
* `INT` - send `QUIT` to each worker parent and immediately shutdown master
|
107
|
+
* `TERM` - send `TERM` to each worker parent and immediately shutdown master
|
108
|
+
* `WINCH` - send `QUIT` to each worker, but keep master running (send `HUP` to reload config and restart workers)
|
109
|
+
* `USR1`/`USR2`/`CONT` - send the signal on to all worker parents (see Resque docs).
|
110
|
+
|
111
|
+
`HUP` will no-op if you use a hash for configuration instead of a config file.
|
112
|
+
So you should probably use a config file. After a `HUP`, workers that are no
|
113
|
+
longer needed will be gracefully shutdown via `QUIT`.
|
114
|
+
|
115
|
+
Other Features
|
116
|
+
--------------
|
117
|
+
|
118
|
+
Workers will watch the pool master, and gracefully shutdown if the master
|
119
|
+
process dies (for whatever reason) before them.
|
120
|
+
|
121
|
+
TODO
|
122
|
+
-----
|
123
|
+
|
124
|
+
* do appropriate logging (e.g. all to one logfile, each queue to its own
|
125
|
+
logfile, or each worker to its own logfile). Logfile location must be
|
126
|
+
configurable.
|
127
|
+
* (optionally) daemonize, setting a PID file somewhere
|
128
|
+
* recover gracefully from a malformed config file (on startup and HUP)
|
129
|
+
* figure out a good way to test this (preferably via cucumber or rspec)
|
130
|
+
* clean up the code (I stole most of it from unicorn, and it's still a bit
|
131
|
+
bastardized)
|
132
|
+
* web interface for adding and removing workers (etc)
|
data/lib/resque/pool.rb
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
require 'resque'
|
3
|
+
require 'resque/pool/logging'
|
4
|
+
require 'resque/pool/pooled_worker'
|
3
5
|
require 'fcntl'
|
6
|
+
require 'yaml'
|
4
7
|
|
5
8
|
module Resque
|
6
9
|
class Pool
|
7
|
-
|
10
|
+
include Logging
|
11
|
+
attr_reader :config
|
8
12
|
attr_reader :workers
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
# CONSTANTS {{{
|
15
|
+
SIG_QUEUE_MAX_SIZE = 5
|
16
|
+
DEFAULT_WORKER_INTERVAL = 5
|
17
|
+
QUEUE_SIGS = [ :QUIT, :INT, :TERM, :USR1, :USR2, :CONT, :HUP, :WINCH, ]
|
13
18
|
CHUNK_SIZE=(16 * 1024)
|
19
|
+
# }}}
|
14
20
|
|
15
21
|
def initialize(config)
|
16
|
-
|
17
|
-
@workers =
|
22
|
+
init_config(config)
|
23
|
+
@workers = {}
|
18
24
|
procline "(initialized)"
|
19
25
|
end
|
20
26
|
|
27
|
+
# Config: after_prefork {{{
|
21
28
|
|
22
29
|
# The `after_prefork` hook will be run in workers if you are using the
|
23
30
|
# preforking master worker to save memory. Use this hook to reload
|
@@ -39,48 +46,131 @@ module Resque
|
|
39
46
|
self.class.after_prefork && self.class.after_prefork.call
|
40
47
|
end
|
41
48
|
|
42
|
-
#
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
# }}}
|
50
|
+
# Config: class methods to start up the pool using the default config {{{
|
51
|
+
|
52
|
+
@config_files = ["resque-pool.yml", "config/resque-pool.yml"]
|
53
|
+
class << self; attr_accessor :config, :config_files; end
|
54
|
+
def self.load_default_config
|
55
|
+
if @config
|
56
|
+
@config
|
57
|
+
else
|
58
|
+
@config_files.detect { |f| File.exist?(f) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.run
|
63
|
+
if GC.respond_to?(:copy_on_write_friendly=)
|
64
|
+
GC.copy_on_write_friendly = true
|
65
|
+
end
|
66
|
+
Resque::Pool.new(load_default_config).start.join
|
67
|
+
end
|
68
|
+
|
69
|
+
# }}}
|
70
|
+
# Config: load config and config file {{{
|
71
|
+
|
72
|
+
def load_config_from_file
|
73
|
+
return unless @pool_config_file
|
74
|
+
log "**** loading config from #{@pool_config_file}"
|
75
|
+
@config = YAML.load_file(@pool_config_file)
|
47
76
|
end
|
48
77
|
|
49
|
-
|
50
|
-
|
51
|
-
|
78
|
+
def init_config(config)
|
79
|
+
unless config
|
80
|
+
raise ArgumentError,
|
81
|
+
"No configuration found. Please setup config/resque-pool.yml"
|
82
|
+
end
|
83
|
+
if config.kind_of? String
|
84
|
+
@pool_config_file = config.to_s
|
85
|
+
load_config_from_file
|
86
|
+
else
|
87
|
+
@config = config.dup
|
88
|
+
end
|
89
|
+
log "**** config: #{@config.inspect}"
|
52
90
|
end
|
53
91
|
|
92
|
+
# }}}
|
93
|
+
|
94
|
+
# Sig handlers and self pipe management {{{
|
95
|
+
|
96
|
+
def self_pipe; @self_pipe ||= [] end
|
97
|
+
def sig_queue; @sig_queue ||= [] end
|
98
|
+
|
54
99
|
def init_self_pipe!
|
55
|
-
|
56
|
-
|
57
|
-
|
100
|
+
self_pipe.each { |io| io.close rescue nil }
|
101
|
+
self_pipe.replace(IO.pipe)
|
102
|
+
self_pipe.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
|
58
103
|
end
|
59
104
|
|
60
105
|
def init_sig_handlers!
|
61
106
|
QUEUE_SIGS.each { |sig| trap_deferred(sig) }
|
62
|
-
trap(:CHLD)
|
107
|
+
trap(:CHLD) { |_| awaken_master }
|
108
|
+
end
|
109
|
+
|
110
|
+
def awaken_master
|
111
|
+
begin
|
112
|
+
self_pipe.last.write_nonblock('.') # wakeup master process from select
|
113
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
114
|
+
# pipe is full, master should wake up anyways
|
115
|
+
retry
|
116
|
+
end
|
63
117
|
end
|
64
118
|
|
65
119
|
# defer a signal for later processing in #join (master process)
|
66
120
|
def trap_deferred(signal)
|
67
121
|
trap(signal) do |sig_nr|
|
68
|
-
if
|
69
|
-
|
122
|
+
if sig_queue.size < SIG_QUEUE_MAX_SIZE
|
123
|
+
sig_queue << signal
|
70
124
|
awaken_master
|
71
125
|
else
|
72
|
-
log "ignoring SIG#{signal}, queue=#{
|
126
|
+
log "ignoring SIG#{signal}, queue=#{sig_queue.inspect}"
|
73
127
|
end
|
74
128
|
end
|
75
129
|
end
|
76
130
|
|
131
|
+
def reset_sig_handlers!
|
132
|
+
QUEUE_SIGS.each {|sig| trap(sig, "DEFAULT") }
|
133
|
+
end
|
134
|
+
|
135
|
+
def handle_sig_queue!
|
136
|
+
case signal = sig_queue.shift
|
137
|
+
when :USR1, :USR2, :CONT
|
138
|
+
log "#{signal}: sending to all workers"
|
139
|
+
signal_all_workers(signal)
|
140
|
+
when :HUP
|
141
|
+
log "HUP: reload config file"
|
142
|
+
load_config_from_file
|
143
|
+
maintain_worker_count
|
144
|
+
when :WINCH
|
145
|
+
log "WINCH: gracefully stopping all workers"
|
146
|
+
@config = {}
|
147
|
+
maintain_worker_count
|
148
|
+
when :QUIT
|
149
|
+
log "QUIT: graceful shutdown, waiting for children"
|
150
|
+
signal_all_workers(:QUIT)
|
151
|
+
reap_all_workers(0) # will hang until all workers are shutdown
|
152
|
+
:break
|
153
|
+
when :INT
|
154
|
+
log "INT: immediate shutdown (graceful worker shutdown)"
|
155
|
+
signal_all_workers(:QUIT)
|
156
|
+
:break
|
157
|
+
when :TERM
|
158
|
+
log "TERM: immediate shutdown (and immediate worker shutdown)"
|
159
|
+
signal_all_workers(:TERM)
|
160
|
+
:break
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# }}}
|
165
|
+
# start, join, and master sleep {{{
|
166
|
+
|
77
167
|
def start
|
78
168
|
procline("(starting)")
|
79
169
|
init_self_pipe!
|
80
170
|
init_sig_handlers!
|
81
171
|
maintain_worker_count
|
82
172
|
procline("(started)")
|
83
|
-
log "****
|
173
|
+
log "**** started master at PID: #{Process.pid}"
|
84
174
|
log "**** Pool contains PIDs: #{all_pids.inspect}"
|
85
175
|
self
|
86
176
|
end
|
@@ -89,84 +179,78 @@ module Resque
|
|
89
179
|
loop do
|
90
180
|
reap_all_workers
|
91
181
|
break if handle_sig_queue! == :break
|
92
|
-
|
182
|
+
if sig_queue.empty?
|
183
|
+
master_sleep
|
184
|
+
maintain_worker_count
|
185
|
+
end
|
93
186
|
procline("managing #{all_pids.inspect}")
|
94
187
|
end
|
95
188
|
procline("(shutting down)")
|
96
189
|
#stop # gracefully shutdown all workers on our way out
|
190
|
+
log "**** master complete"
|
97
191
|
#unlink_pid_safe(pid) if pid
|
98
192
|
end
|
99
193
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
when :TERM, :INT # immediate shutdown
|
107
|
-
#stop(false)
|
108
|
-
:break
|
194
|
+
def master_sleep
|
195
|
+
begin
|
196
|
+
ready = IO.select([self_pipe.first], nil, nil, 1) or return
|
197
|
+
ready.first && ready.first.first or return
|
198
|
+
loop { self_pipe.first.read_nonblock(CHUNK_SIZE) }
|
199
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
109
200
|
end
|
110
201
|
end
|
111
202
|
|
112
|
-
|
203
|
+
# }}}
|
204
|
+
# worker process management {{{
|
205
|
+
|
206
|
+
def reap_all_workers(waitpid_flags=Process::WNOHANG)
|
113
207
|
begin
|
114
208
|
loop do
|
115
|
-
wpid, status = Process.waitpid2(-1,
|
209
|
+
wpid, status = Process.waitpid2(-1, waitpid_flags)
|
116
210
|
wpid or break
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
#self.pid = pid.chomp('.oldbin') if pid
|
121
|
-
#proc_name 'master'
|
122
|
-
#else
|
123
|
-
worker = delete_worker(wpid) #and worker.tmp.close rescue nil
|
124
|
-
log "reaped #{status.inspect} " \
|
125
|
-
"worker=#{worker.nr rescue 'unknown'}"
|
126
|
-
#end
|
211
|
+
worker = delete_worker(wpid)
|
212
|
+
# TODO: close any file descriptors connected to worker, if any
|
213
|
+
log "** reaped #{status.inspect}, worker=#{worker.queues.join(",")}"
|
127
214
|
end
|
128
215
|
rescue Errno::ECHILD
|
129
216
|
end
|
130
217
|
end
|
131
218
|
|
132
219
|
def delete_worker(pid)
|
133
|
-
|
134
|
-
|
220
|
+
worker = nil
|
221
|
+
workers.detect do |queues, pid_to_worker|
|
222
|
+
worker = pid_to_worker.delete(pid)
|
135
223
|
end
|
224
|
+
worker
|
136
225
|
end
|
137
226
|
|
138
|
-
def
|
139
|
-
|
140
|
-
ready = IO.select([SELF_PIPE.first], nil, nil, 1) or return
|
141
|
-
ready.first && ready.first.first or return
|
142
|
-
loop { SELF_PIPE.first.read_nonblock(CHUNK_SIZE) }
|
143
|
-
rescue Errno::EAGAIN, Errno::EINTR
|
144
|
-
end
|
227
|
+
def all_pids
|
228
|
+
workers.map {|q,workers| workers.keys }.flatten
|
145
229
|
end
|
146
230
|
|
147
|
-
def
|
148
|
-
|
149
|
-
|
150
|
-
rescue Errno::EAGAIN, Errno::EINTR
|
151
|
-
# pipe is full, master should wake up anyways
|
152
|
-
retry
|
231
|
+
def signal_all_workers(signal)
|
232
|
+
all_pids.each do |pid|
|
233
|
+
Process.kill signal, pid
|
153
234
|
end
|
154
235
|
end
|
155
236
|
|
237
|
+
# }}}
|
238
|
+
# ???: maintain_worker_count, all_known_queues {{{
|
239
|
+
|
156
240
|
def maintain_worker_count
|
157
|
-
|
158
|
-
|
241
|
+
all_known_queues.each do |queues|
|
242
|
+
delta = worker_delta_for(queues)
|
159
243
|
spawn_missing_workers_for(queues) if delta > 0
|
160
|
-
|
244
|
+
quit_excess_workers_for(queues) if delta < 0
|
161
245
|
end
|
162
246
|
end
|
163
247
|
|
164
|
-
def
|
165
|
-
|
248
|
+
def all_known_queues
|
249
|
+
config.keys | workers.keys
|
166
250
|
end
|
167
251
|
|
168
|
-
|
169
|
-
#
|
252
|
+
# }}}
|
253
|
+
# methods that operate on a single grouping of queues {{{
|
170
254
|
# perhaps this means a class is waiting to be extracted
|
171
255
|
|
172
256
|
def spawn_missing_workers_for(queues)
|
@@ -175,8 +259,19 @@ module Resque
|
|
175
259
|
end
|
176
260
|
end
|
177
261
|
|
262
|
+
def quit_excess_workers_for(queues)
|
263
|
+
delta = -worker_delta_for(queues)
|
264
|
+
pids_for(queues)[0...delta].each do |pid|
|
265
|
+
Process.kill("QUIT", pid)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
178
269
|
def worker_delta_for(queues)
|
179
|
-
|
270
|
+
config.fetch(queues, 0) - workers.fetch(queues, []).size
|
271
|
+
end
|
272
|
+
|
273
|
+
def pids_for(queues)
|
274
|
+
workers[queues].keys
|
180
275
|
end
|
181
276
|
|
182
277
|
def spawn_worker!(queues)
|
@@ -184,20 +279,23 @@ module Resque
|
|
184
279
|
pid = fork do
|
185
280
|
log "*** Starting worker #{worker}"
|
186
281
|
call_after_prefork!
|
187
|
-
|
282
|
+
reset_sig_handlers!
|
283
|
+
#self_pipe.each {|io| io.close }
|
284
|
+
worker.work(ENV['INTERVAL'] || DEFAULT_WORKER_INTERVAL) # interval, will block
|
188
285
|
end
|
286
|
+
workers[queues] ||= {}
|
189
287
|
workers[queues][pid] = worker
|
190
288
|
end
|
191
289
|
|
192
290
|
def create_worker(queues)
|
193
291
|
queues = queues.to_s.split(',')
|
194
|
-
worker =
|
292
|
+
worker = PooledWorker.new(*queues)
|
195
293
|
worker.verbose = ENV['LOGGING'] || ENV['VERBOSE']
|
196
294
|
worker.very_verbose = ENV['VVERBOSE']
|
197
295
|
worker
|
198
|
-
rescue Resque::NoQueueError
|
199
|
-
abort "set QUEUE env var, e.g. $ QUEUE=critical,high rake resque:work"
|
200
296
|
end
|
201
297
|
|
298
|
+
# }}}
|
299
|
+
|
202
300
|
end
|
203
301
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Resque
|
2
|
+
class Pool
|
3
|
+
module Logging
|
4
|
+
|
5
|
+
# Given a string, sets the procline ($0)
|
6
|
+
# Procline is always in the format of:
|
7
|
+
# resque-pool-master: STRING
|
8
|
+
def procline(string)
|
9
|
+
$0 = "resque-pool-master: #{string}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# TODO: make this use an actual logger
|
13
|
+
def log(message)
|
14
|
+
puts message
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Resque::Pool
|
2
|
+
|
3
|
+
class PooledWorker < ::Resque::Worker
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
@pool_master_pid = Process.pid
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def pool_master_has_gone_away?
|
11
|
+
@pool_master_pid && @pool_master_pid != Process.ppid
|
12
|
+
end
|
13
|
+
|
14
|
+
# this allows us to shutdown
|
15
|
+
def shutdown
|
16
|
+
@shutdown || pool_master_has_gone_away?
|
17
|
+
end
|
18
|
+
|
19
|
+
# this entire method (except for one line) is copied and pasted from
|
20
|
+
# resque-1.9.9. If shutdown were used as a method (attr_reader) rather
|
21
|
+
# than an instance variable, I wouldn't need to reduplicate this. :-(
|
22
|
+
#
|
23
|
+
# hopefully I can get defunkt to accept my patch for this.
|
24
|
+
# Until it is, the resque-pool gem will depend on an exact version of
|
25
|
+
# resque.
|
26
|
+
def work(interval = 5, &block)
|
27
|
+
$0 = "resque: Starting"
|
28
|
+
startup
|
29
|
+
|
30
|
+
loop do
|
31
|
+
#### THIS IS THE ONLY LINE THAT IS CHANGED
|
32
|
+
break if shutdown
|
33
|
+
#### THAT WAS THE ONLY LINE THAT WAS CHANGED
|
34
|
+
|
35
|
+
if not @paused and job = reserve
|
36
|
+
log "got: #{job.inspect}"
|
37
|
+
run_hook :before_fork
|
38
|
+
working_on job
|
39
|
+
|
40
|
+
if @child = fork
|
41
|
+
rand # Reseeding
|
42
|
+
procline "Forked #{@child} at #{Time.now.to_i}"
|
43
|
+
Process.wait
|
44
|
+
else
|
45
|
+
procline "Processing #{job.queue} since #{Time.now.to_i}"
|
46
|
+
perform(job, &block)
|
47
|
+
exit! unless @cant_fork
|
48
|
+
end
|
49
|
+
|
50
|
+
done_working
|
51
|
+
@child = nil
|
52
|
+
else
|
53
|
+
break if interval.to_i == 0
|
54
|
+
log! "Sleeping for #{interval.to_i}"
|
55
|
+
procline @paused ? "Paused" : "Waiting for #{@queues.join(',')}"
|
56
|
+
sleep interval.to_i
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
ensure
|
61
|
+
unregister_worker
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/resque/pool/tasks.rb
CHANGED
@@ -1,17 +1,12 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
|
-
# require 'resque/pool/tasks'
|
4
|
-
# and configure "resque:setup" task to start up the environment, initialize
|
5
|
-
# RESQUE_POOL_CONFIG, and setup other resque hooks
|
6
|
-
|
7
3
|
namespace :resque do
|
8
4
|
task :setup
|
9
5
|
|
10
|
-
desc "Launch a pool of resque workers
|
6
|
+
desc "Launch a pool of resque workers"
|
11
7
|
task :pool => :setup do
|
12
|
-
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true
|
13
8
|
require 'resque/pool'
|
14
|
-
Resque::Pool.
|
9
|
+
Resque::Pool.run
|
15
10
|
end
|
16
11
|
|
17
12
|
end
|
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
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- nicholas a. evans
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-07-29 00:00:00 -04:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -39,14 +39,14 @@ dependencies:
|
|
39
39
|
requirement: &id002 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
|
-
- -
|
42
|
+
- - "="
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
hash:
|
44
|
+
hash: 33
|
45
45
|
segments:
|
46
46
|
- 1
|
47
47
|
- 9
|
48
|
-
-
|
49
|
-
version: 1.9.
|
48
|
+
- 9
|
49
|
+
version: 1.9.9
|
50
50
|
type: :runtime
|
51
51
|
version_requirements: *id002
|
52
52
|
description: " quickly and easily fork a pool of resque workers,\n saving memory (w/REE) and monitoring their uptime\n"
|
@@ -61,6 +61,9 @@ extra_rdoc_files: []
|
|
61
61
|
files:
|
62
62
|
- lib/resque/pool.rb
|
63
63
|
- lib/resque/pool/tasks.rb
|
64
|
+
- lib/resque/pool/pooled_worker.rb
|
65
|
+
- lib/resque/pool/logging.rb
|
66
|
+
- README.md
|
64
67
|
has_rdoc: true
|
65
68
|
homepage: http://github.com/nevans/resque-pool
|
66
69
|
licenses: []
|