Live data from Hacker News

Serving a half billion requests per day with Rust and CGI

jacob.gold

31–40 of 118 posts

Re: Serving a half billion requests per day with Rust and CGI

#32
post #19

Earlier quoted context omitted.

Think about all the problems associated with process life cycle - is a process stalled? How often should I restart a crashed process? Why is that process using so much memory? How should my process count change with demand? All of those go away when the lifecycle is tied to the request. It’s also more secure because each request is isolated at the process level. Long lived processes leak information to other requests…

> Think about all the problems associated with process life cycle - is a process stalled? Should I restart it? Why is that process using so much memory? How should my process count change with demand? All of those go away when the lifecycle is tied to the request. So the upshot of writing CGI scripts is that you can... ship broken, buggy code that leaks memory to your webserver and have it work mostly alright. I mean…

I'd push back on some of this. Specifically, the memory management that is somewhat inherent to how a CGI script works is typically easier to manage than longer life cycle things. You just tear down the entire process; instead of having to carefully tear down each thing created during the process.

Sure, it is easy to view this as the process being somewhat sloppy with regards to how it did memory. But it can also be seen as just less work. If you can toss the entire allocated range of memory, what benefit is there to carefully walking back each allocated structure? (Notably, arenas and such are efforts to get this kind of behavior in longer lived processes.)

Re: Serving a half billion requests per day with Rust and CGI

#33

Earlier quoted context omitted.

I guess multiprocessing got a bad reputation because it used to be slow and simple so it got looked down upon as a primitive tool for less capable developers. But the world has changed. Modern systems are excellent for multiprocessing, CPUs are fast, cores are plentiful and memory bandwidth just continues getting better and better. Single thread performance has stalled. It really is time to reconsider the old mantras…

That really has nothing to do with the choice to use CGI. You can just as well use rust with Axum or Actix and get a fully threaded web server without having to fork for every request.

Absolutely, I'm not recommending for everybody to go back using CGI (the protocol). I was responding to this:

> The CGI model may still work fine, but it is an outdated execution model

The CGI model of one process per request is excellent for modern hardware and really should not be scoffed at anymore IMO.

It can both utilize big machines, scale to zero, is almost leak-proof as the OS cleans up all used memory and file descriptors, is language-independent, dead simple to understand, allows for finer granularity resource control (max mem, file descriptor count, chroot) than threads, ...

How is this execution model "outdated"?

Re: Serving a half billion requests per day with Rust and CGI

#34
I have recently been writing CGI scripts for the web server of our universities computer lab in Go, and it has been a nice experience. In my case, the Guestbook doesn't use SQLite but I just encode the list of entries using Go's native https://pkg.go.dev/encoding/gob format, and it worked out well -- and critically frees me from using CGO to use SQLite!

But in the end efficiency isn't my concern, as I have almost not visitors, what turns out to be more important is that Go has a lot of useful stuff in the standard library, especially the HTML templates, that allow me to write safe code easily. To test the statement, I'll even provide the link and invite anyone to try and break it: https://wwwcip.cs.fau.de/~oj14ozun/guestbook.cgi (the worst I anticipate happening is that someone could use up my storage quota, but even that should take a while).

Re: Serving a half billion requests per day with Rust and CGI

#35
post #19

Earlier quoted context omitted.

> Think about all the problems associated with process life cycle - is a process stalled? Should I restart it? Why is that process using so much memory? How should my process count change with demand? All of those go away when the lifecycle is tied to the request. So the upshot of writing CGI scripts is that you can... ship broken, buggy code that leaks memory to your webserver and have it work mostly alright. I mean…

> So the upshot of writing CGI scripts is that you can... ship broken, buggy code that leaks memory to your webserver and have it work mostly alright Yes. The code is already shitty. That’s life. Let’s make the system more reliable and fault tolerant. This argument sounds a lot like “garbage collection is for bad programmers who can’t manage their memory”. But let me add another reason with your framing. In fire/forg…

> Yes. The code is already shitty. That’s life. Let’s make the system more reliable so that small code mistakes are disasters.

> This argument sounds a lot like “garbage collection is for bad programmers who can’t manage their memory”.

This is not a "Simply don't make mistakes" type of argument, it's more like a "We've moved past this problem" type of argument. The choice of garbage collection as an example is a little funny, because actually I'd argue heavily in favor of using garbage collection if you're not latency-sensitive; after all, like I said, I use Go for a lot of one-off servers.

It'd be one thing if every person had to sit there and solve again the basic problems behind writing an HTTP server, but you don't anymore. Many modern platforms put a perfectly stable HTTP server right in the standard library, freeing you from even needing to install more dependencies to be able to handle HTTP requests effectively.

> > The only way you can really prevent that is by having complete isolation between processes

> Yes. That’s the idea. Web server forks, and execs. Separate memory spaces.

