Skip to content

Commit

Permalink
ZOOKEEPER-3706: ZooKeeper.close() would leak SendThread when the netw…
Browse files Browse the repository at this point in the history
…ork is broken

- add unit test to verify the bug
- wrap state modification in SendThread.setZkState()
- SendThread.startConnect() could be interrupted by IOException caused by SendThread.setZkState()

Author: Fangxi Yin <[email protected]>
  • Loading branch information
yinfangxi committed Feb 19, 2020
1 parent 6db92d7 commit 8938807
Show file tree
Hide file tree
Showing 2 changed files with 364 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,11 @@ void queueCallback(AsyncCallback cb, int rc, String path, Object ctx) {
eventThread.queueCallback(cb, rc, path, ctx);
}

// for test only
protected void onConnecting(InetSocketAddress addr) {

}

private void conLossPacket(Packet p) {
if (p.replyHeader == null) {
return;
Expand Down Expand Up @@ -876,7 +881,7 @@ void readResponse(ByteBuffer incomingBuffer) throws IOException {
case AUTHPACKET_XID:
LOG.debug("Got auth session id: 0x{}", Long.toHexString(sessionId));
if (replyHdr.getErr() == KeeperException.Code.AUTHFAILED.intValue()) {
state = States.AUTH_FAILED;
changeZkState(States.AUTH_FAILED);
eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None,
Watcher.Event.KeeperState.AuthFailed, null));
eventThread.queueEventOfDeath();
Expand Down Expand Up @@ -957,7 +962,11 @@ void readResponse(ByteBuffer incomingBuffer) throws IOException {

SendThread(ClientCnxnSocket clientCnxnSocket) {
super(makeThreadName("-SendThread()"));
state = States.CONNECTING;
try {
changeZkState(States.CONNECTING);
} catch (IOException e) {
throw new RuntimeException("Connection setup failed when migrate state to CONNECTING");
}
this.clientCnxnSocket = clientCnxnSocket;
setDaemon(true);
}
Expand All @@ -971,10 +980,19 @@ void readResponse(ByteBuffer incomingBuffer) throws IOException {
*
* @return
*/
ZooKeeper.States getZkState() {
synchronized ZooKeeper.States getZkState() {
return state;
}

synchronized void changeZkState(ZooKeeper.States newState) throws IOException {
if (!state.isAlive() && newState == States.CONNECTING) {
throw new IOException(
"Connection has already been closed and reconnection is not allowed");
}
// It's safer to place state modification at the end.
state = newState;
}

ClientCnxnSocket getClientCnxnSocket() {
return clientCnxnSocket;
}
Expand Down Expand Up @@ -1121,7 +1139,7 @@ private void startConnect(InetSocketAddress addr) throws IOException {
LOG.warn("Unexpected exception", e);
}
}
state = States.CONNECTING;
changeZkState(States.CONNECTING);

String hostPort = addr.getHostString() + ":" + addr.getPort();
MDC.put("myid", hostPort);
Expand Down Expand Up @@ -1179,6 +1197,7 @@ public void run() {
} else {
serverAddress = hostProvider.next(1000);
}
onConnecting(serverAddress);
startConnect(serverAddress);
clientCnxnSocket.updateLastSendAndHeard();
}
Expand All @@ -1192,15 +1211,15 @@ public void run() {
zooKeeperSaslClient.initialize(ClientCnxn.this);
} catch (SaslException e) {
LOG.error("SASL authentication with Zookeeper Quorum member failed.", e);
state = States.AUTH_FAILED;
changeZkState(States.AUTH_FAILED);
sendAuthEvent = true;
}
}
KeeperState authState = zooKeeperSaslClient.getKeeperState();
if (authState != null) {
if (authState == KeeperState.AuthFailed) {
// An authentication error occurred during authentication with the Zookeeper Server.
state = States.AUTH_FAILED;
changeZkState(States.AUTH_FAILED);
sendAuthEvent = true;
} else {
if (authState == KeeperState.SaslAuthenticated) {
Expand Down Expand Up @@ -1394,7 +1413,7 @@ void onConnected(
boolean isRO) throws IOException {
negotiatedSessionTimeout = _negotiatedSessionTimeout;
if (negotiatedSessionTimeout <= 0) {
state = States.CLOSED;
changeZkState(States.CLOSED);

eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, Watcher.Event.KeeperState.Expired, null));
eventThread.queueEventOfDeath();
Expand All @@ -1415,7 +1434,7 @@ void onConnected(
hostProvider.onConnected();
sessionId = _sessionId;
sessionPasswd = _sessionPasswd;
state = (isRO) ? States.CONNECTEDREADONLY : States.CONNECTED;
changeZkState((isRO) ? States.CONNECTEDREADONLY : States.CONNECTED);
seenRwServerBefore |= !isRO;
LOG.info(
"Session establishment complete on server {}, session id = 0x{}, negotiated timeout = {}{}",
Expand All @@ -1428,7 +1447,10 @@ void onConnected(
}

void close() {
state = States.CLOSED;
try {
changeZkState(States.CLOSED);
} catch (IOException e) {
}
clientCnxnSocket.onClosing();
}

Expand Down
Loading

0 comments on commit 8938807

Please sign in to comment.