maxguzenski
Hello.
I don’t know what the best place is, hoping to find any help with the problem I’ve been facing in production for about 3 days.
I have a niche social network that, on average, has 4 thousand users online, and makes around 30 thousand requests per second to the server. The application runs smoothly with an average per request of just 0.02s and memory of around 10GB. But something happens when the site reaches 5.5k users online, everything drops very quickly (less than 10s), the application goes from 10gb to 60gb in a few seconds, the Elixir Logger starts dropping logs, the server loses all connections with the database and starts trying to reestablish new connections, but they all timeout… so the app becomes irresponsible until docker is restarted (returning to normal speed and staying like that for about 20 minutes).
Error that appears in the log thousands of times:
DBConnection.ConnectionError
tcp recv: closed (the connection was closed by the pool, possibly due to a timeout or because the pool has been terminated)
My infrastructure:
The application runs on Docker (generated by Phoenix 1.7), deploys with AWS ECS on a c6i.x24large (96vcpu/189gb) and Aurora r6.x4large database (16cpu/128gb)
The database uses around 35% of CPU, and remains that way even when the application crashes. The application uses only 10% CPU and 10GB of RAM.
ps.: All online users connect via websocket, in addition to the normal request.
Elixir 1.15.7
Phoenix 1.7.10 with all deps updated
React front-end
Things I’ve tried
- db connection pool: from 100 to 2400
- Direct connection to Aurora and throught RDS proxy as well
- I have tested with an instance with 50 gigabits of network performance
- soft/hard “ulimit” from default value to 130k
- queue_interval/target from 50ms to 2s
Because of websocket, I haven’t been able to test scaling horizontally yet.
Some screenshots
App on “normal” mode:
When app is not responding (See how memory increased):
Avarage request time, you can see it stop to responding very fast:
Aurora database shows that app is not reading the query result:
So what I need is some direction, it’s strange that it happens right after passing the 5500 users mark online, before that the app doesn’t present any problems or slowdowns.
I also don’t know if scaling horizontally would solve it, but there is no metric (memory/cpu) that indicates that the problem is close to occurring so that it can be scaled.
I would bet on some network limit, port exhaustion or process limit reached. But I don’t find anything in the logs other than the database connection error.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex














Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
mpope
Was a crash_dump generated?
maxguzenski
No, I said it “crash” but in fact the server just starts to timeout all requests. I’ll check next time if the docker container generates any files on disk.
josevalim
I would start by adding more metrics around the application. Having such a large connection number is likely going to lead to worse performance, as they won’t ever be effectively used and the runtime now has to spend more time managing them.
If you suspect this is related to the database, you should log the metrics emitted by Ecto, such as query time, checkout time, idle time and so on.
I would also log the memory metrics from the VM and, most important, the Erlang VM run queues, which tells you how much CPU/IO work it has to do. You can get those by running Phoenix LiveDashboard on the repo, but ideally you want to push these to an external tool too.
felix-starman
Are you by chance using Bandit? I was experiencing a similar symptom, although w/ no resource issues that I noticed, just sudden timeouts that felt like port exhaustion.
If so, there’s a PR @mtrudel put out for
thousand_island(Refine acceptor behaviour on abnormal conditions by mtrudel · Pull Request #103 · mtrudel/thousand_island · GitHub) that resolves an issue where the kernel might kill a connection, but the process responsible for it was not restarting since it looked “normal” to it. I was similarly seeing it occur during peaks, but it was mostly just coincidence since that’s when the most activity and aborted connections were occurring. I also saw large numbers of DB timeouts right after, but it was a red herring.maxguzenski
@felix-starman It was cowboy then yesterday I moved to bandit: same result.
@josevalim how many db connections do you recommend? 100 or even less ?
more about my app:
application.rb
pg listen (there is no issue releted to that I think):
deps version and deps tree:
Key configs from runtime/prod.exs (private data was changed):
mpope
When I am faced with these kinds of high-load issues I rely on recon and remote nodes. I’d suggest trying to come up with a load test that simulates this error (maybe 1/4 of the max users on 1/4 of the CPUs?), then connect to your Phoneix app using a remote node and poke around. Check scheduler usage across your CPUs, check processes with high memory / reductions, etc.
Ofcourse this can be done through automated metrics gathering by sweeping the system periodically and measuring / logging the most ‘heavy’ processes, but sometimes the proactive approach is easier to work with. You can see process state, what the current stacktrace is, etc.
Checking out Microstate Accounting and Lock Profiling can also help if nothing really stands out with scheduler usage or processes. Also try giving Erlang in Anger a read.
maxguzenski
@josevalim here is my telemetry logic, I’m log queries that takes over 700ms (and I have about 10 por minute)
1 - I’m not sure if this is the correct way to log things.
2 - I’ve already tried disabling logging completely, with no effect.
IMPORT: before I said 35 thousand requests per second, but it is per minute.
maxguzenski
Thank you very much for the tips and book, I’ve never needed to remotely debug an application in elixir/erlang, I’m going to read this book today. Thanks!
al2o3cr
Likely unrelated, but what’s the provisioned storage size for the DB?
Years ago, ran into a situation where some flavors of RDS backed by EBS would limit disk IOPS based on size. We had an app that didn’t use a ton of data but was very read-heavy - it ran into similar weird “spikes” when a temporary load increase exhausted the available I/O.
pdgonzalez872
likely unrelated disclaimer as well (kinda reminds me of this
), but, what do you keep in the cache?