Live data from Hacker News

How Rust Lets Us Monitor 30k API calls/min

blog.bearer.sh

31–40 of 81 posts

Re: How Rust Lets Us Monitor 30k API calls/min

#31
post #6

Sorry, I must be missing something in this blog post because the requirements here sound incredibly minimal. You just needed an HTTP service (sitting behind an Envoy proxy) to process a mere 500 requests/second (up to 1MB payload) and pipe them to Kinesis? How much data preparation is happening in Rust? It sounds like all the permission/rate-limiting/etc happens between Envoy/Redis before it ever reaches Rust? I know…

Seriously, 500 qps was something we used to do in interpreted languages on the Pentium Pro. But this kind of blog post is a whole genre: How [ridiculous startup name] serves [trivial traffic] using only [obscenely wasteful infrastructure] in [trendy runtime framework that's a tiny niche or totally unknown in real industry].

Re: How Rust Lets Us Monitor 30k API calls/min

#32

Earlier quoted context omitted.

They ruled out a language because it had stop the world GC, and even if it removed the bottleneck, it would likely become one later. Java has the same issue. Rust does not. Not sure why they'd consider Java, given that concern.

Shenandoah and ZGC collectors have worse case pauses of ~10ms and average pauses of 0.5ms . The average pause is faster than malloc() sometimes in C, so you won't really be introducing more latency than C does. The other option is to avoid allocating memory at all, which you could do in C/Rust but also in Java. The vast majority of shops given the choice for low/no allocation performance use Java anyways (HFT)

It’s easier to filter for better candidates by hiring for Rust over Java

Re: How Rust Lets Us Monitor 30k API calls/min

#33
post #6

Sorry, I must be missing something in this blog post because the requirements here sound incredibly minimal. You just needed an HTTP service (sitting behind an Envoy proxy) to process a mere 500 requests/second (up to 1MB payload) and pipe them to Kinesis? How much data preparation is happening in Rust? It sounds like all the permission/rate-limiting/etc happens between Envoy/Redis before it ever reaches Rust? I know…

They list out what is being done by the service - "It would receive the logs, communicate with an elixir service to check customer access rights, check rate limits using Redis, and then send the log to CloudWatch. There, it would trigger an event to tell our processing worker to take over." That sounds like a decent amount of work for a service, and without more detail it's very hard to say whether or not a given lev…

wasn't that 4k requests per minute?

Re: How Rust Lets Us Monitor 30k API calls/min

#34

They didn't mention Java as a possible solution, even though its GC's are far better than anything else out there. I have nothing against Rust but if I was at a startup I would save my innovation points for where they're mandatory

They did mention Go that also has a modern GC, and does not need to run your app under a software VM. But ultimately, even Go is just another GC language. They had no real need for GC and the like so why use Go, let alone Java?

Re: How Rust Lets Us Monitor 30k API calls/min

#35
post #6

Sorry, I must be missing something in this blog post because the requirements here sound incredibly minimal. You just needed an HTTP service (sitting behind an Envoy proxy) to process a mere 500 requests/second (up to 1MB payload) and pipe them to Kinesis? How much data preparation is happening in Rust? It sounds like all the permission/rate-limiting/etc happens between Envoy/Redis before it ever reaches Rust? I know…

They list out what is being done by the service - "It would receive the logs, communicate with an elixir service to check customer access rights, check rate limits using Redis, and then send the log to CloudWatch. There, it would trigger an event to tell our processing worker to take over." That sounds like a decent amount of work for a service, and without more detail it's very hard to say whether or not a given lev…

> That sounds like a decent amount of work for a service

5+ years ago I wrote a real-time transcoding, muxing streaming radio service that did 5000 simultaneous connections with inline, per-client ad spot injection (every 30 seconds in my benchmark). Using C and Lua. On 2 Xeon E3 cores--1 core for all the stream transcoding, muxing, and HTTP/RTSP setup, 1 core for the Lua controller (which was mostly idle). The ceiling was handling all the NIC IRQs.

While I think what I did was cool, I know people can eke much more performance out of their hardware than I can. And I wasn't even trying too hard--my emphasis is always on writing clear code and simple abstractions (though that often translates into cache-friendly code).

At my day job, in the past two months I've seen two services in a "scalable" k8s clusters fall over because the daemons were running with file descriptor ulimits of 1024. "Highly concurrent" Go-based daemons. For all the emphasis on scale, apparently none of the engineers had yet hit the teeny, tiny 1024 descriptor limit.

We really do need to raise our expectations a little.

I haven't written any Rust but I have recently helped someone writing a concurrent Rust-based reverse proxy service debug their Rust code and from my vantage point I have some serious criticisms of Tokio. Some of the decisions are clearly premature optimization chosen by people who probably haven't actually developed and pushed into production a process that handles 10s of thousands of concurrent connections, single-threaded or multi-threaded. At least not without a team of people debugging things and pushing it along. For example, their choice of defaulting to edge-triggered instead of level-triggered notification shows a failure to appreciate the difficulties of managing backpressure, or debugging lost edge-triggered readiness state. These are hard lessons to learn, but people don't often learn them because in practice it's cheaper and easier to scale up with EC2 than it is to actually write a solid piece of software.

Re: How Rust Lets Us Monitor 30k API calls/min

#36

Earlier quoted context omitted.

> I have nothing against Rust but if I was at a startup I would save my innovation points for where they're mandatory An article published today, addressing that exact point: https://tim.mcnamara.nz/post/621040767010504704/spend-your-n...

Ah yes, a 236 word "article", that says to choose "boring old technology" and also to use Rust in the same breath. This article should mention that rust isn't close to ready when it comes to web backends. As much as I love Rust, if I were running a startup or even a decently sized company I would always choose Rails. Now -that- is boring, old... and mature technology. Certain components could get re-written in Rust,…

> This article should mention that rust isn't close to ready when it comes to web backends.

Actix-web works just fine. They got a new maintainer team involved that has been spending some time getting rid of all the insane unsafety that was in the code before.

Re: How Rust Lets Us Monitor 30k API calls/min

#37

Having never dealt with issues relating to garbage collection before, how do you go about diagnosing GC issues in a language where that’s all handled for you?

In the case of NodeJS, which use the V8 Engine [0], you have access to the diagonistics API [1] that allows you profile your cpu or memory consumption. There are some tools that make that easier (see [2] or [3]) but you often left to interpret the result yourself

[0]: https://v8.dev/

[1]: http://nodejs.org/dist/latest-v12.x/docs/api/inspector.html#...

[2]: https://github.com/davidmarkclements/0x

[3]: https://github.com/vmarchaud/openprofiling-node

PS: Disclamer i wrote one of tool i mentionned above [3]

Re: How Rust Lets Us Monitor 30k API calls/min

#38
post #3

I'm one of the engineers that worked on this. It was the first Rust production app code I've written so it was a really fun project.

Interesting post ! From what i've understood you had only one instance of NodeJS, while i agree that Rust is more performant generally, couldn't have you just added more instance ?

Re: How Rust Lets Us Monitor 30k API calls/min

#39
post #35

Earlier quoted context omitted.

They list out what is being done by the service - "It would receive the logs, communicate with an elixir service to check customer access rights, check rate limits using Redis, and then send the log to CloudWatch. There, it would trigger an event to tell our processing worker to take over." That sounds like a decent amount of work for a service, and without more detail it's very hard to say whether or not a given lev…

> That sounds like a decent amount of work for a service 5+ years ago I wrote a real-time transcoding, muxing streaming radio service that did 5000 simultaneous connections with inline, per-client ad spot injection (every 30 seconds in my benchmark). Using C and Lua. On 2 Xeon E3 cores--1 core for all the stream transcoding, muxing, and HTTP/RTSP setup, 1 core for the Lua controller (which was mostly idle). The ceili…

All I'm saying is that without some example of the payloads they're managing, and the logic they're performing, it's hard to say "this is inefficient". And, as I mentioned, if their CPU and memory are both very low, it's likely they're hitting a network (or, yes, OS) limit.

I've seen places hit ulimit limits...I've also seen places hit port assignment issues, where they're calling out to a downstream that can handle thousands of requests with a single instance, so there are two, and there aren't enough port identifiers to support that (and the engineers are relying on code that isn't reusing connections properly). Those are all things worth learning to do right, agreed, and generally doing right. I'm just reluctant to call out someone for doing something wrong unless I -know- they're doing something wrong. The numbers don't tell the whole story.

Re: How Rust Lets Us Monitor 30k API calls/min

#40
post #6

Sorry, I must be missing something in this blog post because the requirements here sound incredibly minimal. You just needed an HTTP service (sitting behind an Envoy proxy) to process a mere 500 requests/second (up to 1MB payload) and pipe them to Kinesis? How much data preparation is happening in Rust? It sounds like all the permission/rate-limiting/etc happens between Envoy/Redis before it ever reaches Rust? I know…

It took your comment to make me notice it wasn't 30k requests/second but minute instead.

500 requests per second is what I would expect of a default PHP + Apache installation on a small Ubuntu server.

I too have a hard time grasping whats special here. For example I saw cached Wordpress setups handle 400 to 500 requests per second. And Wordpress isn't known for performance even with caching plugins.

Post reply on HN