From 81d26e91b777ab120e8f52d45385f0e018438ba4 Mon Sep 17 00:00:00 2001 From: Christian Gregg Date: Tue, 9 Mar 2021 14:40:31 +0000 Subject: [PATCH] Print warning when running one-worker cluster (#2565) * Print warning when running one-worker cluster Running Puma in cluster-mode is likely a misconfiguration in most scenarios. Cluster mode has some overhead of running an addtional 'control' process in order to manage the cluster. If only running a single worker it is likely not worth paying that overhead vs running in single mode with additional threads instead. There are some scenarios where running cluster mode with a single worker may still be warranted and valid under certain deployment scenarios, see the linked issue for details. From experience at work, we were able to migrate our Rails applications from single worker cluster to single-mode and saw a reduction of RAM usage of around ~15%. Closes #2534 * Remove link to issue * Add #silence_single_worker_warning option * Test single_worker_warning --- History.md | 1 + lib/puma/cluster.rb | 11 +++++++++++ lib/puma/configuration.rb | 1 + lib/puma/dsl.rb | 7 +++++++ test/test_config.rb | 16 ++++++++++++++++ test/test_integration_cluster.rb | 22 ++++++++++++++++++++++ 6 files changed, 58 insertions(+) diff --git a/History.md b/History.md index 4d841f59f8..15fc6d8e8b 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ * Features * Your feature goes here (#Github Number) + * Warn when running Cluster mode with a single worker (#2565) * Bugfixes * Your bugfix goes here (#Github Number) diff --git a/lib/puma/cluster.rb b/lib/puma/cluster.rb index 1c593d771f..592160790d 100644 --- a/lib/puma/cluster.rb +++ b/lib/puma/cluster.rb @@ -382,6 +382,8 @@ def run log "Use Ctrl-C to stop" + single_worker_warning + redirect_io Plugins.fire_background @@ -470,6 +472,15 @@ def run private + def single_worker_warning + return if @options[:workers] != 1 || @options[:silence_single_worker_warning] + + log "! WARNING: Detected running cluster mode with 1 worker." + log "! Running Puma in cluster mode with a single worker is often a misconfiguration." + log "! Consider running Puma in single-mode in order to reduce memory overhead." + log "! Set the `silence_single_worker_warning` option to silence this warning message." + end + # loops thru @workers, removing workers that exited, and calling # `#term` if needed def wait_workers diff --git a/lib/puma/configuration.rb b/lib/puma/configuration.rb index d7c18ad14e..a28777d63b 100644 --- a/lib/puma/configuration.rb +++ b/lib/puma/configuration.rb @@ -193,6 +193,7 @@ def puma_default_options :debug => false, :binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"], :workers => Integer(ENV['WEB_CONCURRENCY'] || 0), + :silence_single_worker_warning => false, :mode => :http, :worker_timeout => DefaultWorkerTimeout, :worker_boot_timeout => DefaultWorkerTimeout, diff --git a/lib/puma/dsl.rb b/lib/puma/dsl.rb index 45511e072e..b7761dbfc2 100644 --- a/lib/puma/dsl.rb +++ b/lib/puma/dsl.rb @@ -482,6 +482,13 @@ def workers(count) @options[:workers] = count.to_i end + # Disable warning message when running in cluster mode with a single worker. + # + # @note Cluster mode only. + def silence_single_worker_warning + @options[:silence_single_worker_warning] = true + end + # Code to run immediately before master process # forks workers (once on boot). These hooks can block if necessary # to wait for background operations unknown to Puma to finish before diff --git a/test/test_config.rb b/test/test_config.rb index 171e222672..072a3b3356 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -342,6 +342,22 @@ def test_final_options_returns_merged_options assert_equal 2, conf.final_options[:max_threads] end + def test_silence_single_worker_warning_default + conf = Puma::Configuration.new + conf.load + + assert_equal false, conf.options[:silence_single_worker_warning] + end + + def test_silence_single_worker_warning_overwrite + conf = Puma::Configuration.new do |c| + c.silence_single_worker_warning + end + conf.load + + assert_equal true, conf.options[:silence_single_worker_warning] + end + private def assert_run_hooks(hook_name, options = {}) diff --git a/test/test_integration_cluster.rb b/test/test_integration_cluster.rb index e5bca38936..fc22588f69 100644 --- a/test/test_integration_cluster.rb +++ b/test/test_integration_cluster.rb @@ -313,6 +313,28 @@ def test_application_is_loaded_exactly_once_if_using_preload_app assert_equal 0, worker_load_count end + def test_warning_message_outputted_when_single_worker + cli_server "-w 1 test/rackup/hello.ru" + + output = [] + while (line = @server.gets) && line !~ /Worker \d \(PID/ + output << line + end + + assert_match /WARNING: Detected running cluster mode with 1 worker/, output.join + end + + def test_warning_message_not_outputted_when_single_worker_silenced + cli_server "-w 1 test/rackup/hello.ru", config: "silence_single_worker_warning" + + output = [] + while (line = @server.gets) && line !~ /Worker \d \(PID/ + output << line + end + + refute_match /WARNING: Detected running cluster mode with 1 worker/, output.join + end + private def worker_timeout(timeout, iterations, config)