puma: High memory usage Heroku (v5)
Hi all 👋🏼
Yesterday I deployed one of my Heroku apps with the new Puma v5 version (from 4.3.6 to 5.0.2). After that, my app starts to consume more memory (~x2) than in v4.3.6 (~pretty similar traffic):
I only changed the gem version, with no config tweaks. I’m running puma
with no cluster mode (only 1 worker) “single” mode and 20 threads, Rails 6.0.3 and Ruby 2.6. Do you know if I can get back to prior memory usage with any setting I’ve probably missed?
Thanks a lot for all your work in Puma!
NOTE I’m using Jemalloc too
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 3
- Comments: 38 (21 by maintainers)
Commits related to this issue
- Mention WEB_CONCURRENCY update in the upgrade doc https://github.com/puma/puma/issues/2393#issuecomment-702359283 — committed to puma/puma by schneems 4 years ago
- Mention WEB_CONCURRENCY update in the upgrade doc (#2402) https://github.com/puma/puma/issues/2393#issuecomment-702359283 — committed to puma/puma by schneems 4 years ago
- use WEB_CONCURRENCY.sh for WEB_CONCURRENCY default The Node.js, PHP and Python buildpacks write their WEB_CONCURRENCY defaults logic to this file name. This ensures that the last buildpack to run de... — committed to heroku/heroku-buildpack-ruby by dzuelke 3 years ago
- use WEB_CONCURRENCY.sh for WEB_CONCURRENCY default (#1188) * use WEB_CONCURRENCY.sh for WEB_CONCURRENCY default The Node.js, PHP and Python buildpacks write their WEB_CONCURRENCY defaults logic to... — committed to heroku/heroku-buildpack-ruby by dzuelke 3 years ago
We’ve repro’d as well on
codetriage
, will start bisecting soon to figure out which commit it isGood news everyone! It’s not a bug. It’s a feature! Before I say more. Let’s talk about debugging technique.
Debug process
My web app https://www.codetriage.com was running a beta version of Puma 5 for some time. When Nate put out a call for people who had seen this issue in the wild I asked if it showed up on CodeTriage. Nate deployed v436 and we saw memory use go way down. We’ve got a reproduction!
I then made a list of all the commits between the beta version we were previously on, and a known “low memory” commit. I then manually bisected the list. I deployed, waited ~30 minutes to see if the issue was present and then went higher or lower to zero in on the problem.
Towards the end I started posting pictures of memory graphs. In the last one, https://github.com/codetriage/CodeTriage/pull/1388, I describe exactly what I expect to see when I deploy if my theory holds, then I deployed and my theory held.
Here’s a graph:
Look at the right side of the graph. Memory drops a bunch that’s me deploying ae3522cb. When it climbs dramatically, that’s me deploying dc3b9b89.
So what happened in that commit range? It’s only one commit, and it was pretty minor, but it did include this line: https://github.com/puma/puma/blob/bb61c7b151c1610e107f914509cac8bb2a9a8b01/lib/puma/configuration.rb#L188
What happened in CodeTriage is that even though I’ve set WEB_CONCURRENCY on my app:
I never modified my config to read it in https://github.com/codetriage/CodeTriage/blob/54dddf119a2f81f7e628bfbd09ff678afe782bd7/config/puma.rb. Note the lack of a
workers
configuration in that file.So essentially: I had been accidentally running in puma’s “single” mode for the last few years even though I had intended to use processes and modify it via
WEB_CONCURRENCY
env var. With this commit, now my application is honoring my wishes and is booting two processes.Good news
The good news is that this increased memory use comes with increased performance. If you look at my graph above, when memory goes down…response time goes up, and the rails autoscale add-on kicks in to scale the app up. When memory goes back up due to more processes, response time goes down and auto-scaling goes down.
So basically: I wasn’t getting the perf I could have gotten out of this dyno if I had utlizing the config that I was setting.
TLDR;
Puma now knows about WEB_CONCURRENCY env var and is honoring the value set on your system. If you’re going above your memory limit’s you’ll need to adjust your WEB_CONCURRENCY value down. If you’re under your memory limits, you likely got increased performance from increased CPU usage that comes with multiple processes that you weren’t getting before. You’ll likely also want to modify your
config/puma.rb
to be a bit more clear about where the workers value is coming from. Rails generates the config file with this line commented out by default:I recommend uncommenting this line.
@schneems has narrowed it down to a range of about 4 commits. Should have an idea of the issue soon.
Also, after you’ve got that memory data, what’s the output of
heroku logs -n 10000 --app <your app name> | grep --context=8 "Puma starting"
. You may need to restart your app to get output. I want to see the lines logged when Puma starts.@markets yes, https://devcenter.heroku.com/articles/usage-and-billing, “Heroku calculates billing based on wall-clock usage”, “All costs are prorated to the second”
Sorry @nateberkopec, I only have access to 24h timeframe (Hobby dyno). I’m going to downgrade to Puma v4.3.6 and tomorrow will post the difference.
Some extra background, in case it’s useful:
WEB_CONCURRENCY
value, the Python, Nodejs and PHP Heroku buildpacks do.profile.d/WEB_CONCURRENCY.sh
in the app’s slug (example).profile.d/WEB_CONCURRENCY.sh
would win (since it overwrites the versions from earlier buildpacks) - similar to how the buildpacks API uses thebin/release
output from only the last buildpackSorry for not including that one in the upgrade guide! I had no idea Heroku buildpacks other than Ruby would set
WEB_CONCURRENCY
. If I did, I definitely would have made this change more obvious in the changelog.@schneems @dentarg thanks!
yes, I can confirm this comes from the Python buildpack (settings for Gunicorn server):
Great idea @edmorley. On it!
While this might not have been expected. I think it’s not a bug to be fixed. I think we should keep the current behavior and honor
WEB_CONCURRENCY
.@nateberkopec about 12 hours of both versions visible 👇🏼
Hi there, I’m seeing similar spike patterns in memory with Heroku.
With 5.0.0 on Friday 18th:
Then with 5.0.2:
These env vars are not set: WEB_CONCURRENCY, PUMA_MIN_THREADS, PUMA_MAX_THREADS, MIN_THREADS MAX_THREADS
And the logs:
We’re on Rails 6.0.3.3, Ruby 2.6.6 if that helps.
Thank you for the amazing work, and I appreciate you’re investigating this bug. Let me know how I can help you further.
Works for me - about 12 hours of both versions visible would really help.