diff --git a/lib/puma/cluster/worker.rb b/lib/puma/cluster/worker.rb index 236b5f20de..face057687 100644 --- a/lib/puma/cluster/worker.rb +++ b/lib/puma/cluster/worker.rb @@ -146,6 +146,8 @@ def run # Invoke any worker shutdown hooks so they can prevent the worker # exiting until any background operations are completed @config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data) + + Process.kill "SIGTERM", master if server.idle_timeout_reached ensure @worker_write << "t#{Process.pid}\n" rescue nil @worker_write.close diff --git a/lib/puma/runner.rb b/lib/puma/runner.rb index ddeddb71b3..a22c5b08a2 100644 --- a/lib/puma/runner.rb +++ b/lib/puma/runner.rb @@ -70,7 +70,7 @@ def start_control app = Puma::App::Status.new @launcher, token - # A Reactor is not created aand nio4r is not loaded when 'queue_requests: false' + # A Reactor is not created and nio4r is not loaded when 'queue_requests: false' # Use `nil` for events, no hooks in control server control = Puma::Server.new app, nil, { min_threads: 0, max_threads: 1, queue_requests: false, log_writer: @log_writer } diff --git a/lib/puma/server.rb b/lib/puma/server.rb index 1fb7fe4bec..e282fdd182 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -37,6 +37,7 @@ class Server attr_reader :events attr_reader :min_threads, :max_threads # for #stats attr_reader :requests_count # @version 5.0.0 + attr_reader :idle_timeout_reached # @todo the following may be deprecated in the future attr_reader :auto_trim_time, :early_hints, :first_data_timeout, @@ -117,6 +118,8 @@ def initialize(app, events = nil, options = {}) @precheck_closing = true @requests_count = 0 + + @idle_timeout_reached = false end def inherit_binder(bind) @@ -328,7 +331,11 @@ def handle_servers begin ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : @idle_timeout) unless ios - @status = :stop unless shutting_down? + unless shutting_down? + @idle_timeout_reached = true + @status = :stop + end + break end @@ -367,6 +374,7 @@ def handle_servers @queue_requests = false @reactor.shutdown end + graceful_shutdown if @status == :stop || @status == :restart rescue Exception => e @log_writer.unknown_error e, nil, "Exception handling servers" diff --git a/test/test_integration_cluster.rb b/test/test_integration_cluster.rb index 282caba4d7..6c073cf297 100644 --- a/test/test_integration_cluster.rb +++ b/test/test_integration_cluster.rb @@ -221,6 +221,20 @@ def test_worker_timeout RUBY end + def test_idle_timeout + cli_server "-w #{workers} test/rackup/hello.ru", config: "idle_timeout 1" + + get_worker_pids # wait for workers to boot + + connect + + sleep 1.15 + + assert_raises Errno::ECONNREFUSED, "Connection refused" do + connect + end + end + def test_worker_index_is_with_in_options_limit skip_unless_signal_exist? :TERM diff --git a/test/test_integration_single.rb b/test/test_integration_single.rb index bab920d852..6b4719bfc7 100644 --- a/test/test_integration_single.rb +++ b/test/test_integration_single.rb @@ -254,6 +254,18 @@ def test_puma_debug_loaded_exts cli_pumactl 'stop' end + def test_idle_timeout + cli_server "test/rackup/hello.ru", config: "idle_timeout 1" + + connect + + sleep 1.15 + + assert_raises Errno::ECONNREFUSED, "Connection refused" do + connect + end + end + def test_pre_existing_unix_after_idle_timeout skip_unless :unix