5

I have a website where new requirement came up where user can download a zip file of 500MB from the server. I am expecting max 10 concurrent users(CU) will be performing this activity . Webservers contains the files on same server.

Current stats : Currently server 60 request per sescond with 99 percentile served with in 1 second . CPU/Memory stats are under utilized.Though I will get the performance benchmarking done here. But before development would like to know what factors I should consider to know below points

  1. What max extra memory and CPU these 10 Concurrent Request(CR) may use ?
  2. Can I serve it from existing web server only ? Or Ideally this should be catered through separate webservers ?
  3. what impact this use case can create on my existing traffic ?

My understanding theoritically is

  1. Considering 100 Mbps network speed average scenario at server side , it will take 40 sec just to transfer the bytes.
  2. Conidering HDD read spead i.e. 2 MB per sec on an average , it will take 250 secs(approx 5 minutes ) just to read the data from disk. Which means that 10 concurrent http thread will be continously working(primarily doing disk IOPS) for 5 mins . Is n't it ?
  3. CPU load may be fine as it is primarily disk io intensive ops. Extra memory load of 5 GB may be there for 10 CR . Right ?
  4. In case of more than 10 CR, I will take them in queue and process later.
  5. My final understanding is : If I store the files on separate server I can reduce the IOPS from current webserver , I may be good considering I get 5 GB of extra memory

Just to want to get the solution/thoughts to design it correctly .

8
  • What web framework are you going to use? The design you settle on is going to depend largely on what your chosen web framework supports. Commented Aug 2, 2020 at 2:51
  • Java Spring framework. @Robert Not sure if system design and impact on existing system will depend on chosen web framework Commented Aug 2, 2020 at 2:52
  • 2
    What hard drive do you have that reads 2MB per second? Should be more like 100MB. You refer to iops. That’s irrelevant for a single, huge file.
    – gnasher729
    Commented Aug 2, 2020 at 7:58
  • Where does the 5GB memory requirement come from? Even if you were to completely load the file to memory you could just do that once and save it in a static byte[] variable, which would only have 500MB overhead. Still way too much, just let the operating system's file system cache handle this. If you have enough RAM, the file might not be loaded from disk every time.
    – amon
    Commented Aug 2, 2020 at 9:59
  • 1
    Five seconds isn't 250 seconds. But there is no reason to read 5GB in one go. If you worry about latency of other operations, you read 10MB, send 10MB. Read another 10MB, send another 10MB. Of course it's not free but you can't have free.
    – gnasher729
    Commented Aug 2, 2020 at 22:22

3 Answers 3

7

I believe you are overthinking this. The slowest link in the chain, whether it's disk or network is going to be the only real limiting factor. You have a choice, you can delegate downloads to another service, or you can serve it yourself. If possible, I would stage your files on SSD drives, which have bandwidth that exceeds your network speeds. Either that, or have another service host it, and simply redirect to the file location.

Offload the problem

For example, AWS S3 buckets can serve large files to millions of concurrent users without breaking a sweat. If you simply have that serve your downloads, then you don't have to do anything fancy.

Do it yourself

The main thing you have to do is to stream the data. When you look at the braindead examples on most tutorials, they have you read the entire image into memory and copy it to the output. That requires you to have at least 500MB of free RAM for each concurrent request.

By streaming data, you only need a couple megabytes for each concurrent request for buffering. You would use a StreamingResponseBody to send the data back. Here is another article to help you out.

The concept is to read a few bytes and serialize that, read a few more bytes, and serialize that. With a small buffer, you can easily max out the Tomcat server hosting your Spring application with roughly 1 GB of memory usage. That makes your service a whole lot more economical to host, and scaling is also easy.

5
  • Regarding first solution you suggested which is Offload the problem I believe to use the S3 service, I need to authenticate the request at my end and redirect to S3 service. If I redirect and expose the s3 url to client , wont I be exposing the s3 url to client and will it be a security issue ? Regarding Second solution you suggested which is inbuilt service Here actually files are stored on the HDD which is on same web server , which means slowest link is disk. I believe even in this case I can go for StreamingResponseBody as you pointed. Right ? Commented Aug 2, 2020 at 6:06
  • 1
    @user3198603 yes, you need to consider security when redirecting to S3. This works best when all objects should be public anyway. For S3, also pay close attention to bandwidth costs. If you want to serve the resource yourself, StreamingResponseBody would be the DIY solution, but Spring has built-in mechanisms to serve static files from disk. That's useful for cache headers, byte-range requests, and so on.
    – amon
    Commented Aug 2, 2020 at 9:56
  • @amon I will explore S3 more to see if we can make those url's authenticated. Regarding in built solution, actually I have those files stored as blob in database right now. Can you please throw some light here how to proceed. Should I save those files to file system first and then read it ? Commented Aug 2, 2020 at 12:32
  • You still use the StreamingResponseBody. You just need to get the blob's output stream to copy into the response output stream. If possible, also set the Content-Length header to the total number of bytes so the browser can give a proper progress bar, Commented Aug 2, 2020 at 18:54
  • 2
    Regarding S3, you can configure it to have a unique URL for each download that is only good for a relatively short period of time. That means you can authenticate and the exposure of the URL is only good for the timeout period of time you set. Commented Aug 2, 2020 at 18:55
-1

Do the work locally. Don’t read the whole 500MB at a time ob you, but maybe 10MB at a time. Make sure the client can resume a failed download, that makes everyone happier.

That’s the cheapest way to implement it. Both development wise, and you save the overhead of renting space elsewhere, paying for it, every month so the service doesn’t just stop, cancel it if the feature isn’t needed anymore, making sure it’s secure and so on. Then if it turns out this badly affects your performance, then you can try more complex methods.

-1

new requirement came up where user can download a zip file of 500MB from the server.

I would challenge this "requirement".

That file could A half Gigabyte zip file could contain an awful lot of your company's Data that's could wind up anywhere.

Why is it necessary to let this much Data out, into "The Wild"?

Webservers contains the files on same server.

So you already have a solution.
Provide the users with a link to the file and let their Browser do the heavy lifting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.