diff --git a/5.0-Upgrade.md b/5.0-Upgrade.md index 7a5a0ff1a8..38379a7d60 100644 --- a/5.0-Upgrade.md +++ b/5.0-Upgrade.md @@ -6,9 +6,9 @@ Puma 5 brings new experimental performance features, a few quality-of-life featu * **Better throughput and lower latency via new experimental option** * **Better memory usage via new experimental option** +* **Better copy-on-write performance via new experimental option** * **Loads of bugfixes** * Faster phased restarts and worker timeouts -* GC Compact/friendly fork * pumactl now has a `thread-backtraces` command to print thread backtraces, bringing thread backtrace printing to all platforms, not just *BSD and Mac. (#2053) * Added incrementing `requests_count` to `Puma.stats`. (#2106) * Faster phased restart and worker timeout (#2220) diff --git a/History.md b/History.md index 01f38bb3ca..0ed5630c4f 100644 --- a/History.md +++ b/History.md @@ -1,9 +1,9 @@ ## 5.0.0 * Features - * EXPERIMENTAL: Add `fork_worker` option and `refork` command for reduced memory usage. (#2099) - * EXPERIMENTAL: Reduce latency on MRI through inserting a small delay before re-listening on the socket if worker is busy (#2079). - * Possibly reduced memory usage by calling `GC.compact` before fork if available (Ruby 2.7+) (#2093) + * EXPERIMENTAL: Add `fork_worker` option and `refork` command for reduced memory usage by forking from a worker process and eliminating the master process. (#2099) + * EXPERIMENTAL: Added `wait_for_less_busy_worker` config. This may reduce latency on MRI through inserting a small delay before re-listening on the socket if worker is busy (#2079). + * EXPERIMENTAL: Added `nakayoshi_fork` option. Reduce memory usage in preloaded cluster-mode apps by GCing before fork and compacting, where available. (#2093, #2256) * Added pumactl `thread-backtraces` command to print thread backtraces (#2054) * Added incrementing `requests_count` to `Puma.stats`. (#2106) * Increased maximum URI path length from 2048 to 8196 bytes (#2167) diff --git a/lib/puma/cluster.rb b/lib/puma/cluster.rb index 7e78d97a67..861b231a13 100644 --- a/lib/puma/cluster.rb +++ b/lib/puma/cluster.rb @@ -298,7 +298,7 @@ def worker(index, master) restart_server.clear server.begin_restart(true) @launcher.config.run_hooks :before_refork, nil, @launcher.events - GC.compact if GC.respond_to?(:compact) + nakayoshi_gc end elsif idx == 0 # restart server restart_server << true << false @@ -540,7 +540,7 @@ def run @master_read, @worker_write = read, @wakeup @launcher.config.run_hooks :before_fork, nil, @launcher.events - GC.compact if GC.respond_to?(:compact) + nakayoshi_gc spawn_workers @@ -644,5 +644,16 @@ def timeout_workers end end end + + def nakayoshi_gc + return unless @options[:nakayoshi_fork] + log "! Promoting existing objects to old generation..." + 4.times { GC.start(full_mark: false) } + if GC.respond_to?(:compact) + log "! Compacting..." + GC.compact + end + log "! Friendly fork preparation complete." + end end end diff --git a/lib/puma/dsl.rb b/lib/puma/dsl.rb index 29419fc4bd..af0ac6c3ae 100644 --- a/lib/puma/dsl.rb +++ b/lib/puma/dsl.rb @@ -759,5 +759,18 @@ def set_remote_address(val=:socket) def fork_worker(after_requests=1000) @options[:fork_worker] = Integer(after_requests) end + + # When enabled, Puma will GC 4 times before forking workers. + # If available (Ruby 2.7+), we will also call GC.compact. + # Not recommended for non-MRI Rubies. + # + # Based on the work of Koichi Sasada and Aaron Patterson, this option may + # decrease memory utilization of preload-enabled cluster-mode Pumas. It will + # also increase time to boot and fork. See your logs for details on how much + # time this adds to your boot process. For most apps, it will be less than one + # second. + def nakayoshi_fork(enabled=false) + @options[:nakayoshi_fork] = enabled + end end end diff --git a/test/test_integration_cluster.rb b/test/test_integration_cluster.rb index 3744c3d65e..6c0415052c 100644 --- a/test/test_integration_cluster.rb +++ b/test/test_integration_cluster.rb @@ -168,6 +168,22 @@ def test_refork refute_includes pids, get_worker_pids(1, WORKERS - 1) end + def test_nakayoshi + cli_server "-w #{WORKERS} test/rackup/hello.ru", config: <