spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Slow webflux multipart upload

Open sbxz opened this issue 8 months ago • 4 comments

Hello,

We are developing a document submission application using WebFlux. However, we have noticed that handling multipart requests is slower with WebFlux compared to MVC. For example, when using MultipartFile in MVC, sending a 200MB file takes about 700ms, whereas it takes 4.2s with WebFlux (FilePart) and 4.5s with PartEvent.

Did we miss something?

Here is a repository reproducing the issue: Repo

Spring boot version : 3.4.4 Java 21

sbxz avatar Mar 25 '25 14:03 sbxz

I suspected that when working in this area recently in #34178 and #34388 but was not 100% sure, but your report seems to confirm there is something to improve there, so I will try to have a deeper look.

What I saw was slow upload locally with big files, so I know how to reproduce even if for now, I am not sure about the reason.

sdeleuze avatar Mar 26 '25 14:03 sdeleuze

Hello @sdeleuze

I’d like to contribute to this, may I ?

I've reduced the elapsed time from 4.2s to 700ms locally

Before:

~/IdeaProjects/WebfluxSlowMultipart git:[main]
curl -w "\nElapsed: %{time_total}s\n" \
     -F file="@200mb.pdf" \
     http://localhost:8080/part-event \
     -o /dev/null -s
Elapsed: 4.260208s


~/IdeaProjects/WebfluxSlowMultipart git:[main]
curl -w "\nElapsed: %{time_total}s\n" \
  -F file="@200mb.pdf" \
  http://localhost:8080/file-part \
  -o /dev/null -s
  Elapsed: 4.346252s

After:

~/IdeaProjects/WebfluxSlowMultipart git:[main]
curl -w "\nElapsed: %{time_total}s\n" \
     -F file="@200mb.pdf" \
     http://localhost:8080/part-event \
     -o /dev/null -s
Elapsed: 0.391556s


~/IdeaProjects/WebfluxSlowMultipart git:[main]
curl -w "\nElapsed: %{time_total}s\n" \
  -F file="@200mb.pdf" \
  http://localhost:8080/file-part \
  -o /dev/null -s
Elapsed: 0.407896s


xyraclius avatar Aug 20 '25 19:08 xyraclius

@xyraclius please share your findings, it is hard for us to tell you whether a contribution would be accepted if we don't know the type of changes you are working on.

bclozel avatar Aug 21 '25 07:08 bclozel

A quick profiling session shows that NettyDataBuffer.getByte() is responsible here for most of the overhead. This is quite similar to an issue reported to Netty.

Image

I think we should introduce an efficient variant in DataBuffer for traversing bytes from a DataBuffer, I have created #35623 for that. This is generally useful and in my tests this brings a x2 improvement. We would still need another x2 improvement to be on par with the multipart support from Servlet containers, but this is a good first step.

bclozel avatar Oct 13 '25 14:10 bclozel