Hacker News new | past | comments | ask | show | jobs | submit login

It's a nice exercise in any web framework to figure out how you would serve a big response without buffering it in memory. This can be surprisingly hard with some frameworks that just assume that you are buffering the entire response in memory. Usually, if you look hard there is a way around this.

Buffering can be appropriate for small responses; or at least convenient. But for bigger responses this can be error prone. If you do this right, you serve the first byte of the response to the user before you read the last byte from wherever you are reading (database, file system, S3, etc.). If you do it wrong, you might run out of memory. Or your user's request times out before you are ready to respond.

This is a thing that's gotten harder with non-blocking frameworks. Spring Boot in particular can be a PITA on this front if you use it with non-blocking IO. I had some fun figuring that out some years ago. Using Kotlin makes it slightly easier to deal with low level Spring internals (fluxes and what not).

Sometimes the right answer is that it's too expensive to figure out the content length, or a content hash. Whatever you do, you need to send the headers with that information before you send anything else. And if you need to read everything before you can calculate that information and send it, your choices are buffering or omitting that information.




"This can be surprisingly hard with some frameworks that just assume that you are buffering the entire response in memory. Usually, if you look hard there is a way around this."

This is the #1 most common mistake made by a "web framework".

Before $YOU jump up with a list of exceptions, it slowly gets better over time, and it has been getting better for a while, and there are many frameworks in the world, so the list that get it right is quite long. But there's still a lot of frameworks out there that assume this, that consider streaming to be the "exception" rather than non-streaming being a special case of streaming, and I still see new people make this mistake with some frequency, so the list of frameworks that still incorporate this mistake into their very core is also quite long.

My favorite is when I see a new framework sit on top of something like Go that properly streams, and it actively wrecks the underlying streaming capability to turn an HTTP response into a string.

Streaming properly is harder in the short term, but writing a framework where all responses are strings becomes harder in the long term. You eventually hit the wall where that is no longer feasible, but then, fixing it becomes very difficult.

Simply not sending a content-length is often the right answer. In an API situation, whatever negative consequences there are are fairly muted. The real problem I encounter a lot is when I'm streaming out some response from some DB query and I encounter a situation that I would have yielded a 500-type response for after I've already streamed out some content. It can be helpful to specify in your API that you may both emit content and an error and users need to check both. For instance, in the common case of dumping JSON, you can spec a top-level {"results": [...], "error": ...} as your return type, stream out a "results", but if a later error occurs, still return an "error" later. Arguably suboptimal, but requiring all errors to be known up front in a streaming situation is impossible, so... suboptimal wins over impossible.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: