resque-pool 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.travis.yml +13 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +23 -0
- data/Changelog.md +10 -1
- data/Gemfile +3 -0
- data/Gemfile.lock +102 -0
- data/LICENSE.txt +3 -2
- data/README.md +74 -23
- data/Rakefile +3 -6
- data/config/alternate.yml +4 -0
- data/config/cucumber.yml +2 -0
- data/examples/Gemfile +2 -0
- data/examples/Gemfile.lock +80 -0
- data/examples/Rakefile +8 -0
- data/examples/chef_cookbook/recipes/default.rb +46 -0
- data/examples/chef_cookbook/templates/default/initd.erb +69 -0
- data/examples/chef_cookbook/templates/default/monitrc.erb +6 -0
- data/examples/rails-resque.rake +22 -0
- data/examples/resque-pool.god +43 -0
- data/examples/resque-pool.yml +4 -0
- data/examples/upstart-reload.conf +24 -0
- data/examples/upstart.conf +44 -0
- data/{bin → exe}/resque-pool +0 -0
- data/lib/resque/pool.rb +49 -26
- data/lib/resque/pool/cli.rb +48 -11
- data/lib/resque/pool/config_loaders/file_or_hash_loader.rb +63 -0
- data/lib/resque/pool/config_loaders/throttled.rb +44 -0
- data/lib/resque/pool/killer.rb +40 -0
- data/lib/resque/pool/logging.rb +3 -1
- data/lib/resque/pool/version.rb +1 -1
- data/resque-pool.gemspec +42 -0
- metadata +95 -42
- data/lib/resque/pool/file_or_hash_loader.rb +0 -55
data/config/cucumber.yml
ADDED
data/examples/Gemfile
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
PATH
|
2
|
+
remote: /home/nick/src/active/resque-pool
|
3
|
+
specs:
|
4
|
+
resque-pool (0.3.0.dev)
|
5
|
+
rake
|
6
|
+
resque (~> 1.20)
|
7
|
+
trollop (~> 1.16)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
SystemTimer (1.2.3)
|
13
|
+
aruba (0.4.11)
|
14
|
+
childprocess (>= 0.2.3)
|
15
|
+
cucumber (>= 1.1.1)
|
16
|
+
ffi (>= 1.0.11)
|
17
|
+
rspec (>= 2.7.0)
|
18
|
+
builder (3.0.0)
|
19
|
+
childprocess (0.3.2)
|
20
|
+
ffi (~> 1.0.6)
|
21
|
+
cucumber (1.1.9)
|
22
|
+
builder (>= 2.1.2)
|
23
|
+
diff-lcs (>= 1.1.2)
|
24
|
+
gherkin (~> 2.9.0)
|
25
|
+
json (>= 1.4.6)
|
26
|
+
term-ansicolor (>= 1.0.6)
|
27
|
+
diff-lcs (1.1.3)
|
28
|
+
ffi (1.0.11)
|
29
|
+
gherkin (2.9.3)
|
30
|
+
json (>= 1.4.6)
|
31
|
+
hpricot (0.8.6)
|
32
|
+
json (1.6.6)
|
33
|
+
multi_json (1.3.2)
|
34
|
+
mustache (0.99.4)
|
35
|
+
rack (1.4.1)
|
36
|
+
rack-protection (1.2.0)
|
37
|
+
rack
|
38
|
+
rake (0.9.2.2)
|
39
|
+
rdiscount (1.6.8)
|
40
|
+
redis (2.2.2)
|
41
|
+
redis-namespace (1.0.3)
|
42
|
+
redis (< 3.0.0)
|
43
|
+
resque (1.20.0)
|
44
|
+
multi_json (~> 1.0)
|
45
|
+
redis-namespace (~> 1.0.2)
|
46
|
+
sinatra (>= 0.9.2)
|
47
|
+
vegas (~> 0.1.2)
|
48
|
+
ronn (0.7.3)
|
49
|
+
hpricot (>= 0.8.2)
|
50
|
+
mustache (>= 0.7.0)
|
51
|
+
rdiscount (>= 1.5.8)
|
52
|
+
rspec (2.9.0)
|
53
|
+
rspec-core (~> 2.9.0)
|
54
|
+
rspec-expectations (~> 2.9.0)
|
55
|
+
rspec-mocks (~> 2.9.0)
|
56
|
+
rspec-core (2.9.0)
|
57
|
+
rspec-expectations (2.9.1)
|
58
|
+
diff-lcs (~> 1.1.3)
|
59
|
+
rspec-mocks (2.9.0)
|
60
|
+
sinatra (1.3.2)
|
61
|
+
rack (~> 1.3, >= 1.3.6)
|
62
|
+
rack-protection (~> 1.2)
|
63
|
+
tilt (~> 1.3, >= 1.3.3)
|
64
|
+
term-ansicolor (1.0.7)
|
65
|
+
tilt (1.3.3)
|
66
|
+
trollop (1.16.2)
|
67
|
+
vegas (0.1.11)
|
68
|
+
rack (>= 1.0.0)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
SystemTimer
|
75
|
+
aruba (~> 0.4.11)
|
76
|
+
bundler (~> 1.0)
|
77
|
+
cucumber (~> 1.1.9)
|
78
|
+
resque-pool!
|
79
|
+
ronn
|
80
|
+
rspec (~> 2.9.0)
|
data/examples/Rakefile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# If you have redis running on the standard port on localhost, then you can
|
2
|
+
# test out resque-pool in the examples directory by running:
|
3
|
+
#
|
4
|
+
# rake --trace resque:pool
|
5
|
+
#
|
6
|
+
# And then you can poke and prod the various processes with signals and edit
|
7
|
+
# the config file, etc.
|
8
|
+
require 'resque/pool/tasks'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
roles = %w[solo util]
|
2
|
+
if roles.include?(node[:instance_role])
|
3
|
+
node[:applications].each do |app, data|
|
4
|
+
|
5
|
+
pidfile = "/data/#{app}/current/tmp/pids/#{app}_resque.pid"
|
6
|
+
|
7
|
+
template "/etc/monit.d/#{app}_resque.monitrc" do
|
8
|
+
owner 'root'
|
9
|
+
group 'root'
|
10
|
+
mode 0644
|
11
|
+
source "monitrc.erb"
|
12
|
+
variables({
|
13
|
+
:app_name => app,
|
14
|
+
:pidfile => pidfile,
|
15
|
+
#:max_mem => "400 MB",
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
template "/etc/init.d/#{app}_resque" do
|
20
|
+
owner 'root'
|
21
|
+
group 'root'
|
22
|
+
mode 0744
|
23
|
+
source "initd.erb"
|
24
|
+
variables({
|
25
|
+
:app_name => app,
|
26
|
+
:pidfile => pidfile,
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
execute "enable-resque" do
|
31
|
+
command "rc-update add #{app}_resque default"
|
32
|
+
action :run
|
33
|
+
not_if "rc-update show | grep -q '^ *#{app}_resque |.*default'"
|
34
|
+
end
|
35
|
+
|
36
|
+
execute "start-resque" do
|
37
|
+
command %Q{/etc/init.d/#{app}_resque start}
|
38
|
+
creates pidfile
|
39
|
+
end
|
40
|
+
|
41
|
+
execute "ensure-resque-is-setup-with-monit" do
|
42
|
+
command %Q{monit reload}
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/bin/sh -e
|
2
|
+
# Copyright (C) nicholas a. evans <nick@ekenosen.net>
|
3
|
+
# See LICENSE.txt
|
4
|
+
|
5
|
+
### BEGIN INIT INFO
|
6
|
+
# Provides: resque-pool-<%= @app_name %>
|
7
|
+
# Required-Start: $local_fs $remote_fs $network
|
8
|
+
# Required-Stop: $local_fs $remote_fs $network
|
9
|
+
# Default-Start: 2 3 4 5
|
10
|
+
# Default-Stop: 0 1 6
|
11
|
+
# Short-Description: resque-pool init script for <%= @app_name %>
|
12
|
+
# Description: resque-pool manages the resque workers
|
13
|
+
### END INIT INFO
|
14
|
+
|
15
|
+
# configure these values here or in /etc/default/resque-pool
|
16
|
+
# make sure your pidfile here matches your monitrc
|
17
|
+
app_name="<%= @app_name %>"
|
18
|
+
pidfile="<%= @pidfile %>"
|
19
|
+
app_dir="/data/${app_name}/current"
|
20
|
+
run_as_user="deploy"
|
21
|
+
sleep_time_during_restart=5
|
22
|
+
stop_schedule="QUIT/30/INT/10/KILL/5"
|
23
|
+
bundler="/usr/bin/bundle"
|
24
|
+
environment="production"
|
25
|
+
stdout_log="${app_dir}/log/resque-pool-${app_name}.stdout.log"
|
26
|
+
stderr_log="${app_dir}/log/resque-pool-${app_name}.stderr.log"
|
27
|
+
|
28
|
+
# override above values, and set any other env variables you need
|
29
|
+
if [ -f /etc/default/resque ] ; then
|
30
|
+
. /etc/default/resque
|
31
|
+
fi
|
32
|
+
if [ -f /etc/default/${app_name}_resque ] ; then
|
33
|
+
. /etc/default/${app_name}_resque
|
34
|
+
fi
|
35
|
+
|
36
|
+
case "$1" in
|
37
|
+
start)
|
38
|
+
# I'm assuming that you are using bundler. If you are using rip or
|
39
|
+
# something else, you'll need to change this. Remember to
|
40
|
+
# keep the double-dash; e.g.: --startas CMD -- ARGS
|
41
|
+
start-stop-daemon --start --pidfile ${pidfile} \
|
42
|
+
--chuid ${run_as_user} --chdir ${app_dir} \
|
43
|
+
--startas ${bundler} -- exec \
|
44
|
+
resque-pool -d -a ${app_name} -p ${pidfile} -E ${environment} -o ${stdout_log} -e ${stderr_log}
|
45
|
+
;;
|
46
|
+
reload)
|
47
|
+
start-stop-daemon --stop --pidfile ${pidfile} --signal HUP
|
48
|
+
;;
|
49
|
+
graceful-stop)
|
50
|
+
start-stop-daemon --stop --pidfile ${pidfile} --signal QUIT
|
51
|
+
;;
|
52
|
+
quick-stop)
|
53
|
+
start-stop-daemon --stop --pidfile ${pidfile} --signal INT
|
54
|
+
;;
|
55
|
+
stop)
|
56
|
+
start-stop-daemon --stop --pidfile ${pidfile} --retry=${stop_schedule}
|
57
|
+
;;
|
58
|
+
restart)
|
59
|
+
$0 stop
|
60
|
+
sleep ${sleep_time_during_restart}
|
61
|
+
$0 start
|
62
|
+
;;
|
63
|
+
*)
|
64
|
+
echo "Usage: $0 {start|stop|graceful-stop|quick-stop|restart|reload}"
|
65
|
+
exit 1
|
66
|
+
;;
|
67
|
+
esac
|
68
|
+
|
69
|
+
# vim:ft=sh
|
@@ -0,0 +1,6 @@
|
|
1
|
+
check process <%= @app_name %>_resque
|
2
|
+
with pidfile <%= @pidfile %>
|
3
|
+
start program = "/etc/init.d/<%= @app_name %>_resque start" with timeout 90 seconds
|
4
|
+
stop program = "/etc/init.d/<%= @app_name %>_resque stop" with timeout 90 seconds
|
5
|
+
#if totalmem is greater than <%= @max_mem || "300 MB" %> for 10 cycles then restart # eating up memory?
|
6
|
+
group resque
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'resque/pool/tasks'
|
2
|
+
|
3
|
+
# this task will get called before resque:pool:setup
|
4
|
+
# preload the rails environment in the pool master
|
5
|
+
task "resque:setup" => :environment do
|
6
|
+
# generic worker setup, e.g. Hoptoad for failed jobs
|
7
|
+
end
|
8
|
+
|
9
|
+
task "resque:pool:setup" do
|
10
|
+
# close any sockets or files in pool master
|
11
|
+
ActiveRecord::Base.connection.disconnect!
|
12
|
+
|
13
|
+
# and re-open them in the resque worker parent
|
14
|
+
Resque::Pool.after_prefork do |job|
|
15
|
+
ActiveRecord::Base.establish_connection
|
16
|
+
end
|
17
|
+
|
18
|
+
# you could also re-open them in the resque worker child, using
|
19
|
+
# Resque.after_fork, but that probably isn't necessary, and
|
20
|
+
# Resque::Pool.after_prefork should be faster, since it won't run
|
21
|
+
# for every single job.
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
rails_env = ENV['RAILS_ENV'] || 'production'
|
2
|
+
rails_root = ENV['RAILS_ROOT'] || "YOUR-APP-PATH"
|
3
|
+
|
4
|
+
God.watch do |w|
|
5
|
+
w.dir = "#{rails_root}"
|
6
|
+
w.name = "resque-pool"
|
7
|
+
w.group = 'resque'
|
8
|
+
w.interval = 30.seconds
|
9
|
+
w.env = { "RAILS_ENV" => rails_env }
|
10
|
+
w.start = "bundle exec resque-pool -d -o #{rails_root}/log/resque-pool.stdout -e #{rails_root}/log/resque-pool.stderr -p #{rails_root}/tmp/pids/resque-pool.pid"
|
11
|
+
|
12
|
+
w.pid_file = "#{rails_root}/tmp/pids/resque-pool.pid"
|
13
|
+
w.behavior(:clean_pid_file)
|
14
|
+
|
15
|
+
# determine the state on startup
|
16
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
17
|
+
on.condition(:process_running) do |c|
|
18
|
+
c.running = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# determine when process has finished starting
|
23
|
+
w.transition([:start, :restart], :up) do |on|
|
24
|
+
on.condition(:process_running) do |c|
|
25
|
+
c.running = true
|
26
|
+
c.interval = 5.seconds
|
27
|
+
end
|
28
|
+
|
29
|
+
# failsafe
|
30
|
+
on.condition(:tries) do |c|
|
31
|
+
c.times = 5
|
32
|
+
c.transition = :start
|
33
|
+
c.interval = 5.seconds
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# start if process is not running
|
38
|
+
w.transition(:up, :start) do |on|
|
39
|
+
on.condition(:process_running) do |c|
|
40
|
+
c.running = false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Seamless reload of resque-pool after a deploy.
|
2
|
+
# Assuming resque-pool is already running, invoke with
|
3
|
+
# `sudo initctl emit resque-pool-reload`.
|
4
|
+
description "resque-pool-reload"
|
5
|
+
|
6
|
+
start on resque-pool-reload
|
7
|
+
|
8
|
+
task
|
9
|
+
|
10
|
+
limit nofile 65536 65536
|
11
|
+
|
12
|
+
# You may need to set things like PATH here.
|
13
|
+
env HOME=/home/your_app_user
|
14
|
+
export HOME
|
15
|
+
|
16
|
+
script
|
17
|
+
source /home/your_app_user/app_env.sh
|
18
|
+
cd /your/app_root
|
19
|
+
/usr/local/bin/setuidgid your_app_user bundle exec resque-pool \
|
20
|
+
--daemon --hot-swap
|
21
|
+
# Optionally add:
|
22
|
+
# --lock /path/to/your/lock_file
|
23
|
+
# --config /path/to/resque_pool_config.yml
|
24
|
+
end script
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Manages resque-pool.
|
2
|
+
# Start the process with `initctl start resque-pool`.
|
3
|
+
description "resque-pool"
|
4
|
+
|
5
|
+
start on virtual-filesystems
|
6
|
+
stop on runlevel [06]
|
7
|
+
|
8
|
+
respawn
|
9
|
+
kill timeout 30
|
10
|
+
limit nofile 65536 65536
|
11
|
+
|
12
|
+
# You may need to set things like PATH here.
|
13
|
+
env HOME=/home/your_app_user
|
14
|
+
export HOME
|
15
|
+
|
16
|
+
# Ensure no subsequently deployed instances are running after shutdown.
|
17
|
+
post-stop script
|
18
|
+
/usr/bin/pkill -INT -f resque-pool-master
|
19
|
+
end script
|
20
|
+
|
21
|
+
# This script assumes you will deploy new application code by using
|
22
|
+
# something similar to upstart-reload.conf which runs a second copy of
|
23
|
+
# the application then shuts down the first when ready to fork. The
|
24
|
+
# --lock argument should point to a file that is accessible across
|
25
|
+
# deploys (i.e. not under a capistrano versioned path). setuidgid is
|
26
|
+
# only necessary if you are running an older version of upstart such as
|
27
|
+
# the one included with RHEL/Centos 6. In Upstart 1.4 and above you can
|
28
|
+
# use the setuid and setgid directives instead.
|
29
|
+
script
|
30
|
+
# Assuming you use environment-based configuration.
|
31
|
+
source /home/your_app_user/app_env.sh
|
32
|
+
cd /your/app_root
|
33
|
+
/usr/local/bin/setuidgid your_app_user bundle exec resque-pool \
|
34
|
+
--daemon --hot-swap
|
35
|
+
# Optionally add:
|
36
|
+
# --lock /path/to/your/lock_file \ # default: tmp/resque-pool.lock
|
37
|
+
# --config /path/to/resque_pool_config.yml # default: config/resque-pool.yml
|
38
|
+
|
39
|
+
# The above daemon will shutdown if another resque-pool is started using the
|
40
|
+
# --kill-others or --hot-swap. This line will block until all pool instances
|
41
|
+
# are terminated, ensuring that upstart doesn't try to restart our process
|
42
|
+
# unless it is actually dead.
|
43
|
+
flock -x 0 < /path/to/your/lock_file
|
44
|
+
end script
|
data/{bin → exe}/resque-pool
RENAMED
File without changes
|
data/lib/resque/pool.rb
CHANGED
@@ -4,7 +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
|
+
require 'resque/pool/config_loaders/file_or_hash_loader'
|
8
8
|
require 'erb'
|
9
9
|
require 'fcntl'
|
10
10
|
require 'yaml'
|
@@ -28,34 +28,49 @@ module Resque
|
|
28
28
|
procline "(initialized)"
|
29
29
|
end
|
30
30
|
|
31
|
-
# Config:
|
31
|
+
# Config: hooks {{{
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
def self.hook(name) # :nodoc:
|
34
|
+
class_eval <<-CODE
|
35
|
+
def self.#{name}(&block)
|
36
|
+
@#{name} ||= []
|
37
|
+
block ? (@#{name} << block) : @#{name}
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.#{name}=(block)
|
41
|
+
@#{name} = [block]
|
42
|
+
end
|
43
|
+
|
44
|
+
def call_#{name}!(*args)
|
45
|
+
self.class.#{name}.each do |hook|
|
46
|
+
hook.call(*args)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
CODE
|
44
50
|
end
|
45
51
|
|
46
|
-
|
47
|
-
#
|
48
|
-
#
|
52
|
+
##
|
53
|
+
# :call-seq:
|
54
|
+
# after_prefork do |worker| ... end => add a hook
|
55
|
+
# after_prefork << hook => add a hook
|
49
56
|
#
|
50
|
-
|
51
|
-
|
52
|
-
|
57
|
+
# +after_prefork+ will run in workers before any jobs. Use these hooks e.g.
|
58
|
+
# to reload database connections to ensure that they're not shared among
|
59
|
+
# workers.
|
60
|
+
#
|
61
|
+
# :yields: worker
|
62
|
+
hook :after_prefork
|
53
63
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
64
|
+
##
|
65
|
+
# :call-seq:
|
66
|
+
# after_prefork do |worker, pid, workers| ... end => add a hook
|
67
|
+
# after_prefork << hook => add a hook
|
68
|
+
#
|
69
|
+
# The `after_spawn` hooks will run in the master after spawning a new
|
70
|
+
# worker.
|
71
|
+
#
|
72
|
+
# :yields: worker, pid, workers
|
73
|
+
hook :after_spawn
|
59
74
|
|
60
75
|
# }}}
|
61
76
|
# Config: class methods to start up the pool using the config loader {{{
|
@@ -73,6 +88,11 @@ module Resque
|
|
73
88
|
@handle_winch = bool
|
74
89
|
end
|
75
90
|
|
91
|
+
def self.kill_other_pools!
|
92
|
+
require 'resque/pool/killer'
|
93
|
+
Resque::Pool::Killer.run
|
94
|
+
end
|
95
|
+
|
76
96
|
def self.single_process_group=(bool)
|
77
97
|
ENV["RESQUE_SINGLE_PGRP"] = !!bool ? "YES" : "NO"
|
78
98
|
end
|
@@ -99,7 +119,7 @@ module Resque
|
|
99
119
|
def init_config(loader)
|
100
120
|
case loader
|
101
121
|
when String, Hash, nil
|
102
|
-
@config_loader = FileOrHashLoader.new(loader)
|
122
|
+
@config_loader = ConfigLoaders::FileOrHashLoader.new(loader)
|
103
123
|
else
|
104
124
|
@config_loader = loader
|
105
125
|
end
|
@@ -220,6 +240,7 @@ module Resque
|
|
220
240
|
|
221
241
|
class << self
|
222
242
|
attr_accessor :term_behavior
|
243
|
+
attr_accessor :kill_other_pools
|
223
244
|
end
|
224
245
|
|
225
246
|
def graceful_worker_shutdown_and_wait!(signal)
|
@@ -264,6 +285,7 @@ module Resque
|
|
264
285
|
procline("(started)")
|
265
286
|
log "started manager"
|
266
287
|
report_worker_pool_pids
|
288
|
+
self.class.kill_other_pools! if self.class.kill_other_pools
|
267
289
|
self
|
268
290
|
end
|
269
291
|
|
@@ -396,13 +418,14 @@ module Resque
|
|
396
418
|
worker.work(ENV['INTERVAL'] || DEFAULT_WORKER_INTERVAL) # interval, will block
|
397
419
|
end
|
398
420
|
workers[queues][pid] = worker
|
421
|
+
call_after_spawn!(worker, pid, workers)
|
399
422
|
end
|
400
423
|
|
401
424
|
def create_worker(queues)
|
402
425
|
queues = queues.to_s.split(',')
|
403
426
|
worker = ::Resque::Worker.new(*queues)
|
404
427
|
worker.pool_master_pid = Process.pid
|
405
|
-
worker.term_timeout = ENV['RESQUE_TERM_TIMEOUT'] || 4.0
|
428
|
+
worker.term_timeout = (ENV['RESQUE_TERM_TIMEOUT'] || 4.0).to_f
|
406
429
|
worker.term_child = ENV['TERM_CHILD']
|
407
430
|
if worker.respond_to?(:run_at_exit_hooks=)
|
408
431
|
# resque doesn't support this until 1.24, but we support 1.22
|