resque-pool 0.5.0 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3d0e06a4a17b962d8423595f52f762c2c648c323
4
- data.tar.gz: e108e81c14763bc5f4603cbb31f8d68a63e0cde9
3
+ metadata.gz: eada222fbbc16be4ae78356bafd528aecaa318c7
4
+ data.tar.gz: d3794970570d267f8b3421a1547179e2171d6e73
5
5
  SHA512:
6
- metadata.gz: d222c8f5475167a18b6a2115a7aed5b5513dda496d5e4eb3481f7735505ff28393e4b3f160d6b69a2e7eaf7843fac74f5912f35113f1f20eaf2a453bfc8e8505
7
- data.tar.gz: 91e0de1439d031b0490fd752e78cd4d22398e142aa4642cff1c5415c2d051636263ef609d5ebe1c4494dd5e930f06972e40f72233966b909526311e8fbbcf3d5
6
+ metadata.gz: 4e1a25962a81a7de91830fe1c785fd97505ec26e1df211124f9c58b54961b6a439b50e2aa53cb6bcad40098c6253b11dada9c199f4fa9598983acaab840266fa
7
+ data.tar.gz: 41578e6985fa8f4581479c2626db8a57a3e1ba1417ad17903b3587845d3b6980871f38b62b257f1f624b352d9af782d79f0168b6f7415cfa7c04391008b54ec3
data/Changelog.md CHANGED
@@ -1,6 +1,74 @@
1
- ## 0.4.0.dev (unreleased)
2
-
3
- * ???
1
+ ## 0.6.0 _(unreleased)_
2
+ [full changelog](https://github.com/nevans/resque-pool/compare/v0.5.0...master).
3
+
4
+ One big new feature: [Custom Config
5
+ Loader](https://github.com/nevans/resque-pool#custom-configuration-loader)
6
+ thanks to [joshuaflanagan](https://github.com/joshuaflanagan)!
7
+
8
+ Lots of cleanup in this release. Thanks to the contributers:
9
+
10
+ * [joshuaflanagan](https://github.com/joshuaflanagan) Custom config loader
11
+ * [grosser](https://github/grosser)
12
+ * ship less files in the gem
13
+ * remove trollop dependency
14
+ * remove -n -t -r -n -i commandline options since they were added unintentionally
15
+ * no longer hijacks shutdown for normal resque worker processes.
16
+ * [PatrickTulskie](https://github.com/PatrickTulskie) Reopening log files now
17
+ reopens *all* logs in memory (append write only; code copied from Unicorn)
18
+ * [jonleighton](https://github.com/jonleighton) pass worker instance to
19
+ `after_prefork` hook
20
+
21
+ ## 0.5.0 (2015-03-24)
22
+
23
+ Some more merges of long outstanding pull requests.
24
+
25
+ * _EVEN BETTER_ `TERM` support for Heroku than 0.4.0. ;)
26
+ * _DOCKER SUPPORT_ (don't go crazy when master pid is 1).
27
+ _(example Dockerfile soon?)_
28
+ * `--spawn_delay` option in case workers respawn too quickly
29
+ * Support `RUN_AT_EXIT_HOOKS`.
30
+ * And [more](https://github.com/nevans/resque-pool/compare/v0.4.0...v0.5.0).
31
+
32
+ Many thanks to the contributors! [JohnBat26](https://github.com/JohnBat26), Eric
33
+ Chapweske, [werkshy](https://github.com/werkshy),
34
+ [spajus](https://github.com/spajus), [greysteil](https://github.com/greysteil),
35
+ [tjsousa](https://github.com/tjsousa), [jkrall](https://github.com/jkrall),
36
+ [zmillman](https://github.com/zmillman), [nevans](http://github.com/nevans).
37
+
38
+ ## 0.4.0 (2015-01-28)
39
+
40
+ Another _long_ overdue maintenance release. Many users had been running the
41
+ various release candidates in production for over 16 months. 0.4.0 was based
42
+ on 0.4.0.rc2 and 0.4.0.rc3 was rolled up into 0.5.0 instead.
43
+
44
+ Better Heroku/`TERM_CHILD` support, better `upstart` process group control, ERB
45
+ in the config file, not-insane package size, and
46
+ [more](https://github.com/nevans/resque-pool/compare/v0.3.0...v0.4.0).
47
+
48
+ Many thanks to the contributors!
49
+
50
+ * Better `TERM_CHILD` support (useful for Heroku or anywhere else that only
51
+ sends `TERM` to quit) [@rayh](https://github.com/rayh) and
52
+ [@jjulian](https://github.com/jjulian)
53
+ * [@jjulian](https://github.com/jjulian):
54
+ * 0.3.0 accidentally packaged up 13MB of extra files! OOOPS... SORRY!
55
+ * better MacOS X compatibility
56
+ * missing LICENCE in gemspec
57
+ * [@jasonrclark](https://github.com/jasonrclark): `after_prefork` hook manages
58
+ an array of hooks, rather than one single hook
59
+ * [@mlanett](https://github.com/mlanett): Parse ERB in the config file (_very_
60
+ useful for hostname/environment switched configuration)
61
+ * [@xjlu](https://github.com/xjlu): Match the task deps in resque:work
62
+ * [@darbyfrey](https://github.com/darbyfrey): Fixing deprecation warnings in
63
+ newer versions of resque
64
+ * [@ewoodh20](https://github.com/ewoodh20): Use `Rails.env` if available
65
+ (`RAILS_ENV` is deprecated)
66
+ * [@dlackty](https://github.com/dlackty): example `god` config file
67
+ * [@mattdbridges](https://github.com/mattdbridges): fix order dependent specs
68
+ * [@nevans](https://github.com/nevans):
69
+ * Ignore `WINCH` signal when running non-daemonized (often in the terminal)
70
+ * Do not run children in the same process group (solves problems with `upstart`
71
+ sending `TERM` to all processes at once)
4
72
 
5
73
  ## 0.3.0 (2012-05-22)
6
74
 
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  Resque Pool
2
2
  ===========
3
3
 
4
- [![Build Status](https://secure.travis-ci.org/nevans/resque-pool.png)](http://travis-ci.org/nevans/resque-pool)
4
+ [![Build Status](https://travis-ci.org/nevans/resque-pool.png)](https://travis-ci.org/nevans/resque-pool)
5
5
  [![Dependency Status](https://gemnasium.com/nevans/resque-pool.png)](https://gemnasium.com/nevans/resque-pool)
6
6
 
7
7
  Resque pool is a simple library for managing a pool of
8
- [resque](http://github.com/defunkt/resque) workers. Given a a config file, it
8
+ [resque](https://github.com/defunkt/resque) workers. Given a a config file, it
9
9
  manages your workers for you, starting up the appropriate number of workers for
10
10
  each worker type.
11
11
 
@@ -40,12 +40,14 @@ environment specific overrides (`RACK_ENV`, `RAILS_ENV`, and `RESQUE_ENV`
40
40
  environment variables can be used to determine environment). For example in
41
41
  `config/resque-pool.yml`:
42
42
 
43
- foo: 1
44
- bar: 2
45
- "foo,bar,baz": 1
43
+ ```Yaml
44
+ foo: 1
45
+ bar: 2
46
+ "foo,bar,baz": 1
46
47
 
47
- production:
48
- "foo,bar,baz": 4
48
+ production:
49
+ "foo,bar,baz": 4
50
+ ```
49
51
 
50
52
  ### Rake task config
51
53
 
@@ -55,20 +57,24 @@ application environment, configure Resque as necessary, and configure
55
57
  manager and reconnect in the workers. For example, with rails you should put
56
58
  the following into `lib/tasks/resque.rake`:
57
59
 
58
- require 'resque/pool/tasks'
59
- # this task will get called before resque:pool:setup
60
- # and preload the rails environment in the pool manager
61
- task "resque:setup" => :environment do
62
- # generic worker setup, e.g. Hoptoad for failed jobs
63
- end
64
- task "resque:pool:setup" do
65
- # close any sockets or files in pool manager
66
- ActiveRecord::Base.connection.disconnect!
67
- # and re-open them in the resque worker parent
68
- Resque::Pool.after_prefork do |job|
69
- ActiveRecord::Base.establish_connection
70
- end
71
- end
60
+ ```Ruby
61
+ require 'resque/pool/tasks'
62
+
63
+ # this task will get called before resque:pool:setup
64
+ # and preload the rails environment in the pool manager
65
+ task "resque:setup" => :environment do
66
+ # generic worker setup, e.g. Hoptoad for failed jobs
67
+ end
68
+
69
+ task "resque:pool:setup" do
70
+ # close any sockets or files in pool manager
71
+ ActiveRecord::Base.connection.disconnect!
72
+ # and re-open them in the resque worker parent
73
+ Resque::Pool.after_prefork do |job|
74
+ ActiveRecord::Base.establish_connection
75
+ end
76
+ end
77
+ ```
72
78
 
73
79
 
74
80
  For normal work with fresh resque and resque-scheduler gems add next lines in lib/rake/resque.rake
@@ -124,7 +130,7 @@ SIGNALS
124
130
 
125
131
  The pool manager responds to the following signals:
126
132
 
127
- * `HUP` - reload the config file, reload logfiles, restart all workers.
133
+ * `HUP` - reset config loader (reload the config file), reload logfiles, restart all workers.
128
134
  * `QUIT` - gracefully shut down workers (via `QUIT`) and shutdown the manager
129
135
  after all workers are done.
130
136
  * `INT` - gracefully shut down workers (via `QUIT`) and immediately shutdown manager
@@ -143,6 +149,37 @@ defined by Resque 1.22 and above. See http://hone.heroku.com/resque/2012/08/21/r
143
149
  for details, overriding any command-line configuration for `TERM`. Setting `TERM_CHILD` tells
144
150
  us you know what you're doing.
145
151
 
152
+ Custom Configuration Loader
153
+ ---------------------------
154
+
155
+ If the static YAML file configuration approach does not meet your needs, you can
156
+ specify a custom configuration loader.
157
+
158
+ Set the `config_loader` class variable on Resque::Pool to an object that
159
+ responds to `#call` (which can simply be a lambda/Proc). The class attribute
160
+ needs to be set before starting the pool. This is usually accomplished
161
+ in the `resque:pool:setup` rake task, as described above.
162
+
163
+ For example, if you wanted to vary the number of worker processes based on a
164
+ value stored in Redis, you could do something like:
165
+
166
+ ```ruby
167
+ task resque:pool:setup do
168
+ Resque::Pool.config_loader = lambda do |env|
169
+ worker_count = Redis.current.get("pool_workers_#{env}").to_i
170
+ {"queueA,queueB" => worker_count }
171
+ end
172
+ end
173
+ ```
174
+
175
+ The configuration loader's `#call` method will be invoked about once a second.
176
+ This allows the configuration to constantly change, allowing you to scale the
177
+ number of workers up or down based on different conditions.
178
+ If the response is generally static, the loader may want to cache the value it
179
+ returns. It can optionally implement a `#reset!` method, which will be invoked
180
+ when the HUP signal is received, allowing the loader to flush its cache, or
181
+ perform any other re-initialization.
182
+
146
183
  Other Features
147
184
  --------------
148
185
 
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
- require 'bundler'
2
- Bundler::GemHelper.install_tasks
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
3
 
4
4
  # for loading the example config file in config/resque-pool.yml
5
5
  require 'resque/pool/tasks'
data/lib/resque/pool.rb CHANGED
@@ -4,6 +4,7 @@ require 'resque/worker'
4
4
  require 'resque/pool/version'
5
5
  require 'resque/pool/logging'
6
6
  require 'resque/pool/pooled_worker'
7
+ require 'resque/pool/file_or_hash_loader'
7
8
  require 'erb'
8
9
  require 'fcntl'
9
10
  require 'yaml'
@@ -18,10 +19,11 @@ module Resque
18
19
  include Logging
19
20
  extend Logging
20
21
  attr_reader :config
22
+ attr_reader :config_loader
21
23
  attr_reader :workers
22
24
 
23
- def initialize(config)
24
- init_config(config)
25
+ def initialize(config_loader=nil)
26
+ init_config(config_loader)
25
27
  @workers = Hash.new { |workers, queues| workers[queues] = {} }
26
28
  procline "(initialized)"
27
29
  end
@@ -31,7 +33,7 @@ module Resque
31
33
  # The `after_prefork` hooks will be run in workers if you are using the
32
34
  # preforking master worker to save memory. Use these hooks to reload
33
35
  # database connections and so forth to ensure that they're not shared
34
- # among workers.
36
+ # among workers. The worker instance is passed as an argument to the block.
35
37
  #
36
38
  # Call with a block to set a hook.
37
39
  # Call with no arguments to return all registered hooks.
@@ -49,17 +51,16 @@ module Resque
49
51
  @after_prefork = [after_prefork]
50
52
  end
51
53
 
52
- def call_after_prefork!
54
+ def call_after_prefork!(worker)
53
55
  self.class.after_prefork.each do |hook|
54
- hook.call
56
+ hook.call(worker)
55
57
  end
56
58
  end
57
59
 
58
60
  # }}}
59
- # Config: class methods to start up the pool using the default config {{{
61
+ # Config: class methods to start up the pool using the config loader {{{
60
62
 
61
- @config_files = ["resque-pool.yml", "config/resque-pool.yml"]
62
- class << self; attr_accessor :config_files, :app_name, :spawn_delay; end
63
+ class << self; attr_accessor :config_loader, :app_name, :spawn_delay; end
63
64
 
64
65
  def self.app_name
65
66
  @app_name ||= File.basename(Dir.pwd)
@@ -81,46 +82,37 @@ module Resque
81
82
  )
82
83
  end
83
84
 
84
- def self.choose_config_file
85
- if ENV["RESQUE_POOL_CONFIG"]
86
- ENV["RESQUE_POOL_CONFIG"]
87
- else
88
- @config_files.detect { |f| File.exist?(f) }
89
- end
90
- end
91
-
92
85
  def self.run
93
86
  if GC.respond_to?(:copy_on_write_friendly=)
94
87
  GC.copy_on_write_friendly = true
95
88
  end
96
- Resque::Pool.new(choose_config_file).start.join
89
+ create_configured.start.join
97
90
  end
98
91
 
99
- # }}}
100
- # Config: load config and config file {{{
101
-
102
- def config_file
103
- @config_file || (!@config && ::Resque::Pool.choose_config_file)
92
+ def self.create_configured
93
+ Resque::Pool.new(config_loader)
104
94
  end
105
95
 
106
- def init_config(config)
107
- case config
108
- when String, nil
109
- @config_file = config
96
+ # }}}
97
+ # Config: store loader and load config {{{
98
+
99
+ def init_config(loader)
100
+ case loader
101
+ when String, Hash, nil
102
+ @config_loader = FileOrHashLoader.new(loader)
110
103
  else
111
- @config = config.dup
104
+ @config_loader = loader
112
105
  end
113
106
  load_config
114
107
  end
115
108
 
116
109
  def load_config
117
- if config_file
118
- @config = YAML.load(ERB.new(IO.read(config_file)).result)
119
- else
120
- @config ||= {}
121
- end
122
- environment and @config[environment] and config.merge!(@config[environment])
123
- config.delete_if {|key, value| value.is_a? Hash }
110
+ @config = config_loader.call(environment)
111
+ end
112
+
113
+ def reset_config
114
+ config_loader.reset! if config_loader.respond_to?(:reset!)
115
+ load_config
124
116
  end
125
117
 
126
118
  def environment
@@ -189,8 +181,8 @@ module Resque
189
181
  log "#{signal}: sending to all workers"
190
182
  signal_all_workers(signal)
191
183
  when :HUP
192
- log "HUP: reload config file and reload logfiles"
193
- load_config
184
+ log "HUP: reset configuration and reload logfiles"
185
+ reset_config
194
186
  Logging.reopen_logs!
195
187
  log "HUP: gracefully shutdown old children (which have old logfiles open)"
196
188
  if term_child
@@ -289,6 +281,7 @@ module Resque
289
281
  break if handle_sig_queue! == :break
290
282
  if sig_queue.empty?
291
283
  master_sleep
284
+ load_config
292
285
  maintain_worker_count
293
286
  end
294
287
  procline("managing #{all_pids.inspect}")
@@ -397,7 +390,7 @@ module Resque
397
390
  Process.setpgrp unless Resque::Pool.single_process_group
398
391
  worker.worker_parent_pid = Process.pid
399
392
  log_worker "Starting worker #{worker}"
400
- call_after_prefork!
393
+ call_after_prefork!(worker)
401
394
  reset_sig_handlers!
402
395
  #self_pipe.each {|io| io.close }
403
396
  worker.work(ENV['INTERVAL'] || DEFAULT_WORKER_INTERVAL) # interval, will block
@@ -1,4 +1,4 @@
1
- require 'trollop'
1
+ require 'optparse'
2
2
  require 'resque/pool'
3
3
  require 'resque/pool/logging'
4
4
  require 'fileutils'
@@ -21,32 +21,35 @@ module Resque
21
21
  end
22
22
 
23
23
  def parse_options
24
- opts = Trollop::options do
25
- version "resque-pool #{VERSION} (c) nicholas a. evans"
26
- banner <<-EOS
27
- resque-pool is the best way to manage a group (pool) of resque workers
24
+ opts = {}
25
+ OptionParser.new do |opt|
26
+ opt.banner = <<-EOS.gsub(/^ /, '')
27
+ resque-pool is the best way to manage a group (pool) of resque workers
28
28
 
29
- When daemonized, stdout and stderr default to resque-pool.stdxxx.log files in
30
- the log directory and pidfile defaults to resque-pool.pid in the current dir.
29
+ When daemonized, stdout and stderr default to resque-pool.stdxxx.log files in
30
+ the log directory and pidfile defaults to resque-pool.pid in the current dir.
31
31
 
32
- Usage:
33
- resque-pool [options]
34
- where [options] are:
32
+ Usage:
33
+ resque-pool [options]
34
+
35
+ where [options] are:
35
36
  EOS
36
- opt :config, "Alternate path to config file", :type => String, :short => "-c"
37
- opt :appname, "Alternate appname", :type => String, :short => "-a"
38
- opt :daemon, "Run as a background daemon", :default => false, :short => "-d"
39
- opt :stdout, "Redirect stdout to logfile", :type => String, :short => '-o'
40
- opt :stderr, "Redirect stderr to logfile", :type => String, :short => '-e'
41
- opt :nosync, "Don't sync logfiles on every write"
42
- opt :pidfile, "PID file location", :type => String, :short => "-p"
43
- opt :environment, "Set RAILS_ENV/RACK_ENV/RESQUE_ENV", :type => String, :short => "-E"
44
- opt :spawn_delay, "Delay in milliseconds between spawning missing workers", :type => Integer, :short => "-s"
45
- opt :term_graceful_wait, "On TERM signal, wait for workers to shut down gracefully"
46
- opt :term_graceful, "On TERM signal, shut down workers gracefully"
47
- opt :term_immediate, "On TERM signal, shut down workers immediately (default)"
48
- opt :single_process_group, "Workers remain in the same process group as the master", :default => false
49
- end
37
+ opt.on('-c', '--config PATH', "Alternate path to config file") { |c| opts[:config] = c }
38
+ opt.on('-a', '--appname NAME', "Alternate appname") { |c| opts[:appname] = c }
39
+ opt.on("-d", '--daemon', "Run as a background daemon") { opts[:daemon] = true }
40
+ opt.on('-o', '--stdout FILE', "Redirect stdout to logfile") { |c| opts[:stdout] = c }
41
+ opt.on('-e', '--stderr FILE', "Redirect stderr to logfile") { |c| opts[:stderr] = c }
42
+ opt.on('--nosync', "Don't sync logfiles on every write") { opts[:nosync] = true }
43
+ opt.on("-p", '--pidfile FILE', "PID file location") { |c| opts[:pidfile] = c }
44
+ opt.on("-E", '--environment ENVIRONMENT', "Set RAILS_ENV/RACK_ENV/RESQUE_ENV") { |c| opts[:environment] = c }
45
+ opt.on("-s", '--spawn-delay MS', Integer, "Delay in milliseconds between spawning missing workers") { |c| opts[:spawn_delay] = c }
46
+ opt.on('--term-graceful-wait', "On TERM signal, wait for workers to shut down gracefully") { opts[:term_graceful_wait] = true }
47
+ opt.on('--term-graceful', "On TERM signal, shut down workers gracefully") { opts[:term_graceful] = true }
48
+ opt.on('--term-immediate', "On TERM signal, shut down workers immediately (default)") { opts[:term_immediate] = true }
49
+ opt.on('--single-process-group', "Workers remain in the same process group as the master") { opts[:single_process_group] = true }
50
+ opt.on("-h", "--help", "Show this.") { puts opt; exit }
51
+ opt.on("-v", "--version", "Show Version"){ puts "resque-pool #{VERSION} (c) nicholas a. evans"; exit}
52
+ end.parse!
50
53
  if opts[:daemon]
51
54
  opts[:stdout] ||= "log/resque-pool.stdout.log"
52
55
  opts[:stderr] ||= "log/resque-pool.stderr.log"
@@ -0,0 +1,55 @@
1
+ class FileOrHashLoader
2
+ def initialize(filename_or_hash=nil)
3
+ case filename_or_hash
4
+ when String, nil
5
+ @filename = filename_or_hash
6
+ when Hash
7
+ @static_config = filename_or_hash.dup
8
+ else
9
+ raise "#{self.class} cannot be initialized with #{filename_or_hash.inspect}"
10
+ end
11
+ end
12
+
13
+ def call(environment)
14
+ @config ||= load_config_from_file(environment)
15
+ end
16
+
17
+ def reset!
18
+ @config = nil
19
+ end
20
+
21
+ private
22
+
23
+ def load_config_from_file(environment)
24
+ if @static_config
25
+ new_config = @static_config
26
+ else
27
+ filename = config_filename
28
+ new_config = load_config filename
29
+ end
30
+ apply_environment new_config, environment
31
+ end
32
+
33
+ def apply_environment(config, environment)
34
+ environment and config[environment] and config.merge!(config[environment])
35
+ config.delete_if {|key, value| value.is_a? Hash }
36
+ end
37
+
38
+ def config_filename
39
+ @filename || choose_config_file
40
+ end
41
+
42
+ def load_config(filename)
43
+ return {} unless filename
44
+ YAML.load(ERB.new(IO.read(filename)).result)
45
+ end
46
+
47
+ CONFIG_FILES = ["resque-pool.yml", "config/resque-pool.yml"]
48
+ def choose_config_file
49
+ if ENV["RESQUE_POOL_CONFIG"]
50
+ ENV["RESQUE_POOL_CONFIG"]
51
+ else
52
+ CONFIG_FILES.detect { |f| File.exist?(f) }
53
+ end
54
+ end
55
+ end