ostap

ostap

Investigating 502s from load testing my Phoenix app on Dokku

Finally deployed my Phoenix with SQLite3 app on Dokku. First things first, I did a lil wrk test from my laptop.

Wrk command with results and server info

The server is “RS 2000 G12” from Netcups. The endpoint serves an inertia-phoenix-powered controller-scaffolded login page. Default Dokku on Debian setup. No Nginx or kernel tuning applied.

$ wrk -t12 -c400 -d30s https://example.com/users/log-in
Running 30s test @ https://example.com/users/log-in
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   127.67ms  140.81ms   1.15s    89.49%
    Req/Sec    98.90     81.46   535.00     82.60%
  33595 requests in 30.07s, 64.23MB read
  Non-2xx or 3xx responses: 5365
Requests/sec:   1117.32
Transfer/sec:      2.14MB

$ wrk -t12 -c400 -d30s https://example.com/users/log-in
Running 30s test @ https://example.com/users/log-in
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   202.20ms  287.43ms   1.96s    89.15%
    Req/Sec    96.50     92.09   640.00     84.52%
  32998 requests in 30.10s, 63.95MB read
  Non-2xx or 3xx responses: 4778
Requests/sec:   1096.33
Transfer/sec:      2.12MB

$ wrk -t12 -c400 -d30s https://example.com/users/log-in
Running 30s test @ https://example.com/users/log-in
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   168.45ms  200.40ms   1.57s    86.71%
    Req/Sec   100.09     86.62   650.00     83.00%
  34932 requests in 30.10s, 64.79MB read
  Non-2xx or 3xx responses: 6704
Requests/sec:   1160.69
Transfer/sec:      2.15MB

During the test, the server’s CPU usage went up to 50-70%, and the process using it the most was either the Beam container or Nginx. In Nginx, I have an SSL certificate to encrypt traffic from Cloudflare to the server.

After some time of running wrk, the server started responding with a 502 error. And I’m really curious to see why that is. I thought it would keep fighting until the CPU reaches 100%.

Even after the wrk is finished, it takes about 5-15 seconds for the server to change from 502s to 200.

Checking Dokku and Docker logs shows lots of similar request logs, which makes it hard to spot any errors:

09:52:34.997 request_id=GIUJwWJwbI8MW6cAAwso [info] GET /users/log-in

Anything I could do to find the reasons behind those 502? My first guess is adding Sentry or alike to see if there are any error logs besides the request logs. Will post an update.

Marked As Solved

ostap

ostap

I applied keepalive 64 to my Nginx proxy config and re-run wrk test. The throughput doubled, there were no 502 errors anymore :tada:

upstream subkit-5000 {

  server 172.17.0.3:5000;
  keepalive 64;
}

It seems that without keepalive, there would be a ton of constant new TCP connections opened between Nginx and the Docker container, making it hit the machine’s Linux configuration limit:

P.S I’m yet to find out if my Nginx config gets overridden on every Dokku deploy, in that case I’ll consult Dokku docs on what’s the best way to apply my own config template.

Also Liked

rhcarvalho

rhcarvalho

(Load) testing can be fun and uncover a lot of interesting lessons!

One tip is reading what Gil Tene wrote on the topic and several talks on YouTube (in particular about coordinated omission).

At some point you need to consider also what your goals are with the load testing, and when to stop.

Do you expect to serve 400 concurrent users with your current setup?

Hitting your controllers is only one part of the test. Consider also WebSocket persistent connections (there will be client-Cloudflare, Cloudflare-nginx, and nginx-BEAM connections open). Consider database writes according to your expectations.

Have fun and learn a bunch!

rhcarvalho

rhcarvalho

Are you hitting Cloudflare in your tests or bypassing it and hitting your server directly?

502 is typically Error 502 or 504 · Cloudflare Support docs

You also need to get familiar with your nginx configuration Nginx Proxy - Dokku Documentation

You need to consider all hops in your setup before reaching the BEAM.

Sentry will not help you in this particular case (i.e. networking and errors before your application sees a request).

rhcarvalho

rhcarvalho

Yep, look at Nginx Proxy - Dokku Documentation

The good thing is you can keep your nginx config along with your app’s source code.

Last Post!

ostap

ostap

I contributed proxy-keepalive option. Now we can easily set it without using a custom config file:

dokku nginx:set --global proxy-keepalive 64

Where Next?

Popular in Questions Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New

Other popular topics Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 54006 488
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New

We're in Beta

About us Mission Statement