Skip to content
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

jersey-netty-connector Intermittent Stuck eventLoop/ExecuterService threads if Request failed in writeEntity(executed in pool threads) under load #5837

Open
sumitaneja opened this issue Jan 15, 2025 · 1 comment

Comments

@sumitaneja
Copy link

NettyConnector doesn't blcoks eventLoops threads and eventually ExecutorServiceThreads under stress if writeEntity(executed in pool threads) fails/throw error for some reason.

We noticed this happening in two cases

  1. Malformed request (say content type is multipart and no body part is present). Request Errors out with illegal argument exception.
  2. We are sending large binary payload and target system returns 413. Occasionally there are BuffereOverflow and Stream Closed Exceptions too.

Issue identified in below code with NettyConnector

executorService.execute(new Runnable() {
@Override
public void run() {
// close listener is not needed any more.
ch.closeFuture().removeListener(closeListener);
try {
jerseyRequest.writeEntity();
if (entityWriter.getType() == NettyEntityWriter.Type.DELAYED) {
nettyRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, entityWriter.getLength());
contentLengthSet.countDown();
}
} catch (IOException e) {
responseDone.completeExceptionally(e);
}
}
});
headersSet.await();
if (!expect100ContinueHandler.isExpected()) {
// Send the HTTP request. Expect:100-continue processing is not applicable
// in this case.
entityWriter.writeAndFlush(nettyRequest);
}
if (HttpUtil.isTransferEncodingChunked(nettyRequest)) {
entityWriter.write(new HttpChunkedInput(entityWriter.getChunkedInput()));
} else {
entityWriter.write(entityWriter.getChunkedInput());
}
if (entityWriter.getType() == NettyEntityWriter.Type.DELAYED) {
contentLengthSet.await();
}
entityWriter.flush();

Our analysis shows this to be timing issue in case of error. I.e Line 509 [entityWriter.flush();] executes before 488 [responseDone.completeExceptionally(e);]. (issue can be recreated without stress by putting a simple sleep before 488)

Issue:
Line 509 starts task in nioEventloopGroup which tries to read chunks. This goes on till Channel is writable and pendingMessage is Chunked Input.
See https://github.com/netty/netty/blob/4.1/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java#L225-269

Once it goes in loop to read chunks, exception in doesnt signal this loop to break.

Possible fix in Netty Connector
close chunked input in NettyConnector on exception at line 488

Sample code fix
}catch (Throwable e){
if(entityWriter.getChunkedInput()!=null){
try {
System.out.println(" CLOSING CHUNKED");
entityWriter.getChunkedInput().close();
} catch (Exception ex) {
System.out.println("ERROR CLOSING CHUNKED");
}
}
responseDone.completeExceptionally(e);
}

@sumitaneja
Copy link
Author

Attached reproducer with suggested code fix in Netty connector. (NettyConnector is part of the

JerseyNetty.zip

zip)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant