-
Notifications
You must be signed in to change notification settings - Fork 902
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
Fix memory leak when reading entry but the connection disconnected. #3528
Changes from all commits
28a4b23
1e397d2
ee23d2e
5db2f6a
c689ade
2bf5d7f
ba307ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
import io.netty.util.Recycler; | ||
import io.netty.util.Recycler.Handle; | ||
import io.netty.util.ReferenceCountUtil; | ||
import io.netty.util.ReferenceCounted; | ||
import org.apache.bookkeeper.proto.BookkeeperProtocol.AuthMessage; | ||
import org.apache.bookkeeper.util.ByteBufList; | ||
|
||
|
@@ -432,10 +433,8 @@ public String toString() { | |
opCode, ledgerId, entryId, errorCode); | ||
} | ||
|
||
void retain() { | ||
} | ||
|
||
void release() { | ||
boolean release() { | ||
return true; | ||
} | ||
|
||
void recycle() { | ||
|
@@ -445,7 +444,7 @@ void recycle() { | |
/** | ||
* A request that reads data. | ||
*/ | ||
class ReadResponse extends Response { | ||
class ReadResponse extends Response implements ReferenceCounted { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need to implement ReferenceCounted ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #3527 will give more context. |
||
final ByteBuf data; | ||
|
||
ReadResponse(byte protocolVersion, int errorCode, long ledgerId, long entryId) { | ||
|
@@ -466,13 +465,38 @@ ByteBuf getData() { | |
} | ||
|
||
@Override | ||
public void retain() { | ||
data.retain(); | ||
public int refCnt() { | ||
return data.refCnt(); | ||
} | ||
|
||
@Override | ||
public void release() { | ||
data.release(); | ||
public ReferenceCounted retain() { | ||
return data.retain(); | ||
} | ||
|
||
@Override | ||
public ReferenceCounted retain(int increment) { | ||
return data.retain(increment); | ||
} | ||
|
||
@Override | ||
public ReferenceCounted touch() { | ||
return data.touch(); | ||
} | ||
|
||
@Override | ||
public ReferenceCounted touch(Object hint) { | ||
return data.touch(hint); | ||
} | ||
|
||
@Override | ||
public boolean release() { | ||
return data.release(); | ||
} | ||
|
||
@Override | ||
public boolean release(int decrement) { | ||
return data.release(decrement); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ | |
|
||
import io.netty.channel.Channel; | ||
import io.netty.channel.ChannelFuture; | ||
import io.netty.channel.ChannelPromise; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.TimeUnit; | ||
import org.apache.bookkeeper.proto.BookieProtocol.Request; | ||
import org.apache.bookkeeper.stats.OpStatsLogger; | ||
|
@@ -104,13 +106,13 @@ protected void sendResponse(int rc, Object response, OpStatsLogger statsLogger) | |
} | ||
|
||
if (!channel.isWritable()) { | ||
LOGGER.warn("cannot write response to non-writable channel {} for request {}", channel, | ||
logger.warn("cannot write response to non-writable channel {} for request {}", channel, | ||
StringUtils.requestToString(request)); | ||
requestProcessor.getRequestStats().getChannelWriteStats() | ||
.registerFailedEvent(MathUtils.elapsedNanos(writeNanos), TimeUnit.NANOSECONDS); | ||
statsLogger.registerFailedEvent(MathUtils.elapsedNanos(enqueueNanos), TimeUnit.NANOSECONDS); | ||
if (response instanceof BookieProtocol.ReadResponse) { | ||
((BookieProtocol.ReadResponse) response).release(); | ||
if (response instanceof BookieProtocol.Response) { | ||
((BookieProtocol.Response) response).release(); | ||
} | ||
return; | ||
} else { | ||
|
@@ -119,12 +121,17 @@ protected void sendResponse(int rc, Object response, OpStatsLogger statsLogger) | |
} | ||
|
||
if (channel.isActive()) { | ||
channel.writeAndFlush(response, channel.voidPromise()); | ||
ChannelPromise promise = channel.newPromise().addListener(future -> { | ||
if (!future.isSuccess()) { | ||
logger.debug("Netty channel write exception. ", future.cause()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not log it as an error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When this error occurs, it means that the client has already close. Log error is unnecessary, the other log will show the connection already close. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this need to print the error |
||
} | ||
}); | ||
Comment on lines
+124
to
+128
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will cause unnecessary object creation. If the only purpose is to log exceptions, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for reminder. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It has been fixed here: #3733 |
||
channel.writeAndFlush(response, promise); | ||
} else { | ||
if (response instanceof BookieProtocol.ReadResponse) { | ||
((BookieProtocol.ReadResponse) response).release(); | ||
if (response instanceof BookieProtocol.Response) { | ||
((BookieProtocol.Response) response).release(); | ||
} | ||
LOGGER.debug("Netty channel {} is inactive, " | ||
logger.debug("Netty channel {} is inactive, " | ||
+ "hence bypassing netty channel writeAndFlush during sendResponse", channel); | ||
} | ||
if (BookieProtocol.EOK == rc) { | ||
|
@@ -145,12 +152,12 @@ protected void sendResponseAndWait(int rc, Object response, OpStatsLogger statsL | |
try { | ||
ChannelFuture future = channel.writeAndFlush(response); | ||
if (!channel.eventLoop().inEventLoop()) { | ||
future.await(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the difference in this case ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
future.get(); | ||
} | ||
} catch (InterruptedException e) { | ||
} catch (ExecutionException | InterruptedException e) { | ||
logger.debug("Netty channel write exception. ", e); | ||
zymap marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
} | ||
|
||
if (BookieProtocol.EOK == rc) { | ||
statsLogger.registerSuccessfulEvent(MathUtils.elapsedNanos(enqueueNanos), TimeUnit.NANOSECONDS); | ||
} else { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about keeping
boolean release()
IIUC your problem is about implementing
ReferenceCounted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has conflict with the release() interface in
ReferenceCounted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I think we can keep
boolean release()
. BecauseReferenceCounted
also has the same method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