Hi,
We're on Vertx 3.2.0 and we have an HttpVerticle accepting file uploads. These uploads can get quite large (2GB). The verticle is accepting HTTP requests through the vertx-web framework. What we see is:
- upload is quite slow
- application takes a lot of memory while uploading, roughly size of upload times 1.5
I have created another verticle, doing the same thing, but with standard vertx.createHttpServer() and a lot of callback handlers. In this POC code, I don't see the same behavior. Upload speed is normal and memory footprint is low.
I've been debugging and profiling and found this method in the default body handler (io.vertx.ext.web.handler.impl.BodyHandlerIml) of vertx-web:
@Override
public void handle(Buffer buff) {
if (failed) {
return;
}
if (bodyLimit != -1 && (body.length() + buff.length()) > bodyLimit) {
failed = true;
context.fail(413);
} else {
body.appendBuffer(buff);
}
}
While debugging, this method gets called. And when the upload is finished, this gets called:
void doEnd() {
if (failed || ended) {
return;
}
ended = true;
HttpServerRequest req = context.request();
if (mergeFormAttributes && req.isExpectMultipart()) {
req.params().addAll(req.formAttributes());
}
context.setBody(body);
context.next();
}
Putting the entire upload on the context, as far as I can see.
I don't think this should happen. When sending a multi part request, we should not try to handle the buffer, but rely on the file that was streamed here:
public BHandler(RoutingContext context) {
this.context = context;
Set<FileUpload> fileUploads = context.fileUploads();
makeUploadDir(context.vertx().fileSystem());
context.request().setExpectMultipart(true);
context.request().exceptionHandler(context::fail);
context.request().uploadHandler(upload -> {
// We actually upload to a file with a generated filename
uploadCount.incrementAndGet();
String uploadedFileName = new File(uploadsDir, UUID.randomUUID().toString()).getPath();
upload.streamToFileSystem(uploadedFileName);
FileUploadImpl fileUpload = new FileUploadImpl(uploadedFileName, upload);
fileUploads.add(fileUpload);
upload.exceptionHandler(context::fail);
upload.endHandler(v -> uploadEnded());
});
}
Or am I not making sense here?
regards,
Kenneth