Skip to content

Commit 6233e53

Browse files
authored
[remove confignode] Add Remove ConfigNode SQL (#14813)
* add remove ConfigNode * add IT * removeConfigNodeNum
1 parent a0eab8f commit 6233e53

File tree

20 files changed

+553
-4
lines changed

20 files changed

+553
-4
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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.confignode.it.removeconfignode;
21+
22+
import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
23+
import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
24+
import org.apache.iotdb.confignode.it.removedatanode.SQLModel;
25+
import org.apache.iotdb.consensus.ConsensusFactory;
26+
import org.apache.iotdb.it.env.EnvFactory;
27+
import org.apache.iotdb.itbase.exception.InconsistentDataException;
28+
import org.apache.iotdb.jdbc.IoTDBSQLException;
29+
import org.apache.iotdb.relational.it.query.old.aligned.TableUtils;
30+
31+
import org.awaitility.Awaitility;
32+
import org.awaitility.core.ConditionTimeoutException;
33+
import org.junit.After;
34+
import org.junit.Assert;
35+
import org.junit.Before;
36+
import org.slf4j.Logger;
37+
import org.slf4j.LoggerFactory;
38+
39+
import java.sql.Connection;
40+
import java.sql.ResultSet;
41+
import java.sql.Statement;
42+
import java.util.HashSet;
43+
import java.util.Map;
44+
import java.util.Set;
45+
import java.util.concurrent.TimeUnit;
46+
import java.util.concurrent.atomic.AtomicReference;
47+
48+
import static org.apache.iotdb.confignode.it.regionmigration.IoTDBRegionOperationReliabilityITFramework.getDataRegionMap;
49+
import static org.apache.iotdb.confignode.it.removedatanode.IoTDBRemoveDataNodeITFramework.getConnectionWithSQLType;
50+
import static org.apache.iotdb.util.MagicUtils.makeItCloseQuietly;
51+
52+
public class IoTDBRemoveConfigNodeITFramework {
53+
private static final Logger LOGGER =
54+
LoggerFactory.getLogger(IoTDBRemoveConfigNodeITFramework.class);
55+
private static final String TREE_MODEL_INSERTION =
56+
"INSERT INTO root.sg.d1(timestamp,speed,temperature) values(100, 1, 2)";
57+
58+
private static final String SHOW_CONFIGNODES = "show confignodes";
59+
60+
private static final String defaultSchemaRegionGroupExtensionPolicy = "CUSTOM";
61+
private static final String defaultDataRegionGroupExtensionPolicy = "CUSTOM";
62+
63+
@Before
64+
public void setUp() throws Exception {
65+
EnvFactory.getEnv()
66+
.getConfig()
67+
.getCommonConfig()
68+
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
69+
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
70+
.setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS)
71+
.setSchemaRegionGroupExtensionPolicy(defaultSchemaRegionGroupExtensionPolicy)
72+
.setDataRegionGroupExtensionPolicy(defaultDataRegionGroupExtensionPolicy);
73+
}
74+
75+
@After
76+
public void tearDown() throws InterruptedException {
77+
EnvFactory.getEnv().cleanClusterEnvironment();
78+
}
79+
80+
public void testRemoveConfigNode(
81+
final int dataReplicateFactor,
82+
final int schemaReplicationFactor,
83+
final int configNodeNum,
84+
final int dataNodeNum,
85+
final int dataRegionPerDataNode,
86+
final SQLModel model)
87+
throws Exception {
88+
89+
// Set up the environment
90+
EnvFactory.getEnv()
91+
.getConfig()
92+
.getCommonConfig()
93+
.setSchemaReplicationFactor(schemaReplicationFactor)
94+
.setDataReplicationFactor(dataReplicateFactor)
95+
.setDefaultDataRegionGroupNumPerDatabase(
96+
dataRegionPerDataNode * dataNodeNum / dataReplicateFactor);
97+
EnvFactory.getEnv().initClusterEnvironment(configNodeNum, dataNodeNum);
98+
99+
try (final Connection connection = makeItCloseQuietly(getConnectionWithSQLType(model));
100+
final Statement statement = makeItCloseQuietly(connection.createStatement());
101+
SyncConfigNodeIServiceClient client =
102+
(SyncConfigNodeIServiceClient) EnvFactory.getEnv().getLeaderConfigNodeConnection()) {
103+
104+
if (SQLModel.TABLE_MODEL_SQL.equals(model)) {
105+
// Insert data in table model
106+
TableUtils.insertData();
107+
} else {
108+
// Insert data in tree model
109+
statement.execute(TREE_MODEL_INSERTION);
110+
}
111+
112+
Map<Integer, Set<Integer>> regionMap = getDataRegionMap(statement);
113+
regionMap.forEach(
114+
(key, valueSet) -> {
115+
LOGGER.info("Key: {}, Value: {}", key, valueSet);
116+
if (valueSet.size() != dataReplicateFactor) {
117+
Assert.fail();
118+
}
119+
});
120+
121+
// Get all config nodes
122+
ResultSet result = statement.executeQuery(SHOW_CONFIGNODES);
123+
Set<Integer> allConfigNodeId = new HashSet<>();
124+
while (result.next()) {
125+
allConfigNodeId.add(result.getInt(ColumnHeaderConstant.NODE_ID));
126+
}
127+
128+
AtomicReference<SyncConfigNodeIServiceClient> clientRef = new AtomicReference<>(client);
129+
130+
int removeConfigNodeId = allConfigNodeId.iterator().next();
131+
String removeConfigNodeSQL = generateRemoveString(removeConfigNodeId);
132+
LOGGER.info("Remove ConfigNodes SQL: {}", removeConfigNodeSQL);
133+
try {
134+
statement.execute(removeConfigNodeSQL);
135+
} catch (IoTDBSQLException e) {
136+
LOGGER.error("Remove ConfigNodes SQL execute fail: {}", e.getMessage());
137+
Assert.fail();
138+
}
139+
LOGGER.info("Remove ConfigNodes SQL submit successfully.");
140+
141+
// Wait until success
142+
try {
143+
awaitUntilSuccess(statement, removeConfigNodeId);
144+
} catch (ConditionTimeoutException e) {
145+
LOGGER.error("Remove ConfigNodes timeout in 2 minutes");
146+
Assert.fail();
147+
}
148+
149+
LOGGER.info("Remove ConfigNodes success");
150+
} catch (InconsistentDataException e) {
151+
LOGGER.error("Unexpected error:", e);
152+
}
153+
}
154+
155+
private static void awaitUntilSuccess(Statement statement, int removeConfigNodeId) {
156+
AtomicReference<Set<Integer>> lastTimeConfigNodes = new AtomicReference<>();
157+
AtomicReference<Exception> lastException = new AtomicReference<>();
158+
159+
try {
160+
Awaitility.await()
161+
.atMost(2, TimeUnit.MINUTES)
162+
.pollDelay(2, TimeUnit.SECONDS)
163+
.until(
164+
() -> {
165+
try {
166+
// Get all config nodes
167+
ResultSet result = statement.executeQuery(SHOW_CONFIGNODES);
168+
Set<Integer> allConfigNodeId = new HashSet<>();
169+
while (result.next()) {
170+
allConfigNodeId.add(result.getInt(ColumnHeaderConstant.NODE_ID));
171+
}
172+
lastTimeConfigNodes.set(allConfigNodeId);
173+
return !allConfigNodeId.contains(removeConfigNodeId);
174+
} catch (Exception e) {
175+
// Any exception can be ignored
176+
lastException.set(e);
177+
return false;
178+
}
179+
});
180+
} catch (ConditionTimeoutException e) {
181+
if (lastTimeConfigNodes.get() == null) {
182+
LOGGER.error(
183+
"Maybe show confignodes fail, lastTimeConfigNodes is null, last Exception:",
184+
lastException.get());
185+
throw e;
186+
}
187+
String actualSetStr = lastTimeConfigNodes.get().toString();
188+
lastTimeConfigNodes.get().remove(removeConfigNodeId);
189+
String expectedSetStr = lastTimeConfigNodes.get().toString();
190+
LOGGER.error(
191+
"Remove ConfigNode timeout in 2 minutes, expected set: {}, actual set: {}",
192+
expectedSetStr,
193+
actualSetStr);
194+
if (lastException.get() == null) {
195+
LOGGER.info("No exception during awaiting");
196+
} else {
197+
LOGGER.error("Last exception during awaiting:", lastException.get());
198+
}
199+
throw e;
200+
}
201+
}
202+
203+
public static String generateRemoveString(Integer configNodeId) {
204+
return "remove confignode " + configNodeId;
205+
}
206+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.confignode.it.removeconfignode;
21+
22+
import org.apache.iotdb.confignode.it.removedatanode.SQLModel;
23+
import org.apache.iotdb.it.framework.IoTDBTestRunner;
24+
import org.apache.iotdb.itbase.category.ClusterIT;
25+
26+
import org.junit.Test;
27+
import org.junit.experimental.categories.Category;
28+
import org.junit.runner.RunWith;
29+
30+
@Category({ClusterIT.class})
31+
@RunWith(IoTDBTestRunner.class)
32+
public class IoTDBRemoveConfigNodeNormalIT extends IoTDBRemoveConfigNodeITFramework {
33+
@Test
34+
public void test3C1DUseTreeSQL() throws Exception {
35+
testRemoveConfigNode(1, 1, 3, 1, 2, SQLModel.TREE_MODEL_SQL);
36+
}
37+
38+
@Test
39+
public void test3C1DUseTableSQL() throws Exception {
40+
testRemoveConfigNode(1, 1, 3, 1, 2, SQLModel.TABLE_MODEL_SQL);
41+
}
42+
}

integration-test/src/test/java/org/apache/iotdb/confignode/it/removedatanode/IoTDBRemoveDataNodeITFramework.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ public static String generateRemoveString(Set<Integer> dataNodes) {
407407
return sb.toString();
408408
}
409409

410-
public Connection getConnectionWithSQLType(SQLModel model) throws SQLException {
410+
public static Connection getConnectionWithSQLType(SQLModel model) throws SQLException {
411411
if (SQLModel.TABLE_MODEL_SQL.equals(model)) {
412412
return EnvFactory.getEnv().getTableConnection();
413413
} else {

iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IdentifierParser.g4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ keyWords
6262
| CLUSTERID
6363
| CONCAT
6464
| CONDITION
65+
| CONFIGNODE
6566
| CONFIGNODES
6667
| CONFIGURATION
6768
| CONNECTION

iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ ddlStatement
6363
// Cluster
6464
| showVariables | showCluster | showRegions | showDataNodes | showConfigNodes | showClusterId
6565
| getRegionId | getTimeSlotList | countTimeSlotList | getSeriesSlotList
66-
| migrateRegion | reconstructRegion | extendRegion | removeRegion | removeDataNode
66+
| migrateRegion | reconstructRegion | extendRegion | removeRegion | removeDataNode | removeConfigNode
6767
| verifyConnection
6868
// AINode
6969
| showAINodes | createModel | dropModel | showModels | callInference
@@ -555,6 +555,11 @@ removeDataNode
555555
: REMOVE DATANODE dataNodeId=INTEGER_LITERAL (COMMA dataNodeId=INTEGER_LITERAL)*
556556
;
557557

558+
// ---- Remove ConfigNode
559+
removeConfigNode
560+
: REMOVE CONFIGNODE configNodeId=INTEGER_LITERAL
561+
;
562+
558563
// Pipe Task =========================================================================================
559564
createPipe
560565
: CREATE PIPE (IF NOT EXISTS)? pipeName=identifier

iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ CONFIGNODES
165165
: C O N F I G N O D E S
166166
;
167167

168+
CONFIGNODE
169+
: C O N F I G N O D E
170+
;
171+
168172
CONFIGURATION
169173
: C O N F I G U R A T I O N
170174
;

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,8 @@ public TSStatus notifyRegisterSuccess() throws TException {
730730

731731
@Override
732732
public TSStatus removeConfigNode(TConfigNodeLocation configNodeLocation) throws TException {
733-
throw new TException("DataNode to ConfigNode client doesn't support removeConfigNode.");
733+
return executeRemoteCallWithRetry(
734+
() -> client.removeConfigNode(configNodeLocation), resp -> !updateConfigNodeLeader(resp));
734735
}
735736

736737
@Override

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.PipeStatement;
7979
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ReconstructRegion;
8080
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement;
81+
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveConfigNode;
8182
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveDataNode;
8283
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveRegion;
8384
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetConfiguration;
@@ -420,6 +421,7 @@ private IQueryExecution createQueryExecutionForTableModel(
420421
|| statement instanceof StopRepairData
421422
|| statement instanceof PipeStatement
422423
|| statement instanceof RemoveDataNode
424+
|| statement instanceof RemoveConfigNode
423425
|| statement instanceof SubscriptionStatement
424426
|| statement instanceof ShowCurrentSqlDialect
425427
|| statement instanceof SetSqlDialect

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.CreatePipePluginTask;
3838
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.DropFunctionTask;
3939
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.DropPipePluginTask;
40+
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.RemoveConfigNodeTask;
4041
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.RemoveDataNodeTask;
4142
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowClusterIdTask;
4243
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowClusterTask;
@@ -135,6 +136,7 @@
135136
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QualifiedName;
136137
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ReconstructRegion;
137138
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement;
139+
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveConfigNode;
138140
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveDataNode;
139141
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveRegion;
140142
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameColumn;
@@ -170,6 +172,7 @@
170172
import org.apache.iotdb.db.queryengine.plan.relational.sql.rewrite.StatementRewrite;
171173
import org.apache.iotdb.db.queryengine.plan.relational.type.TypeNotFoundException;
172174
import org.apache.iotdb.db.queryengine.plan.statement.metadata.DatabaseSchemaStatement;
175+
import org.apache.iotdb.db.queryengine.plan.statement.metadata.RemoveConfigNodeStatement;
173176
import org.apache.iotdb.db.queryengine.plan.statement.metadata.RemoveDataNodeStatement;
174177
import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowClusterStatement;
175178
import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowRegionStatement;
@@ -384,6 +387,18 @@ protected IConfigTask visitRemoveDataNode(
384387
return new RemoveDataNodeTask(treeStatement);
385388
}
386389

390+
@Override
391+
protected IConfigTask visitRemoveConfigNode(
392+
final RemoveConfigNode removeConfigNode, final MPPQueryContext context) {
393+
context.setQueryType(QueryType.WRITE);
394+
accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName());
395+
// As the implementation is identical, we'll simply translate to the
396+
// corresponding tree-model variant and execute that.
397+
final RemoveConfigNodeStatement treeStatement =
398+
new RemoveConfigNodeStatement(removeConfigNode.getNodeId());
399+
return new RemoveConfigNodeTask(treeStatement);
400+
}
401+
387402
@Override
388403
protected IConfigTask visitShowDataNodes(
389404
final ShowDataNodes showDataNodesStatement, final MPPQueryContext context) {

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.GetRegionIdTask;
4141
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.GetSeriesSlotListTask;
4242
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.GetTimeSlotListTask;
43+
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.RemoveConfigNodeTask;
4344
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.RemoveDataNodeTask;
4445
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.SetTTLTask;
4546
import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowAINodesTask;
@@ -121,6 +122,7 @@
121122
import org.apache.iotdb.db.queryengine.plan.statement.metadata.GetRegionIdStatement;
122123
import org.apache.iotdb.db.queryengine.plan.statement.metadata.GetSeriesSlotListStatement;
123124
import org.apache.iotdb.db.queryengine.plan.statement.metadata.GetTimeSlotListStatement;
125+
import org.apache.iotdb.db.queryengine.plan.statement.metadata.RemoveConfigNodeStatement;
124126
import org.apache.iotdb.db.queryengine.plan.statement.metadata.RemoveDataNodeStatement;
125127
import org.apache.iotdb.db.queryengine.plan.statement.metadata.SetTTLStatement;
126128
import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowClusterIdStatement;
@@ -666,6 +668,12 @@ public IConfigTask visitRemoveDataNode(
666668
return new RemoveDataNodeTask(removeDataNodeStatement);
667669
}
668670

671+
@Override
672+
public IConfigTask visitRemoveConfigNode(
673+
RemoveConfigNodeStatement removeConfigNodeStatement, MPPQueryContext context) {
674+
return new RemoveConfigNodeTask(removeConfigNodeStatement);
675+
}
676+
669677
@Override
670678
public IConfigTask visitCreateContinuousQuery(
671679
CreateContinuousQueryStatement createContinuousQueryStatement, MPPQueryContext context) {

0 commit comments

Comments
 (0)