Skip to content

Commit 7e17826

Browse files
joshuay03nateberkopec
authored andcommitted
[Fix #3282] idle-timeout not waiting on all workers in cluster mode (#3283)
1 parent 437142e commit 7e17826

File tree

4 files changed

+43
-6
lines changed

4 files changed

+43
-6
lines changed

lib/puma/cluster.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ def all_workers_in_phase?
158158
@workers.all? { |w| w.phase == @phase }
159159
end
160160

161+
def all_workers_idle_timed_out?
162+
(@workers.map(&:pid) - idle_timed_out_worker_pids).empty?
163+
end
164+
161165
def check_workers
162166
return if @next_check >= Time.now
163167

@@ -344,6 +348,8 @@ def setup_signals
344348
def run
345349
@status = :run
346350

351+
@idle_workers = {}
352+
347353
output_header "cluster"
348354

349355
# This is aligned with the output from Runner, see Runner#output_header
@@ -417,6 +423,8 @@ def run
417423

418424
@master_read, @worker_write = read, @wakeup
419425

426+
@options[:worker_write] = @worker_write
427+
420428
@config.run_hooks(:before_fork, nil, @log_writer)
421429

422430
spawn_workers
@@ -432,6 +440,11 @@ def run
432440

433441
while @status == :run
434442
begin
443+
if all_workers_idle_timed_out?
444+
log "- All workers reached idle timeout"
445+
break
446+
end
447+
435448
if @phased_restart
436449
start_phased_restart
437450
@phased_restart = false
@@ -483,17 +496,23 @@ def run
483496
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
484497
booted = true
485498
end
499+
when "i"
500+
if @idle_workers[pid]
501+
@idle_workers.delete pid
502+
else
503+
@idle_workers[pid] = true
504+
end
486505
end
487506
else
488507
log "! Out-of-sync worker list, no #{pid} worker"
489508
end
490509
end
510+
491511
if in_phased_restart && workers_not_booted.zero?
492512
@events.fire_on_booted!
493513
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
494514
in_phased_restart = false
495515
end
496-
497516
rescue Interrupt
498517
@status = :stop
499518
end
@@ -581,5 +600,9 @@ def timeout_workers
581600
end
582601
end
583602
end
603+
604+
def idle_timed_out_worker_pids
605+
@idle_workers.keys
606+
end
584607
end
585608
end

lib/puma/cluster/worker.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ def run
146146
# Invoke any worker shutdown hooks so they can prevent the worker
147147
# exiting until any background operations are completed
148148
@config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)
149-
150-
Process.kill "SIGTERM", master if server.idle_timeout_reached
151149
ensure
152150
@worker_write << "t#{Process.pid}\n" rescue nil
153151
@worker_write.close

lib/puma/server.rb

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ class Server
3737
attr_reader :events
3838
attr_reader :min_threads, :max_threads # for #stats
3939
attr_reader :requests_count # @version 5.0.0
40-
attr_reader :idle_timeout_reached
4140

4241
# @todo the following may be deprecated in the future
4342
attr_reader :auto_trim_time, :early_hints, :first_data_timeout,
@@ -82,6 +81,8 @@ def initialize(app, events = nil, options = {})
8281
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
8382
end
8483

84+
@clustered = (@options.fetch :workers, 0) > 0
85+
@worker_write = @options[:worker_write]
8586
@log_writer = @options.fetch :log_writer, LogWriter.stdio
8687
@early_hints = @options[:early_hints]
8788
@first_data_timeout = @options[:first_data_timeout]
@@ -333,12 +334,24 @@ def handle_servers
333334
unless ios
334335
unless shutting_down?
335336
@idle_timeout_reached = true
336-
@status = :stop
337+
338+
if @clustered
339+
@worker_write << "i#{Process.pid}\n" rescue nil
340+
next
341+
else
342+
@log_writer.log "- Idle timeout reached"
343+
@status = :stop
344+
end
337345
end
338346

339347
break
340348
end
341349

350+
if @idle_timeout_reached && @clustered
351+
@idle_timeout_reached = false
352+
@worker_write << "i#{Process.pid}\n" rescue nil
353+
end
354+
342355
ios.first.each do |sock|
343356
if sock == check
344357
break if handle_check

test/test_integration_cluster.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,10 @@ def test_idle_timeout
226226

227227
get_worker_pids # wait for workers to boot
228228

229-
connect
229+
10.times {
230+
fast_connect
231+
sleep 0.5
232+
}
230233

231234
sleep 1.15
232235

0 commit comments

Comments
 (0)