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 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
- attr_reader :pool_config
10
+ include Logging
11
+ attr_reader :config
8
12
  attr_reader :workers
9
13
 
10
- SELF_PIPE = []
11
- SIG_QUEUE = []
12
- QUEUE_SIGS = [ :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, ]
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
- @pool_config = config.dup
17
- @workers = pool_config.keys.inject({}) {|h,k| h[k] = {}; h}
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
- # Given a string, sets the procline ($0)
43
- # Procline is always in the format of:
44
- # resque-pool-master: STRING
45
- def procline(string)
46
- $0 = "resque-pool-master: #{string}"
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
- # TODO: make this use an actual logger
50
- def log(message)
51
- puts message
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
- SELF_PIPE.each { |io| io.close rescue nil }
56
- SELF_PIPE.replace(IO.pipe)
57
- SELF_PIPE.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
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) { |_| awaken_master }
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 SIG_QUEUE.size < 5
69
- SIG_QUEUE << signal
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=#{SIG_QUEUE.inspect}"
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 "**** done in pool master #initialize"
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
- maintain_worker_count if SIG_QUEUE.empty?
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 handle_sig_queue!
101
- case SIG_QUEUE.shift
102
- when nil
103
- master_sleep
104
- when :QUIT # graceful shutdown
105
- :break
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
- def reap_all_workers
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, Process::WNOHANG)
209
+ wpid, status = Process.waitpid2(-1, waitpid_flags)
116
210
  wpid or break
117
- #if reexec_pid == wpid
118
- #log "reaped #{status.inspect} exec()-ed"
119
- #self.reexec_pid = 0
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
- workers.each do |queues, pid_to_worker|
134
- pid_to_worker.delete(pid)
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 master_sleep
139
- begin
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 awaken_master
148
- begin
149
- SELF_PIPE.last.write_nonblock('.') # wakeup master process from select
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
- pool_config.each do |queues, count|
158
- next if (delta = worker_delta_for(queues)) == 0
241
+ all_known_queues.each do |queues|
242
+ delta = worker_delta_for(queues)
159
243
  spawn_missing_workers_for(queues) if delta > 0
160
- #TODO: quit_excess_workers_for(queues)
244
+ quit_excess_workers_for(queues) if delta < 0
161
245
  end
162
246
  end
163
247
 
164
- def all_pids
165
- workers.map {|q,workers| workers.keys }.flatten
248
+ def all_known_queues
249
+ config.keys | workers.keys
166
250
  end
167
251
 
168
- ##
169
- # all methods below operate on a single grouping of queues
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
- pool_config[queues] - workers[queues].size
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
- worker.work(ENV['INTERVAL'] || 5) # interval, will block
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 = Resque::Worker.new(*queues)
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
@@ -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 (set RESQUE_POOL_CONFIG)"
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.new(RESQUE_POOL_CONFIG).start.join
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: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
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-06-15 00:00:00 -04:00
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: 49
44
+ hash: 33
45
45
  segments:
46
46
  - 1
47
47
  - 9
48
- - 1
49
- version: 1.9.1
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: []