Skip to content

Commit bf8329b

Browse files
Pipe: Fix connection leak caused by clients not closed after task dropped (2 situations) (#15910)
1 parent e6bc39f commit bf8329b

9 files changed

Lines changed: 76 additions & 23 deletions

File tree

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/connector/protocol/IoTDBConfigRegionConnector.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ private void doTransferWrapper(
149149

150150
private void doTransfer(final PipeConfigRegionWritePlanEvent pipeConfigRegionWritePlanEvent)
151151
throws PipeException {
152-
final Pair<IoTDBSyncClient, Boolean> clientAndStatus = clientManager.getClient();
152+
final Pair<IoTDBSyncClient, Boolean> clientAndStatus = getClientManager().getClient();
153153

154154
final TPipeTransferResp resp;
155155
try {
@@ -175,7 +175,7 @@ private void doTransfer(final PipeConfigRegionWritePlanEvent pipeConfigRegionWri
175175
final TSStatus status = resp.getStatus();
176176
// Send handshake req and then re-transfer the event
177177
if (status.getCode() == TSStatusCode.PIPE_CONFIG_RECEIVER_HANDSHAKE_NEEDED.getStatusCode()) {
178-
clientManager.sendHandshakeReq(clientAndStatus);
178+
getClientManager().sendHandshakeReq(clientAndStatus);
179179
}
180180
// Only handle the failed statuses to avoid string format performance overhead
181181
if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()
@@ -214,7 +214,7 @@ private void doTransfer(final PipeConfigRegionSnapshotEvent snapshotEvent)
214214
final long creationTime = snapshotEvent.getCreationTime();
215215
final File snapshotFile = snapshotEvent.getSnapshotFile();
216216
final File templateFile = snapshotEvent.getTemplateFile();
217-
final Pair<IoTDBSyncClient, Boolean> clientAndStatus = clientManager.getClient();
217+
final Pair<IoTDBSyncClient, Boolean> clientAndStatus = getClientManager().getClient();
218218

219219
// 1. Transfer snapshotFile, and template File if exists
220220
transferFilePieces(
@@ -265,7 +265,7 @@ private void doTransfer(final PipeConfigRegionSnapshotEvent snapshotEvent)
265265
final TSStatus status = resp.getStatus();
266266
// Send handshake req and then re-transfer the event
267267
if (status.getCode() == TSStatusCode.PIPE_CONFIG_RECEIVER_HANDSHAKE_NEEDED.getStatusCode()) {
268-
clientManager.sendHandshakeReq(clientAndStatus);
268+
getClientManager().sendHandshakeReq(clientAndStatus);
269269
}
270270
// Only handle the failed statuses to avoid string format performance overhead
271271
if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/connector/PipeConnectorSubtask.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ protected boolean executeOnce() {
164164
}
165165

166166
private void transferHeartbeatEvent(final PipeHeartbeatEvent event) {
167+
// DO NOT call heartbeat or transfer after closed, or will cause connection leak
168+
if (isClosed.get()) {
169+
return;
170+
}
171+
167172
try {
168173
outputPipeConnector.heartbeat();
169174
outputPipeConnector.transfer(event);

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/client/IoTDBDataNodeAsyncClientManager.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,18 @@ public void onError(final Exception e) {
299299
client.resetMethodStateIfStopped();
300300
throw e;
301301
} finally {
302+
if (isClosed) {
303+
try {
304+
client.close();
305+
client.invalidateAll();
306+
} catch (final Exception e) {
307+
LOGGER.warn(
308+
"Failed to close client {}:{} after handshake failure when the manager is closed.",
309+
targetNodeUrl.getIp(),
310+
targetNodeUrl.getPort(),
311+
e);
312+
}
313+
}
302314
client.setShouldReturnSelf(true);
303315
client.returnSelf();
304316
}
@@ -348,8 +360,14 @@ public void close() {
348360
if (clientManager != null) {
349361
try {
350362
clientManager.close();
363+
LOGGER.info(
364+
"Closed AsyncPipeDataTransferServiceClientManager for receiver attributes: {}",
365+
receiverAttributes);
351366
} catch (final Exception e) {
352-
LOGGER.warn("Failed to close client manager.", e);
367+
LOGGER.warn(
368+
"Failed to close AsyncPipeDataTransferServiceClientManager for receiver attributes: {}",
369+
receiverAttributes,
370+
e);
353371
}
354372
}
355373
return null;

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/protocol/thrift/async/IoTDBDataRegionAsyncConnector.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ public synchronized void handshake() throws Exception {
198198

199199
@Override
200200
public void heartbeat() throws Exception {
201-
syncConnector.heartbeat();
201+
if (!isClosed()) {
202+
syncConnector.heartbeat();
203+
}
202204
}
203205

204206
@Override

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/protocol/thrift/async/handler/PipeTransferTrackableHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ protected boolean tryTransfer(
8282
if (connector.isClosed()) {
8383
clearEventsReferenceCount();
8484
connector.eliminateHandler(this);
85+
client.setShouldReturnSelf(true);
86+
client.returnSelf();
8587
return false;
8688
}
8789
doTransfer(client, req);

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/connector/protocol/thrift/async/handler/PipeTransferTsFileHandler.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,11 +411,25 @@ protected void onErrorInternal(final Exception exception) {
411411
}
412412

413413
private void returnClientIfNecessary() {
414-
if (client != null) {
415-
client.setShouldReturnSelf(true);
416-
client.returnSelf();
417-
client = null;
414+
if (client == null) {
415+
return;
416+
}
417+
418+
if (connector.isClosed()) {
419+
try {
420+
client.close();
421+
client.invalidateAll();
422+
} catch (final Exception e) {
423+
LOGGER.warn(
424+
"Failed to close or invalidate client when connector is closed. Client: {}, Exception: {}",
425+
client,
426+
e.getMessage(),
427+
e);
428+
}
418429
}
430+
client.setShouldReturnSelf(true);
431+
client.returnSelf();
432+
client = null;
419433
}
420434

421435
@Override

iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/ClientManager.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,19 @@ public V borrowClient(K node) throws ClientManagerException {
6464
* return of a client is automatic whenever a particular client is used.
6565
*/
6666
public void returnClient(K node, V client) {
67-
Optional.ofNullable(node)
68-
.ifPresent(
69-
x -> {
70-
try {
71-
pool.returnObject(node, client);
72-
} catch (Exception e) {
73-
LOGGER.warn("Return client {} for node {} to pool failed.", client, node, e);
74-
}
75-
});
67+
if (node != null) {
68+
try {
69+
pool.returnObject(node, client);
70+
} catch (Exception e) {
71+
LOGGER.warn("Return client {} for node {} to pool failed.", client, node, e);
72+
}
73+
} else if (client instanceof ThriftClient) {
74+
((ThriftClient) client).invalidateAll();
75+
LOGGER.warn(
76+
"Return client {} to pool failed because the node is null. "
77+
+ "This may cause resource leak, please check your code.",
78+
client);
79+
}
7680
}
7781

7882
@Override

iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/async/AsyncPipeDataTransferServiceClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public void setTimeoutDynamically(final int timeout) {
127127
}
128128
}
129129

130-
private void close() {
130+
public void close() {
131131
___transport.close();
132132
___currentMethod = null;
133133
}

iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/connector/protocol/IoTDBSslSyncConnector.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,14 @@ public abstract class IoTDBSslSyncConnector extends IoTDBConnector {
6666

6767
private static final Logger LOGGER = LoggerFactory.getLogger(IoTDBSslSyncConnector.class);
6868

69-
protected IoTDBSyncClientManager clientManager;
69+
private volatile IoTDBSyncClientManager clientManager;
70+
71+
protected IoTDBSyncClientManager getClientManager() {
72+
if (clientManager == null) {
73+
throw new IllegalStateException("IoTDB sync client manager has been closed");
74+
}
75+
return clientManager;
76+
}
7077

7178
@Override
7279
public void validate(final PipeParameterValidator validator) throws Exception {
@@ -153,7 +160,7 @@ protected abstract IoTDBSyncClientManager constructClient(
153160

154161
@Override
155162
public void handshake() throws Exception {
156-
clientManager.checkClientStatusAndTryReconstructIfNecessary();
163+
getClientManager().checkClientStatusAndTryReconstructIfNecessary();
157164
}
158165

159166
@Override
@@ -229,7 +236,7 @@ protected void transferFilePieces(
229236
// Send handshake req and then re-transfer the event
230237
if (status.getCode()
231238
== TSStatusCode.PIPE_CONFIG_RECEIVER_HANDSHAKE_NEEDED.getStatusCode()) {
232-
clientManager.sendHandshakeReq(clientAndStatus);
239+
getClientManager().sendHandshakeReq(clientAndStatus);
233240
}
234241
// Only handle the failed statuses to avoid string format performance overhead
235242
if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()
@@ -255,6 +262,7 @@ protected abstract PipeTransferFilePieceReq getTransferMultiFilePieceReq(
255262
public void close() {
256263
if (clientManager != null) {
257264
clientManager.close();
265+
clientManager = null;
258266
}
259267

260268
super.close();

0 commit comments

Comments
 (0)