Skip to content

Commit 8037d95

Browse files
committed
Merge pull request resque#672 from kjwierenga/feature/conditional_fork_hooks
Don't invoke :before_fork and :after_fork hooks when not forking - fixes fd leak when used with NewRelic
2 parents b91bdb2 + 1a48dd2 commit 8037d95

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

lib/resque/worker.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,9 @@ def work(interval = 5.0, &block)
136136
if not paused? and job = reserve
137137
log "got: #{job.inspect}"
138138
job.worker = self
139-
run_hook :before_fork, job
140139
working_on job
141140

142-
if @child = fork
141+
if @child = fork(job)
143142
srand # Reseeding
144143
procline "Forked #{@child} at #{Time.now.to_i}"
145144
begin
@@ -148,11 +147,11 @@ def work(interval = 5.0, &block)
148147
nil
149148
end
150149
else
151-
unregister_signal_handlers if !@cant_fork && term_child
150+
unregister_signal_handlers if will_fork? && term_child
152151
procline "Processing #{job.queue} since #{Time.now.to_i}"
153152
redis.client.reconnect # Don't share connection with parent
154153
perform(job, &block)
155-
exit! unless @cant_fork
154+
exit! if will_fork?
156155
end
157156

158157
done_working
@@ -184,7 +183,7 @@ def process(job = nil, &block)
184183
# Processes a given job in the child.
185184
def perform(job)
186185
begin
187-
run_hook :after_fork, job
186+
run_hook :after_fork, job unless @cant_fork
188187
job.perform
189188
rescue Object => e
190189
log "#{job.inspect} failed: #{e.inspect}"
@@ -228,15 +227,17 @@ def queues
228227

229228
# Not every platform supports fork. Here we do our magic to
230229
# determine if yours does.
231-
def fork
232-
@cant_fork = true if $TESTING
233-
230+
def fork(job)
234231
return if @cant_fork
232+
233+
# Only run before_fork hooks if we're actually going to fork
234+
# (after checking @cant_fork)
235+
run_hook :before_fork, job
235236

236237
begin
237238
# IronRuby doesn't support `Kernel.fork` yet
238239
if Kernel.respond_to?(:fork)
239-
Kernel.fork
240+
Kernel.fork if will_fork?
240241
else
241242
raise NotImplementedError
242243
end
@@ -507,6 +508,10 @@ def working?
507508
def idle?
508509
state == :idle
509510
end
511+
512+
def will_fork?
513+
!(@cant_fork || $TESTING)
514+
end
510515

511516
# Returns a symbol representing the current worker state,
512517
# which can be either :working or :idle

test/worker_test.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,19 @@ def self.perform
376376
workerA.work(0)
377377
assert $BEFORE_FORK_CALLED
378378
end
379+
380+
test "Will not call a before_fork hook when the worker can't fork" do
381+
Resque.redis.flushall
382+
$BEFORE_FORK_CALLED = false
383+
Resque.before_fork = Proc.new { $BEFORE_FORK_CALLED = true }
384+
workerA = Resque::Worker.new(:jobs)
385+
workerA.cant_fork = true
386+
387+
assert !$BEFORE_FORK_CALLED, "before_fork should not have been called before job runs"
388+
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
389+
workerA.work(0)
390+
assert !$BEFORE_FORK_CALLED, "before_fork should not have been called after job runs"
391+
end
379392

380393
test "very verbose works in the afternoon" do
381394
begin
@@ -408,6 +421,19 @@ def self.perform
408421
assert $AFTER_FORK_CALLED
409422
end
410423

424+
test "Will not call an after_fork hook when the worker can't fork" do
425+
Resque.redis.flushall
426+
$AFTER_FORK_CALLED = false
427+
Resque.after_fork = Proc.new { $AFTER_FORK_CALLED = true }
428+
workerA = Resque::Worker.new(:jobs)
429+
workerA.cant_fork = true
430+
431+
assert !$AFTER_FORK_CALLED
432+
Resque::Job.create(:jobs, SomeJob, 20, '/tmp')
433+
workerA.work(0)
434+
assert !$AFTER_FORK_CALLED
435+
end
436+
411437
test "returns PID of running process" do
412438
assert_equal @worker.to_s.split(":")[1].to_i, @worker.pid
413439
end

0 commit comments

Comments
 (0)