That's not complete isolation between processes. You can still starve the CPU or RAM, get into contention over global locks (e.g. sqlite database), do conflicting file I/O inside the same namespace. I can go on but the point is that I don't consider two processes running on the same machine to be "isolated" with each-other. ("Process isolation" is typically used to talk about isolation between processes, not isolation of workloads into processes.) If you do it badly, you can wind up with requests that sporadically fail or hang. If you do it worse, you can wind up with corruption/interleaving writes/etc.

Meanwhile, if you're running a typical Linux distro with systemd, you can slap cgroups and namespacing onto your service with the triviality of slapping some options into an INI file. (And if you're not because you hate systemd, well, all of the features are still there, you just may need to do more work to use them.)

> > What information does this leak

> Anything that might be in a resource, or memory. Or even in the resource of a library you use.

> > and why should I be concerned

> Accessing leaked information form a prior run is a common attack.

I will grant you that you can't help it if one of your dependencies (or God help you, the standard library/runtime of your programming language) is buggy and leaks global state between instantiations. Practically speaking though, if you are already not sharing state between requests this is just not a huge issue.

Sometimes it feels like we're comparing "simple program written in CGI where it isn't a big deal if it fails or has some bugs" to "complex program written using a FastCGI or HTTP server where it is a big deal if it leaks a string between users".

> As opposed to? All processes have a working directory. What problems come from using the file system?

The problem isn't the working directory, it's the fact that anything in a cgi-bin directory 1. will be exec'd if it can be 2. exists under the document root, which the webserver typically has privileges to write to.

> Yes it’s the same amount of effort to configure this.

I actually really didn't read this before writing out how easy it was to use these with systemd, so I guess refer to the point above.

Re: Serving a half billion requests per day with Rust and CGI

#36

Earlier quoted context omitted.

That really has nothing to do with the choice to use CGI. You can just as well use rust with Axum or Actix and get a fully threaded web server without having to fork for every request.

Absolutely, I'm not recommending for everybody to go back using CGI (the protocol). I was responding to this: > The CGI model may still work fine, but it is an outdated execution model The CGI model of one process per request is excellent for modern hardware and really should not be scoffed at anymore IMO. It can both utilize big machines, scale to zero, is almost leak-proof as the OS cleans up all used memory and fi…

The part of the execution model that is dated is this:

> having the web server execute stuff in a specific folder inside the document root just seems like a recipe for problems

Re: Serving a half billion requests per day with Rust and CGI

#37
post #31

It'd be interesting to compare the performance of the author's approach to an analogous design that changes CGI for WASI, and scripts/binaries to Wasm.

Would it? It would be exactly the same but a bit slower because of the WASM overhead.

Re: Serving a half billion requests per day with Rust and CGI

#38
> No one should ever run a Bash script under CGI. It’s almost impossible to do so securely, and performance is terrible.

Actually shell scripting is the perfect language for CGI on embedded devices. Bash is ~500k and other shells are 10x smaller. It can output headers and html just fine, you can call other programs to do complex stuff. Obviously the source compresses down to a tiny size too, and since it's a script you can edit it or upload new versions on the fly. Performance is good enough for basic work. Just don't let the internet or unauthenticated requests at it (use an embedded web server with basic http auth).

Re: Serving a half billion requests per day with Rust and CGI

#39
post #22

One reason to use CGI is legacy systems. A large, complex, and important system that I inherited was still using CGI (and it worked, because a rare "10x genuinely more productive" developer built it). Many years later, to reduce peak resource usage, and speed up a few things, I made an almost drop-in replacement library, to permit it to also run with SCGI (and back out easily to CGI if there was a problem in producti…

I found that ChatGPT revived vanilla javascript and jquery for me.

Most of the chore part is done by chatgpt and the mental model of understanding what it wrote is very light and often single file. It is also easily embedded in static file generators.

On the contrary Vue/React have a lot of context required to understand and mentally parse. On react the useCallback/useEffect/useMemo make me need to manually manage dependencies. This really reminds me of manual memory management in C, with perhaps even more pitfalls. On vue the difference between computed, props and vanilla variables. I am amazed that the supposed more approachable part of tech is actually more complex than regular library/script programming.

Re: Serving a half billion requests per day with Rust and CGI

#40
This is a followup to Gold's previous post that served 200 million requests per day with CGI, which Simon Willison wrote a post about, which we had a thread about three days ago at https://news.ycombinator.com/item?id=44476716. It addresses some of the misconceptions that were common in that thread.

Summary:

- 60 virtual AMD Genoa CPUs with 240 GB (!!!) of RAM

- bash guestbook CGI: 40 requests per second (and a warning not to do such a thing)

- Perl guestbook CGI: 500 requests per second

- JS (Node) guestbook CGI: 600 requests per second

- Python guestbook CGI: 700 requests per second

- Golang guestbook CGI: 3400 requests per second

- Rust guestbook CGI: 5700 requests per second

- C guestbook CGI: 5800 requests per second

https://github.com/Jacob2161/cgi-bin

I wonder if the gohttpd web server he was using was actually the bottleneck for the Rust and C versions?

Post reply on HN