1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00
puma--puma/test/test_config.rb
Christian Gregg 81d26e91b7
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
2021-03-09 07:40:31 -07:00

484 lines
13 KiB
Ruby

# frozen_string_literal: true
require_relative "helper"
require_relative "helpers/config_file"
require "puma/configuration"
require 'puma/events'
class TestConfigFile < TestConfigFileBase
parallelize_me!
def test_default_max_threads
max_threads = 16
max_threads = 5 if RUBY_ENGINE.nil? || RUBY_ENGINE == 'ruby'
assert_equal max_threads, Puma::Configuration.new.default_max_threads
end
def test_app_from_rackup
conf = Puma::Configuration.new do |c|
c.rackup "test/rackup/hello-bind.ru"
end
conf.load
# suppress deprecation warning of Rack (>= 2.2.0)
# > Parsing options from the first comment line is deprecated!\n
assert_output(nil, nil) do
conf.app
end
assert_equal ["tcp://127.0.0.1:9292"], conf.options[:binds]
end
def test_app_from_app_DSL
conf = Puma::Configuration.new do |c|
c.load "test/config/app.rb"
end
conf.load
app = conf.app
assert_equal [200, {}, ["embedded app"]], app.call({})
end
def test_ssl_configuration_from_DSL
skip 'No ssl support' unless ::Puma::HAS_SSL
conf = Puma::Configuration.new do |config|
config.load "test/config/ssl_config.rb"
end
conf.load
bind_configuration = conf.options.file_options[:binds].first
app = conf.app
assert bind_configuration =~ %r{ca=.*ca.crt}
assert bind_configuration =~ /verify_mode=peer/
assert_equal [200, {}, ["embedded app"]], app.call({})
end
def test_ssl_bind
skip_on :jruby
skip 'No ssl support' unless ::Puma::HAS_SSL
conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
cert: "/path/to/cert",
key: "/path/to/key",
verify_mode: "the_verify_mode",
}
end
conf.load
ssl_binding = "ssl://0.0.0.0:9292?cert=/path/to/cert&key=/path/to/key&verify_mode=the_verify_mode"
assert_equal [ssl_binding], conf.options[:binds]
end
def test_ssl_bind_jruby
skip_unless :jruby
skip 'No ssl support' unless ::Puma::HAS_SSL
cipher_list = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
keystore: "/path/to/keystore",
keystore_pass: "password",
ssl_cipher_list: cipher_list,
verify_mode: "the_verify_mode"
}
end
conf.load
ssl_binding = "ssl://0.0.0.0:9292?keystore=/path/to/keystore" \
"&keystore-pass=password&ssl_cipher_list=#{cipher_list}" \
"&verify_mode=the_verify_mode"
assert_equal [ssl_binding], conf.options[:binds]
end
def test_ssl_bind_no_tlsv1_1
skip_on :jruby
skip 'No ssl support' unless ::Puma::HAS_SSL
conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
cert: "/path/to/cert",
key: "/path/to/key",
verify_mode: "the_verify_mode",
no_tlsv1_1: true
}
end
conf.load
ssl_binding = "ssl://0.0.0.0:9292?cert=/path/to/cert&key=/path/to/key&verify_mode=the_verify_mode&no_tlsv1_1=true"
assert_equal [ssl_binding], conf.options[:binds]
end
def test_ssl_bind_with_cipher_filter
skip_on :jruby
skip 'No ssl support' unless ::Puma::HAS_SSL
cipher_filter = "!aNULL:AES+SHA"
conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
cert: "cert",
key: "key",
ssl_cipher_filter: cipher_filter,
}
end
conf.load
ssl_binding = conf.options[:binds].first
assert ssl_binding.include?("&ssl_cipher_filter=#{cipher_filter}")
end
def test_ssl_bind_with_verification_flags
skip_on :jruby
skip 'No ssl support' unless ::Puma::HAS_SSL
conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
cert: "cert",
key: "key",
verification_flags: ["TRUSTED_FIRST", "NO_CHECK_TIME"]
}
end
conf.load
ssl_binding = conf.options[:binds].first
assert ssl_binding.include?("&verification_flags=TRUSTED_FIRST,NO_CHECK_TIME")
end
def test_ssl_bind_with_ca
skip 'No ssl support' unless ::Puma::HAS_SSL
conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
cert: "/path/to/cert",
ca: "/path/to/ca",
key: "/path/to/key",
verify_mode: :peer,
}
end
conf.load
ssl_binding = conf.options[:binds].first
assert_match "ca=/path/to/ca", ssl_binding
assert_match "verify_mode=peer", ssl_binding
end
def test_lowlevel_error_handler_DSL
conf = Puma::Configuration.new do |c|
c.load "test/config/app.rb"
end
conf.load
app = conf.options[:lowlevel_error_handler]
assert_equal [200, {}, ["error page"]], app.call({})
end
def test_allow_users_to_override_default_options
conf = Puma::Configuration.new(restart_cmd: 'bin/rails server')
assert_equal 'bin/rails server', conf.options[:restart_cmd]
end
def test_overwrite_options
conf = Puma::Configuration.new do |c|
c.workers 3
end
conf.load
assert_equal conf.options[:workers], 3
conf.options[:workers] += 1
assert_equal conf.options[:workers], 4
end
def test_explicit_config_files
conf = Puma::Configuration.new(config_files: ['test/config/settings.rb']) do |c|
end
conf.load
assert_match(/:3000$/, conf.options[:binds].first)
end
def test_parameters_overwrite_files
conf = Puma::Configuration.new(config_files: ['test/config/settings.rb']) do |c|
c.port 3030
end
conf.load
assert_match(/:3030$/, conf.options[:binds].first)
assert_equal 3, conf.options[:min_threads]
assert_equal 5, conf.options[:max_threads]
end
def test_config_files_default
conf = Puma::Configuration.new do
end
assert_equal [nil], conf.config_files
end
def test_config_files_with_dash
conf = Puma::Configuration.new(config_files: ['-']) do
end
assert_equal [], conf.config_files
end
def test_config_files_with_existing_path
conf = Puma::Configuration.new(config_files: ['test/config/settings.rb']) do
end
assert_equal ['test/config/settings.rb'], conf.config_files
end
def test_config_files_with_non_existing_path
conf = Puma::Configuration.new(config_files: ['test/config/typo/settings.rb']) do
end
assert_equal ['test/config/typo/settings.rb'], conf.config_files
end
def test_config_files_with_integer_convert
conf = Puma::Configuration.new(config_files: ['test/config/with_integer_convert.rb']) do
end
conf.load
assert_equal 6, conf.options[:persistent_timeout]
assert_equal 3, conf.options[:first_data_timeout]
assert_equal 2, conf.options[:workers]
assert_equal 4, conf.options[:min_threads]
assert_equal 8, conf.options[:max_threads]
assert_equal 90, conf.options[:worker_timeout]
assert_equal 120, conf.options[:worker_boot_timeout]
assert_equal 150, conf.options[:worker_shutdown_timeout]
end
def test_config_files_with_float_convert
conf = Puma::Configuration.new(config_files: ['test/config/with_float_convert.rb']) do
end
conf.load
assert_equal Float::INFINITY, conf.options[:max_fast_inline]
end
def test_config_files_with_symbol_convert
conf = Puma::Configuration.new(config_files: ['test/config/with_symbol_convert.rb']) do
end
conf.load
assert_equal :ruby, conf.options[:io_selector_backend]
end
def test_config_raise_exception_on_sigterm
conf = Puma::Configuration.new do |c|
c.raise_exception_on_sigterm false
end
conf.load
assert_equal conf.options[:raise_exception_on_sigterm], false
conf.options[:raise_exception_on_sigterm] = true
assert_equal conf.options[:raise_exception_on_sigterm], true
end
def test_run_hooks_on_restart_hook
assert_run_hooks :on_restart
end
def test_run_hooks_before_worker_fork
assert_run_hooks :before_worker_fork, configured_with: :on_worker_fork
end
def test_run_hooks_after_worker_fork
assert_run_hooks :after_worker_fork
end
def test_run_hooks_before_worker_boot
assert_run_hooks :before_worker_boot, configured_with: :on_worker_boot
end
def test_run_hooks_before_worker_shutdown
assert_run_hooks :before_worker_shutdown, configured_with: :on_worker_shutdown
end
def test_run_hooks_before_fork
assert_run_hooks :before_fork
end
def test_run_hooks_and_exception
conf = Puma::Configuration.new do |c|
c.on_restart do |a|
raise RuntimeError, 'Error from hook'
end
end
conf.load
events = Puma::Events.strings
conf.run_hooks :on_restart, 'ARG', events
expected = /WARNING hook on_restart failed with exception \(RuntimeError\) Error from hook/
assert_match expected, events.stdout.string
end
def test_config_does_not_load_workers_by_default
assert_equal 0, Puma::Configuration.new.options.default_options[:workers]
end
def test_final_options_returns_merged_options
conf = Puma::Configuration.new({ min_threads: 1, max_threads: 2 }, { min_threads: 2 })
assert_equal 1, conf.final_options[:min_threads]
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 = {})
configured_with = options[:configured_with] || hook_name
messages = []
conf = Puma::Configuration.new do |c|
c.send(configured_with) do |a|
messages << "#{hook_name} is called with #{a}"
end
end
conf.load
conf.run_hooks hook_name, 'ARG', Puma::Events.strings
assert_equal messages, ["#{hook_name} is called with ARG"]
end
end
# Thread unsafe modification of ENV
class TestEnvModifificationConfig < TestConfigFileBase
def test_double_bind_port
port = (rand(10_000) + 30_000).to_s
with_env("PORT" => port) do
conf = Puma::Configuration.new do |user_config, file_config, default_config|
user_config.bind "tcp://#{Puma::Configuration::DefaultTCPHost}:#{port}"
file_config.load "test/config/app.rb"
end
conf.load
assert_equal ["tcp://0.0.0.0:#{port}"], conf.options[:binds]
end
end
end
class TestConfigEnvVariables < TestConfigFileBase
def test_config_loads_correct_min_threads
assert_equal 0, Puma::Configuration.new.options.default_options[:min_threads]
with_env("MIN_THREADS" => "7") do
conf = Puma::Configuration.new
assert_equal 7, conf.options.default_options[:min_threads]
end
with_env("PUMA_MIN_THREADS" => "8") do
conf = Puma::Configuration.new
assert_equal 8, conf.options.default_options[:min_threads]
end
end
def test_config_loads_correct_max_threads
conf = Puma::Configuration.new
assert_equal conf.default_max_threads, conf.options.default_options[:max_threads]
with_env("MAX_THREADS" => "7") do
conf = Puma::Configuration.new
assert_equal 7, conf.options.default_options[:max_threads]
end
with_env("PUMA_MAX_THREADS" => "8") do
conf = Puma::Configuration.new
assert_equal 8, conf.options.default_options[:max_threads]
end
end
def test_config_loads_workers_from_env
with_env("WEB_CONCURRENCY" => "9") do
conf = Puma::Configuration.new
assert_equal 9, conf.options.default_options[:workers]
end
end
def test_config_does_not_preload_app_if_not_using_workers
with_env("WEB_CONCURRENCY" => "0") do
conf = Puma::Configuration.new
assert_equal false, conf.options.default_options[:preload_app]
end
end
def test_config_preloads_app_if_using_workers
with_env("WEB_CONCURRENCY" => "2") do
preload = Puma.forkable?
conf = Puma::Configuration.new
assert_equal preload, conf.options.default_options[:preload_app]
end
end
end
class TestConfigFileWithFakeEnv < TestConfigFileBase
def setup
FileUtils.mkpath("config/puma")
File.write("config/puma/fake-env.rb", "")
end
def test_config_files_with_rack_env
with_env('RACK_ENV' => 'fake-env') do
conf = Puma::Configuration.new do
end
assert_equal ['config/puma/fake-env.rb'], conf.config_files
end
end
def test_config_files_with_rails_env
with_env('RAILS_ENV' => 'fake-env', 'RACK_ENV' => nil) do
conf = Puma::Configuration.new do
end
assert_equal ['config/puma/fake-env.rb'], conf.config_files
end
end
def test_config_files_with_specified_environment
conf = Puma::Configuration.new do
end
conf.options[:environment] = 'fake-env'
assert_equal ['config/puma/fake-env.rb'], conf.config_files
end
def teardown
FileUtils.rm_r("config/puma")
end
end