Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,15 @@ int maxReadsInProgressCount() {

@Override
public void close() {
LOG.info("Closing RequestProcessor");
Comment thread
Vanlightly marked this conversation as resolved.
shutdownExecutor(writeThreadPool);
shutdownExecutor(readThreadPool);
if (serverCfg.getNumLongPollWorkerThreads() > 0 || readThreadPool == null) {
shutdownExecutor(longPollThreadPool);
}
shutdownExecutor(highPriorityThreadPool);
requestTimer.stop();
LOG.info("Closed RequestProcessor");
}

private OrderedExecutor createExecutor(
Expand All @@ -295,6 +297,7 @@ private OrderedExecutor createExecutor(
private void shutdownExecutor(OrderedExecutor service) {
if (null != service) {
service.shutdown();
service.forceShutdown(10, TimeUnit.SECONDS);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ public synchronized void shutdown() {
if (!running) {
return;
}
exitCode = bookie.shutdown();
this.requestProcessor.close();
exitCode = bookie.shutdown();
running = false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ protected boolean isVersionCompatible() {
}

protected void sendResponse(int rc, Object response, OpStatsLogger statsLogger) {
channel.writeAndFlush(response, channel.voidPromise());
if (channel.isActive()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about adding a "ELSE" branch with a log message ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently what happens when the BookieServer is shutdown is that for each in-progress op, when the response is sent it will fail because the channel is closed, and that will get logged as a metric:

if (!future.isSuccess()) {
    requestProcessor.getRequestStats().getChannelWriteStats()
                        .registerFailedEvent(writeElapsedNanos, TimeUnit.NANOSECONDS);
} else {
    requestProcessor.getRequestStats().getChannelWriteStats()
                        .registerSuccessfulEvent(writeElapsedNanos, TimeUnit.NANOSECONDS);
 }

So either we add an ELSE branch with the same registerFailedEvent call, or we don't use an IF at all and allow it to use the existing logic flow.

@pradeepbn what was the reason for avoiding channel.writeAndFlush?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say this change is not strictly related to shutdown reordering, so I think removing these channel checks can be removed from this PR.

@pradeepbn pradeepbn Nov 23, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Vanlightly

bookie1_1 | 02:15:08,926 ERROR Failed to submit a listener notification task. Event loop shut down? bookie1_1 | java.util.concurrent.RejectedExecutionException: event executor terminated bookie1_1 | at io.netty.util.concurrent.SingleThreadEventExecutor.reject(SingleThreadEventExecutor.java:923) ~[netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.util.concurrent.SingleThreadEventExecutor.offerTask(SingleThreadEventExecutor.java:350) ~[netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.util.concurrent.SingleThreadEventExecutor.addTask(SingleThreadEventExecutor.java:343) ~[netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:825) ~[netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:815) ~[netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.util.concurrent.DefaultPromise.safeExecute(DefaultPromise.java:841) [netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:499) [netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:184) [netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.channel.DefaultChannelPromise.addListener(DefaultChannelPromise.java:95) [netty-transport-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at io.netty.channel.DefaultChannelPromise.addListener(DefaultChannelPromise.java:30) [netty-transport-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at org.apache.bookkeeper.proto.PacketProcessorBaseV3.sendResponse(PacketProcessorBaseV3.java:91) [bookkeeper-server.jar:?] bookie1_1 | at org.apache.bookkeeper.proto.WriteEntryProcessorV3.sendResponse(WriteEntryProcessorV3.java:180) [bookkeeper-server.jar:?] bookie1_1 | at org.apache.bookkeeper.proto.WriteEntryProcessorV3$1.writeComplete(WriteEntryProcessorV3.java:108) [bookkeeper-server.jar:?] bookie1_1 | at org.apache.bookkeeper.bookie.Journal$QueueEntry.run(Journal.java:335) [bookkeeper-server.jar:?] bookie1_1 | at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?] bookie1_1 | at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?] bookie1_1 | at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-common-4.1.68.Final.jar:4.1.68.Final] bookie1_1 | at java.lang.Thread.run(Thread.java:829) [?:?]

If we do not check for isActive(), we will get this exception on channel.writeAndFlush at the time of shutdown. This happens because of nettyserver shutdown before the request processor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a controlled exception within Netty but it will pollute the log during shutdown. So I think that's its worth keeping the IF statement. What do you think @eolivelli?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, let's keep the original "if"

we could add an "else" branch with a DEBUG log that says that we are skipping the write, this would help in debugging tests failures probably one day

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the else statement with the logs. CC: @eolivelli @Vanlightly

channel.writeAndFlush(response, channel.voidPromise());
} else {
LOGGER.debug("Netty channel {} is inactive, "
+ "hence bypassing netty channel writeAndFlush during sendResponse", channel);
}
if (BookieProtocol.EOK == rc) {
statsLogger.registerSuccessfulEvent(MathUtils.elapsedNanos(enqueueNanos), TimeUnit.NANOSECONDS);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,29 @@ protected void sendResponse(StatusCode code, Object response, OpStatsLogger stat
requestProcessor.invalidateBlacklist(channel);
}
}

channel.writeAndFlush(response).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
long writeElapsedNanos = MathUtils.elapsedNanos(writeNanos);
if (!future.isSuccess()) {
requestProcessor.getRequestStats().getChannelWriteStats()
.registerFailedEvent(writeElapsedNanos, TimeUnit.NANOSECONDS);
} else {
requestProcessor.getRequestStats().getChannelWriteStats()
.registerSuccessfulEvent(writeElapsedNanos, TimeUnit.NANOSECONDS);
}
if (StatusCode.EOK == code) {
statsLogger.registerSuccessfulEvent(MathUtils.elapsedNanos(enqueueNanos), TimeUnit.NANOSECONDS);
} else {
statsLogger.registerFailedEvent(MathUtils.elapsedNanos(enqueueNanos), TimeUnit.NANOSECONDS);
if (channel.isActive()) {
Comment thread
Vanlightly marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about adding a "ELSE" branch with a log message ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

channel.writeAndFlush(response).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
long writeElapsedNanos = MathUtils.elapsedNanos(writeNanos);
if (!future.isSuccess()) {
requestProcessor.getRequestStats().getChannelWriteStats()
.registerFailedEvent(writeElapsedNanos, TimeUnit.NANOSECONDS);
} else {
requestProcessor.getRequestStats().getChannelWriteStats()
.registerSuccessfulEvent(writeElapsedNanos, TimeUnit.NANOSECONDS);
}
if (StatusCode.EOK == code) {
statsLogger.registerSuccessfulEvent(MathUtils.elapsedNanos(enqueueNanos), TimeUnit.NANOSECONDS);
} else {
statsLogger.registerFailedEvent(MathUtils.elapsedNanos(enqueueNanos), TimeUnit.NANOSECONDS);
}
}
}
});
});
} else {
LOGGER.debug("Netty channel {} is inactive, "
+ "hence bypassing netty channel writeAndFlush during sendResponse", channel);
}
}

protected boolean isVersionCompatible() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public void setup() {
when(requestProcessor.getBookie()).thenReturn(bookie);
when(requestProcessor.getWaitTimeoutOnBackpressureMillis()).thenReturn(-1L);
when(requestProcessor.getRequestStats()).thenReturn(new RequestStats(NullStatsLogger.INSTANCE));
when(channel.isActive()).thenReturn(true);
processor = new ForceLedgerProcessorV3(
request,
channel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public void setup() {
requestProcessor = mock(BookieRequestProcessor.class);
when(requestProcessor.getBookie()).thenReturn(bookie);
when(requestProcessor.getRequestStats()).thenReturn(new RequestStats(NullStatsLogger.INSTANCE));
when(channel.isActive()).thenReturn(true);
processor = WriteEntryProcessor.create(
request,
channel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public void setup() {
when(requestProcessor.getBookie()).thenReturn(bookie);
when(requestProcessor.getWaitTimeoutOnBackpressureMillis()).thenReturn(-1L);
when(requestProcessor.getRequestStats()).thenReturn(new RequestStats(NullStatsLogger.INSTANCE));
when(channel.isActive()).thenReturn(true);
processor = new WriteEntryProcessorV3(
request,
channel,
Expand Down