resque-pool 0.0.2 → 0.0.4

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/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: []