resque-pool 0.2.0 → 0.3.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +14 -0
- data/ExperimentalFeatures.md +48 -0
- data/README.md +7 -2
- data/lib/resque/pool.rb +69 -61
- data/lib/resque/pool/cli.rb +1 -1
- data/lib/resque/pool/manager.rb +11 -0
- data/lib/resque/pool/memory_manager.rb +118 -0
- data/lib/resque/pool/orphan_watcher.rb +49 -0
- data/lib/resque/pool/version.rb +1 -1
- data/lib/resque/pool/worker_type_manager.rb +102 -0
- metadata +17 -8
data/Changelog.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
## unreleased
|
2
2
|
|
3
|
+
* enhancement: new callbacks for configuration
|
4
|
+
* `Resque::Pool.configure do |pool| ... end`
|
5
|
+
* `pool.after_manager_wakeup`
|
6
|
+
* `pool.to_calculate_worker_offset`
|
7
|
+
* `pool.after_prefork` (instance callback prefered over class callback)
|
8
|
+
* experimental: memory management
|
9
|
+
* experimental: check orphaned workers
|
10
|
+
* development: a good bit of code cleanup and rearrangement
|
11
|
+
|
12
|
+
See ExperimentalFeatures.md for more info. Thanks to Jason Haruska for these
|
13
|
+
features!
|
14
|
+
|
15
|
+
## 0.2.0 (2011-03-15)
|
16
|
+
|
3
17
|
* new feature: sending `HUP` to pool manager will reload the logfiles and
|
4
18
|
gracefully restart all workers.
|
5
19
|
* enhancement: logging now includes timestamp, process "name" (worker or
|
@@ -0,0 +1,48 @@
|
|
1
|
+
Experimental Features
|
2
|
+
---------------------
|
3
|
+
|
4
|
+
Features listed here should not cause you problems if you don't use them... and
|
5
|
+
probably won't cause you problems if you *do* use them. Maybe. We hope. ;-)
|
6
|
+
Once these features have stood the test of time, can be trusted to work cross
|
7
|
+
platform, are well documented and tested, and have settled on a stable API,
|
8
|
+
then we'll stop calling them experimental and probably give them command line
|
9
|
+
options. Until then, be forewarned that it might not work for you and the API
|
10
|
+
might change between minor releases.
|
11
|
+
|
12
|
+
### Memory management
|
13
|
+
|
14
|
+
A memory manager is provided which can check once a minute to see if any of
|
15
|
+
your workers are using too much memory. If a worker is over the soft limit it
|
16
|
+
will be sent a QUIT signal. If a worker is over the hard signal it will be
|
17
|
+
sent a TERM signal, and if it is still running a minute later it will be sent a
|
18
|
+
KILL signal. To use the memory manager, add something like the following to
|
19
|
+
your Rakefile config:
|
20
|
+
|
21
|
+
task "resque:pool:setup" do
|
22
|
+
Resque::Pool.configure do |pool|
|
23
|
+
# memory limits are in MB
|
24
|
+
hard_limit = 250
|
25
|
+
soft_limit = 200
|
26
|
+
mm = Resque::Pool::MemoryManager.new(pool, hard_limit, soft_limit)
|
27
|
+
pool.after_manager_wakeup { mm.monitor_memory_usage }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
### Wait for orphaned workers to quit.
|
32
|
+
|
33
|
+
When restarting resque-pool, some orphaned workers may be left over from the
|
34
|
+
previous manager, even after the new manager has started up and forked its
|
35
|
+
workers. If you set the `RESQUE_WAIT_FOR_ORPHANS` environment variable, then
|
36
|
+
the new manager will not start up all of its configured workers until all of
|
37
|
+
the orphaned workers have finished. This might be useful for long running jobs
|
38
|
+
on memory constrained servers.
|
39
|
+
|
40
|
+
To use this, add something like the following to your Rakefile config:
|
41
|
+
|
42
|
+
task "resque:pool:setup" do
|
43
|
+
Resque::Pool.configure do |pool|
|
44
|
+
orphan_watcher = Resque::Pool::OrphanWatcher.new(pool)
|
45
|
+
pool.to_calculate_worker_offset { orphan_watcher.worker_offset }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
data/README.md
CHANGED
@@ -130,6 +130,11 @@ their current job) if the manager process disappears before them.
|
|
130
130
|
You can specify an alternate config file by setting the `RESQUE_POOL_CONFIG` or
|
131
131
|
with the `--config` command line option.
|
132
132
|
|
133
|
+
Experimental Features
|
134
|
+
---------------------
|
135
|
+
|
136
|
+
See `ExperimentalFeatures.md` for some potentially useful works in progress.
|
137
|
+
|
133
138
|
TODO
|
134
139
|
-----
|
135
140
|
|
@@ -138,8 +143,8 @@ See [the TODO list](https://github.com/nevans/resque-pool/issues) at github issu
|
|
138
143
|
Contributors
|
139
144
|
-------------
|
140
145
|
|
146
|
+
* Jason Haruska from Backupify (pidfile management, memory management, wait for orphaned workers)
|
141
147
|
* John Schult (config file can be split by environment)
|
142
148
|
* Stephen Celis (increased gemspec sanity)
|
143
149
|
* Vincent Agnello, Robert Kamunyori, Paul Kauders; for pairing with me at
|
144
|
-
B'more on Rails Open Source Hack Nights. :)
|
145
|
-
|
150
|
+
[B'more on Rails](http://twitter.com/bmoreonrails) Open Source Hack Nights. :)
|
data/lib/resque/pool.rb
CHANGED
@@ -2,7 +2,12 @@
|
|
2
2
|
require 'resque'
|
3
3
|
require 'resque/pool/version'
|
4
4
|
require 'resque/pool/logging'
|
5
|
-
require 'resque/pool/
|
5
|
+
require 'resque/pool/manager'
|
6
|
+
require 'resque/pool/worker_type_manager'
|
7
|
+
# experimental features!
|
8
|
+
require 'resque/pool/memory_manager'
|
9
|
+
require 'resque/pool/orphan_watcher'
|
10
|
+
|
6
11
|
require 'fcntl'
|
7
12
|
require 'yaml'
|
8
13
|
|
@@ -23,26 +28,70 @@ module Resque
|
|
23
28
|
procline "(initialized)"
|
24
29
|
end
|
25
30
|
|
26
|
-
# Config:
|
31
|
+
# Config: hooks {{{
|
32
|
+
|
33
|
+
# The +configure+ block will be run once during pool startup, after the
|
34
|
+
# internal initialization is complete, but before any workers are started
|
35
|
+
# up. This is for configuring any runtime callbacks that you want to
|
36
|
+
# customize (e.g. +after_wakup+, +worker_offset_handler+)
|
37
|
+
#
|
38
|
+
# Call with a block to set.
|
39
|
+
# Call with no arguments to return the hook.
|
40
|
+
def self.configure(&block)
|
41
|
+
block ? (@configure = block) : @configure
|
42
|
+
end
|
43
|
+
|
44
|
+
def call_configure!
|
45
|
+
self.class.configure && self.class.configure.call(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
# The +after_manager_wakeup+ hook will be run in the pool manager every
|
49
|
+
# time the pool manager wakes up from IO.select (normally once a second).
|
50
|
+
# It will run immediately before the pool manager performs its normal
|
51
|
+
# worker maintenance (starting and stopping workers as necessary). If you
|
52
|
+
# have some special logic for killing workers that are taking too long or
|
53
|
+
# using too much memory, this is the place to put it.
|
54
|
+
#
|
55
|
+
# Call with a block to set the hook.
|
56
|
+
# Call with no arguments to return the hook.
|
57
|
+
def after_manager_wakeup(&block)
|
58
|
+
block ? (@after_manager_wakeup = block) : @after_manager_wakeup
|
59
|
+
end
|
60
|
+
|
61
|
+
def call_after_manager_wakeup!
|
62
|
+
after_manager_wakeup && after_manager_wakeup.call(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_calculate_worker_offset(&block)
|
66
|
+
block ? (@to_calculate_worker_offset = block) : @to_calculate_worker_offset
|
67
|
+
end
|
68
|
+
|
69
|
+
# deprecated by instance level +after_prefork+ hook
|
70
|
+
def self.after_prefork(&block)
|
71
|
+
#log '"Resque::Pool.after_prefork(&blk)" is deprecated.'
|
72
|
+
#log 'Please use "Resque::Pool.configure {|p| p.after_prefork(&blk) }" instead.'
|
73
|
+
block ? (@after_prefork = block) : @after_prefork
|
74
|
+
end
|
27
75
|
|
28
|
-
# The
|
76
|
+
# The +after_prefork+ hook will be run in workers if you are using the
|
29
77
|
# preforking master worker to save memory. Use this hook to reload
|
30
78
|
# database connections and so forth to ensure that they're not shared
|
31
79
|
# among workers.
|
32
80
|
#
|
33
81
|
# Call with a block to set the hook.
|
34
82
|
# Call with no arguments to return the hook.
|
35
|
-
def
|
83
|
+
def after_prefork(&block)
|
36
84
|
block ? (@after_prefork = block) : @after_prefork
|
37
85
|
end
|
38
86
|
|
39
87
|
# Set the after_prefork proc.
|
40
|
-
def
|
88
|
+
def after_prefork=(after_prefork)
|
41
89
|
@after_prefork = after_prefork
|
42
90
|
end
|
43
91
|
|
44
92
|
def call_after_prefork!
|
45
|
-
|
93
|
+
(after_prefork && after_prefork.call) ||
|
94
|
+
(self.class.after_prefork && self.class.after_prefork.call)
|
46
95
|
end
|
47
96
|
|
48
97
|
# }}}
|
@@ -93,7 +142,9 @@ module Resque
|
|
93
142
|
end
|
94
143
|
|
95
144
|
def environment
|
96
|
-
if defined?
|
145
|
+
if defined? Rails
|
146
|
+
Rails.env
|
147
|
+
elsif defined? RAILS_ENV # keep compatibility with older versions of rails
|
97
148
|
RAILS_ENV
|
98
149
|
else
|
99
150
|
ENV['RACK_ENV'] || ENV['RAILS_ENV'] || ENV['RESQUE_ENV']
|
@@ -144,10 +195,6 @@ module Resque
|
|
144
195
|
end
|
145
196
|
end
|
146
197
|
|
147
|
-
def reset_sig_handlers!
|
148
|
-
QUEUE_SIGS.each {|sig| trap(sig, "DEFAULT") }
|
149
|
-
end
|
150
|
-
|
151
198
|
def handle_sig_queue!
|
152
199
|
case signal = sig_queue.shift
|
153
200
|
when :USR1, :USR2, :CONT
|
@@ -185,6 +232,8 @@ module Resque
|
|
185
232
|
# start, join, and master sleep {{{
|
186
233
|
|
187
234
|
def start
|
235
|
+
procline("(configuring)")
|
236
|
+
call_configure!
|
188
237
|
procline("(starting)")
|
189
238
|
init_self_pipe!
|
190
239
|
init_sig_handlers!
|
@@ -209,6 +258,7 @@ module Resque
|
|
209
258
|
break if handle_sig_queue! == :break
|
210
259
|
if sig_queue.empty?
|
211
260
|
master_sleep
|
261
|
+
call_after_manager_wakeup!
|
212
262
|
maintain_worker_count
|
213
263
|
end
|
214
264
|
procline("managing #{all_pids.inspect}")
|
@@ -242,6 +292,8 @@ module Resque
|
|
242
292
|
# TODO: close any file descriptors connected to worker, if any
|
243
293
|
log "Reaped resque worker[#{status.pid}] (status: #{status.exitstatus}) queues: #{worker.queues.join(",")}"
|
244
294
|
end
|
295
|
+
rescue Errno::EINTR
|
296
|
+
retry
|
245
297
|
rescue Errno::ECHILD, QuitNowException
|
246
298
|
end
|
247
299
|
end
|
@@ -265,64 +317,20 @@ module Resque
|
|
265
317
|
end
|
266
318
|
|
267
319
|
# }}}
|
268
|
-
#
|
320
|
+
# maintain_worker_count, all_known_worker_types, worker_offset {{{
|
269
321
|
|
270
322
|
def maintain_worker_count
|
271
|
-
|
272
|
-
|
273
|
-
spawn_missing_workers_for(queues) if delta > 0
|
274
|
-
quit_excess_workers_for(queues) if delta < 0
|
323
|
+
all_known_worker_types.each do |queues|
|
324
|
+
WorkerTypeManager.new(self, queues).maintain_worker_count(worker_offset)
|
275
325
|
end
|
276
326
|
end
|
277
327
|
|
278
|
-
def
|
328
|
+
def all_known_worker_types
|
279
329
|
config.keys | workers.keys
|
280
330
|
end
|
281
331
|
|
282
|
-
|
283
|
-
|
284
|
-
# perhaps this means a class is waiting to be extracted
|
285
|
-
|
286
|
-
def spawn_missing_workers_for(queues)
|
287
|
-
worker_delta_for(queues).times do |nr|
|
288
|
-
spawn_worker!(queues)
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
def quit_excess_workers_for(queues)
|
293
|
-
delta = -worker_delta_for(queues)
|
294
|
-
pids_for(queues)[0...delta].each do |pid|
|
295
|
-
Process.kill("QUIT", pid)
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
def worker_delta_for(queues)
|
300
|
-
config.fetch(queues, 0) - workers.fetch(queues, []).size
|
301
|
-
end
|
302
|
-
|
303
|
-
def pids_for(queues)
|
304
|
-
workers[queues].keys
|
305
|
-
end
|
306
|
-
|
307
|
-
def spawn_worker!(queues)
|
308
|
-
worker = create_worker(queues)
|
309
|
-
pid = fork do
|
310
|
-
log_worker "Starting worker #{worker}"
|
311
|
-
call_after_prefork!
|
312
|
-
reset_sig_handlers!
|
313
|
-
#self_pipe.each {|io| io.close }
|
314
|
-
worker.work(ENV['INTERVAL'] || DEFAULT_WORKER_INTERVAL) # interval, will block
|
315
|
-
end
|
316
|
-
workers[queues] ||= {}
|
317
|
-
workers[queues][pid] = worker
|
318
|
-
end
|
319
|
-
|
320
|
-
def create_worker(queues)
|
321
|
-
queues = queues.to_s.split(',')
|
322
|
-
worker = PooledWorker.new(*queues)
|
323
|
-
worker.verbose = ENV['LOGGING'] || ENV['VERBOSE']
|
324
|
-
worker.very_verbose = ENV['VVERBOSE']
|
325
|
-
worker
|
332
|
+
def worker_offset
|
333
|
+
to_calculate_worker_offset && to_calculate_worker_offset.call || 0
|
326
334
|
end
|
327
335
|
|
328
336
|
# }}}
|
data/lib/resque/pool/cli.rb
CHANGED
@@ -99,7 +99,7 @@ where [options] are:
|
|
99
99
|
def setup_environment(opts)
|
100
100
|
ENV["RACK_ENV"] = ENV["RAILS_ENV"] = ENV["RESQUE_ENV"] = opts[:environment] if opts[:environment]
|
101
101
|
log "Resque Pool running in #{ENV["RAILS_ENV"] || "development"} environment"
|
102
|
-
ENV["RESQUE_POOL_CONFIG"] = opts[:config] if opts[:config]
|
102
|
+
ENV["RESQUE_POOL_CONFIG"] = opts[:config].to_s if opts[:config]
|
103
103
|
end
|
104
104
|
|
105
105
|
def start_pool
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'resque/pool/logging'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
class Pool
|
5
|
+
class MemoryManager
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
attr_reader :hard_limit, :soft_limit
|
9
|
+
|
10
|
+
def initialize(hard_limit=250, soft_limit=200)
|
11
|
+
@hard_limit = hard_limit
|
12
|
+
@soft_limit = soft_limit
|
13
|
+
end
|
14
|
+
|
15
|
+
def monitor_memory_usage(pool_manager)
|
16
|
+
#only check every minute
|
17
|
+
if @last_mem_check.nil? || @last_mem_check < Time.now - 60
|
18
|
+
hard_kill_workers
|
19
|
+
pool_manager.all_pids.each do |pid|
|
20
|
+
total_usage = memory_usage(pid)
|
21
|
+
child_pid = find_child_pid(pid)
|
22
|
+
total_usage += memory_usage(child_pid) if child_pid
|
23
|
+
|
24
|
+
if total_usage > hard_limit
|
25
|
+
log "Terminating worker #{pid} for using #{total_usage}MB memory"
|
26
|
+
stop_worker(pid)
|
27
|
+
elsif total_usage > soft_limit
|
28
|
+
log "Gracefully shutting down worker #{pid} for using #{total_usage}MB memory"
|
29
|
+
stop_worker(pid, :QUIT)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
@last_mem_check = Time.now
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def add_killed_worker(pid)
|
39
|
+
@term_workers ||= []
|
40
|
+
@term_workers << pid if pid
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_child_pid(parent_pid)
|
44
|
+
begin
|
45
|
+
p = `ps --ppid #{parent_pid} -o pid --no-header`.to_i
|
46
|
+
p == 0 ? nil : p
|
47
|
+
rescue Errno::EINTR
|
48
|
+
retry
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def hard_kill_workers
|
53
|
+
@term_workers ||= []
|
54
|
+
#look for workers that didn't terminate
|
55
|
+
@term_workers.delete_if {|pid| !process_exists?(pid)}
|
56
|
+
#send the rest a -9
|
57
|
+
@term_workers.each {|pid| `kill -9 #{pid}`}
|
58
|
+
end
|
59
|
+
|
60
|
+
def hostname
|
61
|
+
begin
|
62
|
+
@hostname ||= `hostname`.strip
|
63
|
+
rescue Errno::EINTR
|
64
|
+
retry
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def memory_usage(pid)
|
69
|
+
smaps_filename = "/proc/#{pid}/smaps"
|
70
|
+
#Grab actual memory usage from proc in MB
|
71
|
+
begin
|
72
|
+
mem_usage = `
|
73
|
+
if [ -f #{smaps_filename} ];
|
74
|
+
then
|
75
|
+
grep Private_Dirty #{smaps_filename} | awk '{s+=$2} END {printf("%d", s/1000)}'
|
76
|
+
else echo "0"
|
77
|
+
fi
|
78
|
+
`.to_i
|
79
|
+
rescue Errno::EINTR
|
80
|
+
retry
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# TODO: DRY up this and the pidfile checker in Resque::Pool::CLI
|
85
|
+
def process_exists?(pid)
|
86
|
+
begin
|
87
|
+
ps_line = `ps -p #{pid} --no-header`
|
88
|
+
rescue Errno::EINTR
|
89
|
+
retry
|
90
|
+
end
|
91
|
+
!ps_line.nil? && ps_line.strip != ''
|
92
|
+
end
|
93
|
+
|
94
|
+
def stop_worker(pid, signal=:TERM)
|
95
|
+
begin
|
96
|
+
worker = Resque.working.find do |w|
|
97
|
+
host, worker_pid, queues = w.id.split(':')
|
98
|
+
w if worker_pid.to_i == pid.to_i && host == hostname
|
99
|
+
end
|
100
|
+
if worker
|
101
|
+
encoded_job = worker.job
|
102
|
+
verb = signal == :QUIT ? 'Graceful' : 'Forcing'
|
103
|
+
total_time = Time.now - Time.parse(encoded_job['run_at']) rescue 0
|
104
|
+
log "#{verb} shutdown while processing: #{encoded_job} -- ran for #{'%.2f' % total_time}s"
|
105
|
+
end
|
106
|
+
Process.kill signal, pid
|
107
|
+
if signal == :TERM
|
108
|
+
add_killed_worker(pid)
|
109
|
+
add_killed_worker(find_child_pid(pid))
|
110
|
+
end
|
111
|
+
rescue Errno::EINTR
|
112
|
+
retry
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'resque/pool/logging'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
class Pool
|
5
|
+
class OrphanWatcher
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
attr_reader :pool_manager
|
9
|
+
|
10
|
+
def initialize(pool_manager)
|
11
|
+
@pool_manager = pool_manager
|
12
|
+
end
|
13
|
+
|
14
|
+
def worker_offset
|
15
|
+
orphaned_worker_count / pool_manager.all_known_worker_types.size
|
16
|
+
end
|
17
|
+
|
18
|
+
def orphaned_worker_count
|
19
|
+
if @last_orphaned_check.nil? || @last_orphaned_check < Time.now - 60
|
20
|
+
if @orphaned_pids.nil?
|
21
|
+
begin
|
22
|
+
pids_with_parents = `ps -Af | grep resque | grep -v grep | grep -v resque-web | grep -v master | awk '{printf("%d %d\\n", $2, $3)}'`.split("\n")
|
23
|
+
rescue Errno::EINTR
|
24
|
+
retry
|
25
|
+
end
|
26
|
+
pids = pids_with_parents.collect {|x| x.split[0].to_i}
|
27
|
+
parents = pids_with_parents.collect {|x| x.split[1].to_i}
|
28
|
+
pids.delete_if {|x| parents.include?(x)}
|
29
|
+
pids.delete_if {|x| all_pids.include?(x)}
|
30
|
+
@orphaned_pids = pids
|
31
|
+
elsif @orphaned_pids.size > 0
|
32
|
+
@orphaned_pids.delete_if do |pid|
|
33
|
+
begin
|
34
|
+
ps_out = `ps --no-heading p #{pid}`
|
35
|
+
ps_out.nil? || ps_out.strip == ''
|
36
|
+
rescue Errno::EINTR
|
37
|
+
retry
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@last_orphaned_check = Time.now
|
42
|
+
log "Current orphaned pids: #{@orphaned_pids}" if @orphaned_pids.size > 0
|
43
|
+
end
|
44
|
+
@orphaned_pids.size
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/resque/pool/version.rb
CHANGED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'resque/pool/logging'
|
2
|
+
require 'resque/pool/pooled_worker'
|
3
|
+
|
4
|
+
module Resque
|
5
|
+
class Pool
|
6
|
+
class WorkerTypeManager
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
attr_reader :pool_manager, :queues, :queue_array
|
10
|
+
|
11
|
+
def initialize(pool_manager, queues)
|
12
|
+
@pool_manager = pool_manager
|
13
|
+
@queues = queues
|
14
|
+
@queue_array = queues.to_s.split(',')
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO: Pool manager will hold onto WorkerTypeManagers, and will push the
|
18
|
+
# configuration directly into the WorkerTypeManagers
|
19
|
+
def configuration
|
20
|
+
{ :count => pool_manager.config.fetch(queues, 0), }
|
21
|
+
end
|
22
|
+
|
23
|
+
def maintain_worker_count(offset)
|
24
|
+
delta = worker_delta - offset
|
25
|
+
spawn_missing_workers_for(delta) if delta > 0
|
26
|
+
quit_excess_workers_for(delta) if delta < 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def pids
|
30
|
+
running_workers.keys
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO: Pool manager will hold onto WorkerTypeManagers,
|
34
|
+
# and WorkerTypeManager will store the workers itself
|
35
|
+
def running_workers
|
36
|
+
pool_manager.workers.fetch(queues, {})
|
37
|
+
end
|
38
|
+
|
39
|
+
def worker_delta
|
40
|
+
configuration[:count] - running_workers.size
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def create_worker
|
46
|
+
worker = PooledWorker.new(*queue_array)
|
47
|
+
worker.verbose = ENV['LOGGING'] || ENV['VERBOSE']
|
48
|
+
worker.very_verbose = ENV['VVERBOSE']
|
49
|
+
worker
|
50
|
+
end
|
51
|
+
|
52
|
+
def quit_excess_workers_for(delta)
|
53
|
+
if delta < 0
|
54
|
+
queue_pids = pids.clone
|
55
|
+
if queue_pids.size >= delta.abs
|
56
|
+
queue_pids[0...delta.abs].each {|pid| Process.kill("QUIT", pid)}
|
57
|
+
else
|
58
|
+
queue_pids.each {|pid| Process.kill("QUIT", pid)}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# TODO: Pool manager will hold onto WorkerTypeManagers,
|
64
|
+
# and WorkerTypeManager will store the pids itself
|
65
|
+
def register_new_worker_with_manager(worker, pid)
|
66
|
+
pool_manager.workers[queues] ||= {}
|
67
|
+
pool_manager.workers[queues][pid] = worker
|
68
|
+
end
|
69
|
+
|
70
|
+
def reset_sig_handlers!
|
71
|
+
QUEUE_SIGS.each {|sig| trap(sig, "DEFAULT") }
|
72
|
+
end
|
73
|
+
|
74
|
+
def spawn_missing_workers_for(delta)
|
75
|
+
delta.times { spawn_worker! } if delta > 0
|
76
|
+
end
|
77
|
+
|
78
|
+
def spawn_worker!
|
79
|
+
worker = create_worker
|
80
|
+
pid = fork do
|
81
|
+
start_forked_worker worker
|
82
|
+
end
|
83
|
+
register_new_worker_with_manager worker, pid
|
84
|
+
end
|
85
|
+
|
86
|
+
def start_forked_worker(worker)
|
87
|
+
reset_sig_handlers!
|
88
|
+
log_worker "Starting worker #{worker}"
|
89
|
+
pool_manager.call_after_prefork!
|
90
|
+
begin
|
91
|
+
worker.work(ENV['INTERVAL'] || DEFAULT_WORKER_INTERVAL) # interval, will block
|
92
|
+
rescue Errno::EINTR
|
93
|
+
log_worker "Caught interrupted system call Errno::EINTR. Retrying."
|
94
|
+
retry
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
metadata
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 62196369
|
5
|
+
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
|
10
|
+
- beta
|
11
|
+
- 1
|
12
|
+
version: 0.3.0.beta.1
|
11
13
|
platform: ruby
|
12
14
|
authors:
|
13
15
|
- nicholas a. evans
|
@@ -179,11 +181,16 @@ files:
|
|
179
181
|
- lib/resque/pool/tasks.rb
|
180
182
|
- lib/resque/pool/cli.rb
|
181
183
|
- lib/resque/pool/version.rb
|
184
|
+
- lib/resque/pool/manager.rb
|
182
185
|
- lib/resque/pool/pooled_worker.rb
|
186
|
+
- lib/resque/pool/memory_manager.rb
|
187
|
+
- lib/resque/pool/orphan_watcher.rb
|
188
|
+
- lib/resque/pool/worker_type_manager.rb
|
183
189
|
- lib/resque/pool/logging.rb
|
184
190
|
- config/cucumber.yml
|
185
191
|
- config/alternate.yml
|
186
192
|
- Gemfile
|
193
|
+
- ExperimentalFeatures.md
|
187
194
|
has_rdoc: true
|
188
195
|
homepage: http://github.com/nevans/resque-pool
|
189
196
|
licenses: []
|
@@ -205,12 +212,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
205
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
206
213
|
none: false
|
207
214
|
requirements:
|
208
|
-
- - "
|
215
|
+
- - ">"
|
209
216
|
- !ruby/object:Gem::Version
|
210
|
-
hash:
|
217
|
+
hash: 25
|
211
218
|
segments:
|
212
|
-
-
|
213
|
-
|
219
|
+
- 1
|
220
|
+
- 3
|
221
|
+
- 1
|
222
|
+
version: 1.3.1
|
214
223
|
requirements: []
|
215
224
|
|
216
225
|
rubyforge_project:
|