-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make WebFlux multipart support fully Reactive [SPR-17122] #21659
Comments
Rossen Stoyanchev commented There are two styles of usage, one via For reading via |
Can someone correct me if I am wrong I see that the current implementation uses This means, if I am uploading a file of size 2GB, a proportional amount of RAM is used up by databuffers, which is a recipe for Out Of Memory errors This is what I am referring to, and the implementation of public static Flux<DataBuffer> split(Publisher<DataBuffer> dataBuffers, byte[] delimiter,
// a lot of code
return Flux.from(dataBuffers)
.flatMap(buffer -> endFrameOnDelimiter(buffer, matcher))
.bufferUntil(buffer -> buffer == END_FRAME)
// a lot more code
} Since we are doing buffering, we are collecting all buffers into a Here are my two cents on how the implementation might needs to be changed to
This way, we don't buffer the data in memory & allow the backpressure be properly cascaded Please let me know your thoughts |
Can we use Since the mapping inside I am referring to Flux.from(dataBuffers)
.flatMap(buffer -> endFrameOnDelimiter(buffer, matcher)) |
Have deleted few comments & corrected few comments above as they were wrong/misleading in earlier versions |
Please note that I dont have the complete understanding of the source. Its possible that I might have the incorrect understanding |
If I am not wrong, we can add the following methods instead to have lazy reading & not buffer anything in memory public static Flux<Flux<DataBuffer>> streamingSplit(Publisher<DataBuffer> dataBuffers, byte[] delimiter) {
DataBufferUtils.Matcher matcher = matcher(delimiter);
return Flux.from(dataBuffers)
.concatMap(buffer -> {
return endFrameOnDelimiter(buffer, matcher, delimiter.length)
.windowWhile(bufferFrame -> bufferFrame != END_FRAME);
})
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
}
private static Flux<DataBuffer> endFrameOnDelimiter(DataBuffer dataBuffer, DataBufferUtils.Matcher matcher, int delimiterLength) {
List<DataBuffer> result = new ArrayList<>();
do {
int endIdx = matcher.match(dataBuffer);
int readPosition = dataBuffer.readPosition();
if (endIdx != -1) {
int length = endIdx + 1 - readPosition ;
result.add(dataBuffer.retainedSlice(readPosition, length));
result.add(END_FRAME);
dataBuffer.readPosition(endIdx + delimiterLength + 1); // lets skip delimiter length
} else {
result.add(retain(dataBuffer));
break;
}
} while (dataBuffer.readableByteCount() > 0);
DataBufferUtils.release(dataBuffer);
return Flux.fromIterable(result);
} |
@thekalinga I think you are right, and have created #23184 to track this issue. Thank you for trying the 5.2 milestones and reporting bugs in them! In the future, feel free to file an issue directly instead of commenting on a closed issue (though you might want to refer to your newly created issue in a comment, to get our attention). |
AFAICT, the only difference in your last snippet is the one that skips the delimiter length (shown above). But |
The DefaultMultipartMessageReader has been removed for 5.2 and will be part of a future release. This commit switches back to the SynchronossPartHttpMessageReader. gh-21659
The refactoring of the |
Any update on this? Are there plans to move away from Synchronoss nio-multipart in one of the next releases? |
Yes, this planned for 5.3.
|
This commit introduces the DefaultMultipartMessageReader, a fully reactive multipart parser without third party dependencies. An earlier version of this code was introduced in fb642ce, but removed again in 77c24aa because of buffering issues. Closes spring-projectsgh-21659
Sébastien Deleuze opened SPR-17122 and commented
Spring Framework 5.0 and 5.1 provides support for reactive multipart leveraging Synchronoss nio-multipart library which comes with several limitations. Spring WebFlux feeds data to the parser, which then provides a callback when the entire content for the part is ready, potentially creating temporary files when the content is too big to avoid consuming too much memory. These limitations mainly comes from the fact that their StreamStorage abstraction is based on
InputStream
/OutputStream
.As proposed initially by Arjen Poutsma, we should probably write our own reactive multipart implementation to overcome these limitation and provide a fully reactive behavior where the content of the file is Reactive Streams compliant (bytes comes to the user as they are received) in order to give more control to the user. If we provide this, I tend to think that we don't have to create temporary files.
Issue Links:
The text was updated successfully, but these errors were encountered: