From bfd75f9f16f8557ddf91a069525d13f7bdc7ce8f Mon Sep 17 00:00:00 2001 From: Joe Ferris Date: Wed, 21 Oct 2015 16:52:00 -0400 Subject: [PATCH] Reduce number of Puma processes and threads Using a simple Suspenders application, I profiled memory usage for our default configuration, as well as a few others. I found the following: * A Puma cluster uses a master process and multiple worker processes. The amount of memory used by a cluster is equal to the memory usage of the master process plus the possible bloated size of a worker process times the number of worker processes. * At boot, a simple Suspenders application uses about 117MB for the master process and 109M for each worker. * After the first request is served, a process increases to around 117M, like the master process. * The amount of potential bloat increases with each thread, because it's possible for every thread to be handling a bloated request at once. * Using [siege], I determined that the expected bloat in a simple scenario is around 10M per thread. This will be much worse in some applications. This provides the following formula for maximum memory usage under load: master_usage + worker_count * (worker_usage + bloat * thread_count) For this simple Suspenders application, this formula provides the following worst-case usage: 117 + 3 * (117 + 10 * 5) = 618 This is over the 512MB limit for a 1x Heroku dyno, and the application is very simple. I recommend changing to a default of two worker processes and two threads per dyno, changing the usage to: 117 + 2 * (117 + 10 * 2) = 391 This provides reasonable performance with a high memory ceiling. When applications begin to show troublesome performance characteristics under load, developers can tune the application's process and thread count according to its real-world memory usage, possibly upgrading the dyno size as appropriate. [siege]: https://www.joedog.org/siege-home/ --- templates/puma.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/templates/puma.rb b/templates/puma.rb index e37a872ab..e64a0c7ff 100644 --- a/templates/puma.rb +++ b/templates/puma.rb @@ -3,8 +3,17 @@ # The environment variable WEB_CONCURRENCY may be set to a default value based # on dyno size. To manually configure this value use heroku config:set # WEB_CONCURRENCY. -workers Integer(ENV.fetch("WEB_CONCURRENCY", 3)) -threads_count = Integer(ENV.fetch("MAX_THREADS", 5)) +# +# Increasing the number of workers will increase the amount of resting memory +# your dynos use. Increasing the number of threads will increase the amount of +# potential bloat added to your dynos when they are responding to heavy +# requests. +# +# Starting with a low number of workers and threads provides adequate +# performance for most applications, even under load, while maintaining a low +# risk of overusing memory. +workers Integer(ENV.fetch("WEB_CONCURRENCY", 2)) +threads_count = Integer(ENV.fetch("MAX_THREADS", 2)) threads(threads_count, threads_count) preload_app!