Pump and server response end .... bug or something I'm doing wrong?

360 views
Skip to first unread message

Patrick Conway

unread,
Nov 18, 2016, 5:55:24 AM11/18/16
to vert.x

I've been hunting down an issue for a while where clients of my api using vertx have been timing out fairly frequently where requests seem to just hang.

I have managed to recreate the issue. 

I also found a solution but I'm wondering is this a bug or expected behaviour?

https://github.com/pconway11/vertx_pump_test

When writing to a response with pump... when consecutive requests come in quickly...the first request succeeds ...but the second request does not get accepted by the router.


However if I add a delay between the requests...they all succeed.

The solution I found is to put an endhandler on the readstream and call .end() on the writestream.( http response)

I'm just concerned that this may cause the response to be cut short if end happens before the pump finishes its work?


To recreate the issue from the github project , you can run the server with run.sh and then execute the Junit test GetRequest

The output in the log shows the server receives the second request.... but the router does not accept it

10:52:06.720 [vert.x-eventloop-thread-1] INFO  com.example.HttpVerticle - Server listening on port 8080
10:52:20.822 [vert.x-eventloop-thread-1] TRACE io.vertx.core.http.impl.HttpServerImpl - Server received request: /abc
10:52:20.832 [vert.x-eventloop-thread-1] TRACE io.vertx.ext.web.impl.RouterImpl - Router: 488545726 accepting request GET http://localhost:8080/abc
10:52:20.834 [vert.x-eventloop-thread-1] TRACE io.vertx.ext.web.impl.RoutingContextImplBase - Route matches: Route[ path:null pattern:null handler:io.vertx.ext.web.handler.impl.LoggerHandlerImpl@33054033 failureHandler:null order:0 methods:[]]@1413612421
10:52:20.834 [vert.x-eventloop-thread-1] TRACE io.vertx.ext.web.impl.RoutingContextImplBase - Calling the  handler
10:52:20.836 [vert.x-eventloop-thread-1] TRACE io.vertx.ext.web.impl.RoutingContextImplBase - Route matches: Route[ path:null pattern:null handler:com.example.HttpVerticle$$Lambda$29/1068682465@106cc826 failureHandler:null order:1 methods:[]]@2127102945
10:52:20.836 [vert.x-eventloop-thread-1] TRACE io.vertx.ext.web.impl.RoutingContextImplBase - Calling the  handler
10:52:20.979 [vert.x-eventloop-thread-1] TRACE io.vertx.core.http.impl.HttpServerImpl - Server received request: /abc


Julien Viet

unread,
Nov 18, 2016, 6:17:59 PM11/18/16
to ve...@googlegroups.com
Hi,

looking at your code, you are using executeBlocking (the RxJava flavor) to get the file with blocking API and also start the pump here.

1/ in an execute blocking, you should always complete the future argument (here f)
2/ the pump should not be created in the blocking code block, instead you should rather provide the file object to the future f
3/ in the lambda subscribing to the Observable (i.e the second block) you should use the file you obtained in the blocking code and setup the pump here

So this works:

Observable<AsyncFile> obs = Vertx.currentContext().<AsyncFile>executeBlockingObservable(f -> {
AsyncFile aFile = vertx.fileSystem().openBlocking("aFile.txt", new OpenOptions());
f.complete(aFile);
});
obs.subscribe(file -> {
ctx.response().putHeader("Content-Length", "19");
Pump p = Pump.pump(file, ctx.response(), 19);
p.start();
file.endHandler(e->{
logger.info("end file");
ctx.response().end();
});
}, error -> {
logger.error(error.getMessage(), error);
});

Here you don’t need to use executeBlocking whatsoever and you can use the fully non blocking of Vert.x as it will be more efficient:

  vertx.fileSystem().open("aFile.txt", new OpenOptions(), ar -> {
if (ar.succeeded()) {
AsyncFile file = ar.result();
ctx.response().putHeader("Content-Length", "19");
Pump p = Pump.pump(file, ctx.response(), 19);
p.start();
file.endHandler(e->{
logger.info("end file");
ctx.response().end();
});
} else {
Throwable error = ar.cause();
logger.error(error.getMessage(), error);
}
});
});
Finally you are overrding start(Future<Void> startFuture), instead you should override start(), the version you used must have its startFuture completed and is meant to have a verticle whose startup is asynchronous.

Julien


--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
Visit this group at https://groups.google.com/group/vertx.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/789c20c5-f751-44e5-9a24-ca4dfcccf437%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Patrick Conway

unread,
Nov 19, 2016, 11:00:40 AM11/19/16
to ve...@googlegroups.com

thanks Julien

Would it be correct to say .end should always be called on writeStream, even when using Pump?

Also I'm not quiet sure when to use executeBlocking.
In my main project I use a vertx http client a lot.  I wrapped all these requests in  executeBlocking methods as I was worried if they were slow or delayed they would block the event loop.... but I think this may have been an incorrect assumption?
Are there any good reading materials about how and when the event loop gets blocked?
I have seen lots of warnings about the "golden rule" of not blocking the event loop, but not a good explanation of what will cause a block or how to identify code which should be in executeBlocking.
Thanks for the help,
Patrick


To unsubscribe from this group and stop receiving emails from it, send an email to vertx+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to a topic in the Google Groups "vert.x" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/vertx/9Vj0zkKHOPA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to vertx+unsubscribe@googlegroups.com.

Alexander Lehmann

unread,
Nov 19, 2016, 12:08:57 PM11/19/16
to vert.x
As long as you use the vertx http client, the event loop will not be blocked, anything that slows down the http request is handled by the platform in a asynchronous way.

If you think of your code in vert.x as a thread that is being executed (which it isn't quite due to the async execution), the operations that you do will usually do some calculations and then run the next vert.x method with a handler. At this point the execution of the "thread" is returned to the vert.x event loop and it isn't blocked. If you start an operation in your code that takes more time and doesn't return execution to vert.x (e.g. thread.sleep(), wait(), a sync file operation etc), the time spent in your code blocks the loop.

This is a bit similar to cooperative multi-tasking like in Windows 3 16 bit, where the application has to take care to return the execution to the process manager, otherwise everything hangs.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.

--
You received this message because you are subscribed to a topic in the Google Groups "vert.x" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/vertx/9Vj0zkKHOPA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to vertx+un...@googlegroups.com.

Patrick Conway

unread,
Nov 19, 2016, 12:26:42 PM11/19/16
to ve...@googlegroups.com

thanks Alexander


To unsubscribe from this group and all its topics, send an email to vertx+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages