I have an app that developed in Phoenix/Elixir.
Recently got challenged by another Architect not familiar with Elixir(Golang background).
The test is very simple:
- k6 to get .m3u8 file from my app ( small files need high RPS )
- k6 to get .ts file from my app (bigger files, size around 2MB, this need high throughput )
Setup a Nginx and host two static files: one .m3u8 , one .ts
Basically in single machine by using k6:
- for .m3u8, Nginx can get around 9600 RPS
- for .ts, Nginx can get around 2500 RPS
- for .m3u8, my app can get around 500 RPS
- for .ts, my app can get around 500 RPS
Not surprise that I suspect the Elixir layer, so I added a Varnish layer front my app, and I got:
- for .m3u8, Varnish can get around 9500 RPS
- for .ts, Varnish can get around 2500 RPS
So this is on par with Nginx.
My questions is for actual application, how many RPS and throughput we expect ?
My person idea were around: anything should around 50% of Nginx performance.
I need your honesty advise, as this might affect Elixir usage in my company. I previously thinking move bytes around should not have performance issue in Erlang Beam.
A test with static files is not a good benchmark. For static files I would not consider using elixir but rather nginx,caddy,apache you name it. These web servers are excellent at serving static files. Furthermore, the tooling is better there. Phoenix is a framework to build a web application.
I actually pretty unsatisfy the result, so I built a Rust app return the same, Rust app can easily return around 6000RPS for .m3u8. So I am not surprise that the other Architect mentioned Golang can have 10 times RPS than my app.
Is there anything I can tune? or Anything I need to avoid ? thanks
If you really want to, maybe start looking at other benchmarks like here 300k requests per second webserver in elixir! OTP21.2 - 10 cores. On another note, without code we cannot help much. And also how you executed it.
I got tagged.
10k RPS seems about what you would get due to the limits of TCP connections of 1 ip connecting to 1 ip:port combination. TCP is a complex subject beyond the scope of this thread and it seems your hitting that limit not the actual apps limit. The reason why I say this is if your getting 9k RPS serving a static file on a well maintained RUST webserver, the bottleneck is clearly not rust.
I would recommend running these tests over a unix streaming socket or if you need more largescale tests, to run them with large ip ranges connecting to the same ip:port combination. And measuring the speed serverside.
About that 300k RPS, I think its possible to get much more now with the JIT compiled VM and implementing zerocopy io_uring2 instead of using :gen_tcp or :socket. Tho using new :socket could produce much better results too.
If you’re looking at raw file performance you should avoid the usage of the erlang file_server. See the
:raw mode in
File: File — Elixir v1.13.2
And there’s numerous threads on the forum about performance including with files. Here’s a link to a helpful comment in one: Help with performance (file io) - #31 by sasajuric
Also what is k6? is it https://k6.io?
Can you maybe clarify how you served the files? Using Plug.Static, or using send_download in a controller, manual code in a controller?
|> Conn.put_resp_header("cache-control", "public, max-age=200000")
|> Conn.send_resp(:ok, file_binary)
This is the code I use to serve the file.
The file actually not from file system, it is just binary get back from Riak KV, the size for .ts file is around 2MB. I cached it in Redis (want to improve the response time)
yes, K6 is tool in Golang as you provided the link.
No to 10K RPS yet, my app only get this result: 500 RPS for small m3u8 file size around 1.4KB.
Check out: The Erlangelist - Low latency in Phoenix
Make sure you’re running in prod mode, with a good keepalive. Exrm isn’t needed for releases nowadays since we have mix release.
When sending files, nginx uses sendfile, you can use it from elixir as well:
:file.sendfile. Otherwise it’s not a fair comparison.