Skip to content

Commit d11770d

Browse files
authored
feat: add integration test support for StreamNode (#17952)
1 parent 08c046e commit d11770d

4 files changed

Lines changed: 160 additions & 64 deletions

File tree

integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AIEnv.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,44 @@
1919

2020
package org.apache.iotdb.it.env.cluster.env;
2121

22+
import org.apache.iotdb.it.env.cluster.node.AINodeStarter;
23+
import org.apache.iotdb.it.env.cluster.node.ConfigNodeWrapper;
24+
import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
25+
26+
import java.util.List;
27+
2228
public class AIEnv extends AbstractEnv {
29+
2330
@Override
2431
public void initClusterEnvironment() {
2532
initClusterEnvironment(1, 1);
2633
}
2734

2835
@Override
2936
public void initClusterEnvironment(int configNodesNum, int dataNodesNum) {
30-
super.initEnvironment(configNodesNum, dataNodesNum, 600, true);
37+
super.initEnvironment(configNodesNum, dataNodesNum, 600);
3138
}
3239

3340
@Override
3441
public void initClusterEnvironment(
3542
int configNodesNum, int dataNodesNum, int testWorkingRetryCount) {
36-
super.initEnvironment(configNodesNum, dataNodesNum, testWorkingRetryCount, true);
43+
super.initEnvironment(configNodesNum, dataNodesNum, testWorkingRetryCount);
44+
}
45+
46+
@Override
47+
protected void initExtraNodes(
48+
final List<ConfigNodeWrapper> configNodeWrappers,
49+
final List<DataNodeWrapper> dataNodeWrappers,
50+
final String testClassName) {
51+
AINodeStarter.startAINode(
52+
configNodeWrappers.get(0).getIpAndPortString(),
53+
dataNodeWrappers.get(0).getPort(),
54+
testClassName,
55+
testMethodName,
56+
index,
57+
startTime,
58+
extraNodeKillPoints,
59+
this::registerExtraNode,
60+
this::dumpTestJVMSnapshot);
3761
}
3862
}

integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import org.apache.iotdb.it.env.cluster.config.MppConfigNodeConfig;
4848
import org.apache.iotdb.it.env.cluster.config.MppDataNodeConfig;
4949
import org.apache.iotdb.it.env.cluster.config.MppJVMConfig;
50-
import org.apache.iotdb.it.env.cluster.node.AINodeWrapper;
5150
import org.apache.iotdb.it.env.cluster.node.AbstractNodeWrapper;
5251
import org.apache.iotdb.it.env.cluster.node.ConfigNodeWrapper;
5352
import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
@@ -101,14 +100,15 @@ public abstract class AbstractEnv implements BaseEnv {
101100
private final Random rand = new Random();
102101
protected List<ConfigNodeWrapper> configNodeWrapperList = Collections.emptyList();
103102
protected List<DataNodeWrapper> dataNodeWrapperList = Collections.emptyList();
104-
protected List<AINodeWrapper> aiNodeWrapperList = Collections.emptyList();
103+
protected List<AbstractNodeWrapper> extraNodeWrappers = Collections.emptyList();
105104
protected String testMethodName = null;
106105
protected int index = 0;
107106
protected long startTime;
108107
protected int retryCount = 30;
109108
private IClientManager<TEndPoint, SyncConfigNodeIServiceClient> clientManager;
110109
private List<String> configNodeKillPoints = new ArrayList<>();
111110
private List<String> dataNodeKillPoints = new ArrayList<>();
111+
protected List<String> extraNodeKillPoints = new ArrayList<>();
112112

113113
/**
114114
* This config object stores the properties set by developers during the test. It will be cleared
@@ -169,17 +169,10 @@ protected void initEnvironment(final int configNodesNum, final int dataNodesNum)
169169

170170
protected void initEnvironment(
171171
final int configNodesNum, final int dataNodesNum, final int testWorkingRetryCount) {
172-
initEnvironment(configNodesNum, dataNodesNum, testWorkingRetryCount, false);
173-
}
174-
175-
protected void initEnvironment(
176-
final int configNodesNum,
177-
final int dataNodesNum,
178-
final int retryCount,
179-
final boolean addAINode) {
180-
this.retryCount = retryCount;
172+
this.retryCount = testWorkingRetryCount;
181173
this.configNodeWrapperList = new ArrayList<>();
182174
this.dataNodeWrapperList = new ArrayList<>();
175+
this.extraNodeWrappers = new ArrayList<>();
183176

184177
clientManager =
185178
new IClientManager.Factory<TEndPoint, SyncConfigNodeIServiceClient>()
@@ -258,14 +251,34 @@ protected void initEnvironment(
258251
throw new AssertionError();
259252
}
260253

261-
if (addAINode) {
262-
this.aiNodeWrapperList = new ArrayList<>();
263-
startAINode(seedConfigNode, this.dataNodeWrapperList.get(0).getPort(), testClassName);
264-
}
254+
initExtraNodes(configNodeWrapperList, dataNodeWrapperList, testClassName);
265255

266256
checkClusterStatusWithoutUnknown();
267257
}
268258

259+
/**
260+
* Hook method for subclasses to initialize and start extra node types beyond the core ConfigNode
261+
* and DataNode (e.g., AINode, StreamNode, ProxyNode).
262+
*
263+
* <p>Subclasses should create node wrappers, add them to {@link #extraNodeWrappers}, configure
264+
* kill points via {@link #extraNodeKillPoints}, and start the nodes. Subclasses have direct
265+
* access to protected fields: {@code testMethodName}, {@code index}, {@code startTime}.
266+
*
267+
* @param configNodeWrappers list of all ConfigNode wrappers in the cluster (unmodifiable)
268+
* @param dataNodeWrappers list of all DataNode wrappers in the cluster (unmodifiable)
269+
* @param testClassName the test class name for logging and identification purposes
270+
*/
271+
protected void initExtraNodes(
272+
final List<ConfigNodeWrapper> configNodeWrappers,
273+
final List<DataNodeWrapper> dataNodeWrappers,
274+
final String testClassName) {
275+
// Default: no extra nodes. Subclasses override to add nodes.
276+
}
277+
278+
protected void registerExtraNode(final AbstractNodeWrapper nodeWrapper) {
279+
extraNodeWrappers.add(nodeWrapper);
280+
}
281+
269282
private ConfigNodeWrapper newConfigNode() {
270283
final ConfigNodeWrapper configNodeWrapper =
271284
new ConfigNodeWrapper(
@@ -309,39 +322,6 @@ private DataNodeWrapper newDataNode() {
309322
return dataNodeWrapper;
310323
}
311324

312-
private void startAINode(
313-
final String seedConfigNode, final int clusterIngressPort, final String testClassName) {
314-
final String aiNodeEndPoint;
315-
final AINodeWrapper aiNodeWrapper =
316-
new AINodeWrapper(
317-
seedConfigNode,
318-
clusterIngressPort,
319-
testClassName,
320-
testMethodName,
321-
index,
322-
EnvUtils.searchAvailablePorts(),
323-
startTime);
324-
aiNodeWrapperList.add(aiNodeWrapper);
325-
aiNodeEndPoint = aiNodeWrapper.getIpAndPortString();
326-
aiNodeWrapper.createNodeDir();
327-
aiNodeWrapper.createLogDir();
328-
final RequestDelegate<Void> aiNodesDelegate =
329-
new ParallelRequestDelegate<>(
330-
Collections.singletonList(aiNodeEndPoint), NODE_START_TIMEOUT, this);
331-
332-
aiNodesDelegate.addRequest(
333-
() -> {
334-
aiNodeWrapper.start();
335-
return null;
336-
});
337-
338-
try {
339-
aiNodesDelegate.requestAll();
340-
} catch (final SQLException e) {
341-
logger.error("Start aiNodes failed", e);
342-
}
343-
}
344-
345325
public String getTestClassName() {
346326
final StackTraceElement[] stack = Thread.currentThread().getStackTrace();
347327
for (final StackTraceElement stackTraceElement : stack) {
@@ -433,7 +413,7 @@ public void checkClusterStatus(
433413
if (showClusterResp.getNodeStatus().size()
434414
!= configNodeWrapperList.size()
435415
+ dataNodeWrapperList.size()
436-
+ aiNodeWrapperList.size()) {
416+
+ extraNodeWrappers.size()) {
437417
passed = false;
438418
nodeSizePassed = false;
439419
actualNodeSize = showClusterResp.getNodeStatusSize();
@@ -465,7 +445,7 @@ public void checkClusterStatus(
465445
processStatusMap.put(nodeWrapper, 0);
466446
}
467447
}
468-
for (AINodeWrapper nodeWrapper : aiNodeWrapperList) {
448+
for (AbstractNodeWrapper nodeWrapper : extraNodeWrappers) {
469449
boolean alive = nodeWrapper.getInstance().isAlive();
470450
if (!alive) {
471451
processStatusMap.put(nodeWrapper, nodeWrapper.getInstance().waitFor());
@@ -568,14 +548,14 @@ private void handleProcessStatus(Map<AbstractNodeWrapper, Integer> processStatus
568548
configNodeWrapper.start();
569549
}
570550
}
571-
for (AINodeWrapper aiNodeWrapper : aiNodeWrapperList) {
572-
if (portOccupationMap.containsValue(aiNodeWrapper.getPid())) {
551+
for (AbstractNodeWrapper extraNodeWrapper : extraNodeWrappers) {
552+
if (portOccupationMap.containsValue(extraNodeWrapper.getPid())) {
573553
logger.info(
574-
"A port is occupied by another AINode {}-{}, restart it",
575-
aiNodeWrapper.getIpAndPortString(),
576-
aiNodeWrapper.getPid());
577-
aiNodeWrapper.stop();
578-
aiNodeWrapper.start();
554+
"A port is occupied by another node {}-{}, restart it",
555+
extraNodeWrapper.getIpAndPortString(),
556+
extraNodeWrapper.getPid());
557+
extraNodeWrapper.stop();
558+
extraNodeWrapper.start();
579559
}
580560
}
581561
} catch (IOException e) {
@@ -592,8 +572,8 @@ private void handleProcessStatus(Map<AbstractNodeWrapper, Integer> processStatus
592572
public void cleanClusterEnvironment() {
593573
final List<AbstractNodeWrapper> allNodeWrappers =
594574
Stream.concat(
595-
dataNodeWrapperList.stream(),
596-
Stream.concat(configNodeWrapperList.stream(), aiNodeWrapperList.stream()))
575+
Stream.concat(configNodeWrapperList.stream(), dataNodeWrapperList.stream()),
576+
extraNodeWrappers.stream())
597577
.collect(Collectors.toList());
598578
allNodeWrappers.stream()
599579
.findAny()
@@ -1045,6 +1025,7 @@ public void dumpTestJVMSnapshot() {
10451025
public List<AbstractNodeWrapper> getNodeWrapperList() {
10461026
final List<AbstractNodeWrapper> result = new ArrayList<>(configNodeWrapperList);
10471027
result.addAll(dataNodeWrapperList);
1028+
result.addAll(extraNodeWrappers);
10481029
return result;
10491030
}
10501031

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.it.env.cluster.node;
21+
22+
import org.apache.iotdb.it.env.cluster.EnvUtils;
23+
import org.apache.iotdb.it.framework.IoTDBTestLogger;
24+
import org.apache.iotdb.itbase.runtime.ParallelRequestDelegate;
25+
import org.apache.iotdb.itbase.runtime.RequestDelegate;
26+
27+
import org.slf4j.Logger;
28+
29+
import java.sql.SQLException;
30+
import java.util.Collections;
31+
import java.util.List;
32+
import java.util.function.Consumer;
33+
34+
import static org.apache.iotdb.it.env.cluster.ClusterConstant.NODE_START_TIMEOUT;
35+
36+
public class AINodeStarter {
37+
private static final Logger logger = IoTDBTestLogger.logger;
38+
39+
private AINodeStarter() {}
40+
41+
public static AINodeWrapper startAINode(
42+
final String seedConfigNode,
43+
final int clusterIngressPort,
44+
final String testClassName,
45+
final String testMethodName,
46+
final int clusterIndex,
47+
final long startTime,
48+
final List<String> killPoints,
49+
final Consumer<AINodeWrapper> nodeRegister,
50+
final Runnable dumpTestJVMSnapshot) {
51+
final AINodeWrapper aiNodeWrapper =
52+
new AINodeWrapper(
53+
seedConfigNode,
54+
clusterIngressPort,
55+
testClassName,
56+
testMethodName,
57+
clusterIndex,
58+
EnvUtils.searchAvailablePorts(),
59+
startTime);
60+
nodeRegister.accept(aiNodeWrapper);
61+
aiNodeWrapper.setKillPoints(killPoints);
62+
aiNodeWrapper.createNodeDir();
63+
aiNodeWrapper.createLogDir();
64+
65+
final RequestDelegate<Void> aiNodesDelegate =
66+
new ParallelRequestDelegate<>(
67+
Collections.singletonList(aiNodeWrapper.getIpAndPortString()),
68+
NODE_START_TIMEOUT,
69+
dumpTestJVMSnapshot);
70+
aiNodesDelegate.addRequest(
71+
() -> {
72+
aiNodeWrapper.start();
73+
return null;
74+
});
75+
76+
try {
77+
aiNodesDelegate.requestAll();
78+
} catch (final SQLException e) {
79+
logger.error("Start AINode {} failed", aiNodeWrapper.getId(), e);
80+
throw new AssertionError();
81+
}
82+
return aiNodeWrapper;
83+
}
84+
}

integration-test/src/main/java/org/apache/iotdb/itbase/runtime/ParallelRequestDelegate.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,20 @@
3737
*/
3838
public class ParallelRequestDelegate<T> extends RequestDelegate<T> {
3939
private final int taskTimeoutSeconds;
40-
private final AbstractEnv env;
40+
private final Runnable dumpTestJVMSnapshot;
4141

4242
public ParallelRequestDelegate(
4343
final List<String> endpoints, final int taskTimeoutSeconds, final AbstractEnv env) {
44+
this(endpoints, taskTimeoutSeconds, env != null ? env::dumpTestJVMSnapshot : () -> {});
45+
}
46+
47+
public ParallelRequestDelegate(
48+
final List<String> endpoints,
49+
final int taskTimeoutSeconds,
50+
final Runnable dumpTestJVMSnapshot) {
4451
super(endpoints);
4552
this.taskTimeoutSeconds = taskTimeoutSeconds;
46-
this.env = env;
53+
this.dumpTestJVMSnapshot = dumpTestJVMSnapshot;
4754
}
4855

4956
public List<T> requestAll() throws SQLException {
@@ -60,7 +67,7 @@ public List<T> requestAll() throws SQLException {
6067
} catch (ExecutionException e) {
6168
exceptions[i] = e;
6269
} catch (InterruptedException | TimeoutException e) {
63-
env.dumpTestJVMSnapshot();
70+
dumpTestJVMSnapshot.run();
6471
for (int j = i; j < getEndpoints().size(); j++) {
6572
resultFutures.get(j).cancel(true);
6673
}

0 commit comments

Comments
 (0)